-
Notifications
You must be signed in to change notification settings - Fork 142
Docking Engine
WpfHexEditor.Shell is a 100% in-house Visual Studio–style docking engine — zero third-party dependency, fully themed.
v0.6.0 rename: previously
WpfHexEditor.Docking.Wpf. All namespaces are nowWpfHexEditor.Shell.*. The assembly isWpfHexEditor.Shell.dll.
flowchart TD
Host["DockControl\n(XAML: dock:DockControl)"]
DockTabControl["DockTabControl\nTab strip per zone"]
AutoHideBar["AutoHideBar\nLeft / Right / Bottom"]
AutoHideFlyout["AutoHideFlyout\nAnimated slide-in panel"]
Preview["AutoHideBarHoverPreview\nSnapshot on hover"]
Serializer["DockLayoutSerializer\nSave / Restore layout"]
Dto["DockItemDto\nSerializable state"]
Themes["ResourceDictionary\n8 built-in themes"]
Host --> DockTabControl
Host --> AutoHideBar
AutoHideBar --> AutoHideFlyout
AutoHideBar --> Preview
Host --> Serializer
Serializer --> Dto
Host --> Themes
| Class | Responsibility |
|---|---|
IDockHost |
Contract implemented by MainWindow — entry point for all docking operations |
DockTabControl |
Tab strip that hosts dockable panels; vertical drag triggers float |
DockTabHeader |
Tab header with title, icon, close button and color indicator |
AutoHideBar |
Collapsed panel strip on window edges (left/right/bottom) |
AutoHideFlyout |
Animated slide-in panel shown when clicking an AutoHide tab |
AutoHideBarHoverPreview |
Snapshot thumbnail shown on AutoHide tab hover |
DockItemSourceSynchronizer |
Keeps the ItemsSource of a DockTabControl in sync with the ViewModel list |
DockTabEventWirer |
Connects drag/drop, keyboard, and close events on each tab item |
| Feature | Description |
|---|---|
| Float | Drag any tab out of the strip → independent Window
|
| Dock | Drag a floating window over a drop zone → re-docks |
| Auto-hide | Pin/unpin collapses panel to edge strip |
| Tab groups | Multiple groups per zone, each a DockTabControl
|
| Colored tabs | Per-tab color picker via TabSettingsDialog
|
| Live themes | Switch among 8 themes at runtime — no restart |
| Layout persistence | Serialize entire layout to JSON (DockItemDto) and restore on next launch |
| Hover preview | Hovering an auto-hide tab shows a snapshot of the panel content |
xmlns:dock="clr-namespace:WpfHexEditor.Shell;assembly=WpfHexEditor.Shell"<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dock="clr-namespace:WpfHexEditor.Shell;assembly=WpfHexEditor.Shell">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <!-- Menu / toolbar -->
<RowDefinition Height="*" /> <!-- Docking area -->
<RowDefinition Height="Auto" /> <!-- Status bar -->
</Grid.RowDefinitions>
<!-- Docking engine — fills the entire editor area -->
<dock:DockControl Grid.Row="1"
x:Name="DockHost"
Margin="2" />
</Grid>
</Window>public partial class MainWindow : Window
{
private DockLayoutRoot _layout = null!;
private DockEngine _engine = null!;
private void InitializeDocking()
{
// Provide the content factory: maps ContentId → WPF element
DockHost.ContentFactory = CreateContentForItem;
DockHost.TabCloseRequested += OnTabCloseRequested;
DockHost.ActiveItemChanged += OnActiveDocumentChanged;
// Assign a layout — DockControl creates its DockEngine internally
DockHost.Layout = _layout;
// IMPORTANT: use the engine that DockControl created, not a separate one.
// Using a different engine leaves DockControl's internal content cache stale on close.
_engine = DockHost.Engine!;
DockHost.RebuildVisualTree();
}
// Content factory: called on layout restore and new tab creation
private object? CreateContentForItem(DockItem item) => item.ContentId switch
{
"panel-output" => new OutputPanel(),
"panel-terminal" => new TerminalPanel(),
_ => null
};
private void OnTabCloseRequested(DockItem item)
{
_engine.Close(item);
DockHost.RebuildVisualTree();
}
private void OnActiveDocumentChanged(DockItem? item)
{
// Update status bar, menu state, etc.
}
}The layout is represented as a DockLayoutRoot tree and serialized to JSON via DockLayoutSerializer.
// --- Save ---
string json = DockLayoutSerializer.Serialize(_layout);
File.WriteAllText(layoutPath, json);
// --- Restore ---
DockLayoutRoot layout = DockLayoutSerializer.Deserialize(File.ReadAllText(layoutPath));
DockHost.Layout = layout;
_engine = DockHost.Engine!;
DockHost.RebuildVisualTree();var timer = new DispatcherTimer(DispatcherPriority.Background)
{
Interval = TimeSpan.FromSeconds(30)
};
timer.Tick += (_, _) =>
File.WriteAllText(layoutPath, DockLayoutSerializer.Serialize(_layout));
timer.Start();For simpler scenarios, DockWorkspace wraps DockControl and exposes SaveLayout() / LoadLayout() directly:
var workspace = new DockWorkspace(DockHost);
// Save
string json = workspace.SaveLayout();
// Restore
workspace.LoadLayout(json);The ContentFactory callback maps each ContentId back to a WPF element on restore.
See ContentId Routing for the full ID mapping table.
public partial class MyPanel : UserControl
{
public MyPanel() => InitializeComponent();
}In MainWindow.Editors.cs, add a constant and a factory method:
private const string MyPanelContentId = "panel-my-panel";
private FrameworkElement CreateMyPanelContent() => new MyPanel
{
DataContext = new MyPanelViewModel()
};case MyPanelContentId:
return CreateMyPanelContent();// Add a new item to the layout, then rebuild
var item = new DockItem
{
ContentId = MyPanelContentId,
Title = "My Panel"
};
_engine.AddToGroup(item, targetGroup: null); // null = default document group
DockHost.RebuildVisualTree();
// Via plugin UIRegistry (from a plugin)
context.UIRegistry.ShowDockablePanel("my-panel");// Force the content factory to recreate the element for a given ContentId
DockHost.InvalidateContent(MyPanelContentId);
DockHost.RebuildVisualTree();DockHost.SyncLayoutSizes();The docking engine reads Dock* brush keys from the active ResourceDictionary:
| Key | Description |
|---|---|
DockBackgroundBrush |
Main docking area background |
DockTabBackgroundBrush |
Tab strip background |
DockTabSelectedBrush |
Active tab highlight |
DockTabHoverBrush |
Tab hover state |
DockTabBorderBrush |
Tab separator lines |
DockTabForegroundBrush |
Tab title text |
DockFloatBorderBrush |
Floating window border |
DockAutoHideBarBrush |
Auto-hide edge strip |
DockDropIndicatorBrush |
Drop zone highlight during drag |
All 8 built-in themes define every Dock* key.
See Architecture — Themes for the full theme list.
- Architecture — full IDE architecture
- ContentId Routing — panel/editor ID mapping
-
Plugin System —
IUIRegistry.ShowDockablePanel()
✨ Wpf HexEditor user control, by Derek Tremblay (derektremblay666@gmail.com) coded for your fun! 😊🤟
- API Reference
- Performance
- Services
- Core Components
- ByteProvider
- Rendering Engine
- Search Architecture
- whfmt.FileFormatCatalog v1.0.0 NuGet (cross-platform net8.0)
- 690+ .whfmt definitions (schema v2.3)
- Structure Editor — block DataGrid, drag-drop, validation, SmartComplete
- WhfmtBrowser/Catalog panels — browse all embedded formats
- AI Assistant (5 providers, 25 MCP tools)
- Tab Groups, Document Structure, Lazy Plugin Loading
- Window Menu + Win32 Fullscreen (
F11) - Git Integration UI (changes, history, blame)
- Shared Undo Engine (HexEditor ↔ CodeEditor)
- Bracket pair colorization, sticky scroll, peek definition
- Format detection hardening (thread-safe, crash guard)