Recently, I started a project for porting a Windows Forms application to WPF. I was quite a novice in WPF, so at first I was considering using some type of Windows Forms interoperability. In particular, the WinForm application has cool docking functionalities that I wanted to port to a newer version. As I was going deeper into WPF technology, I discovered a new world that radically changed my initial ideas. In this article, I wish to share a library that implements the Windows docking feature in pure WPF without any Win32-interop.
Using the code
There are three fundamental classes:
DockManager is a class responsible for managing the main window layout.
Pane represents the window area that can be docked to a border. Each
Pane contains one or more
ManagedContent elements, which precisely refer to a window content element of the client code. Using this library is straightforward.
DockManager is a user control that can be easily embedded into a window. For example, the following XAML code creates a
DockManager in a
Title="DockingDemo" Height="500" Width="500"
Loaded="OnLoaded" Background="LightGray" >
<MenuItem Header="New" Click="NewDocument"/>
<MenuItem Header="Exit" Click="ExitApplication"/>
<MenuItem Header="Explorer" Click="ShowExplorerWindow"/>
<MenuItem Header="Output" Click="ShowOutputWindow"/>
<MenuItem Header="Property" Click="ShowPropertyWindow"/>
<MenuItem Header="ToDoList" Click="ShowListWindow"/>
<custom:DockManager Name ="DockManager"/>
Notice that to use
DockManager you have to refer an external CLR namespace,
DockingLibary here. You can now add your windows to
DockManager with code like this:
public partial class MainWindow : System.Windows.Window
private TreeViewWindow explorerWindow = new TreeViewWindow();
private OutputWindow outputWindow = new OutputWindow();
private PropertyWindow propertyWindow = new PropertyWindow();
private ListViewWindow listWindow = new ListViewWindow();
private void OnLoaded(object sender, EventArgs e)
dockManager.ParentWindow = this;
propertyWindow.DockManager = dockManager;
explorerWindow.DockManager = dockManager;
listWindow.DockManager = dockManager;
Ib order to be dockable, a window must derive from
DockableContent. Deriving from
DockableContent indicates that the window content can be hosted in
DockablePane. Windows are initially docked either where you set the
Show member call or, by default, to the left border. The last thing to see is how to add a document window. A document window is a particular window that can't be docked to the main window border. It lives only in
DocumentsPane which, as
DockablePane, is a particular kind of
Pane. The following code adds a document window with a unique title to
private bool ContainsDocument(string docTitle)
foreach (DockingLibrary.DocumentContent doc in DockManager.Documents)
if (string.CompareOrdinal(doc.Title, docTitle) == 0)
private void NewDocument(object sender, EventArgs e)
string title = "Document";
int i = 1;
while (ContainsDocument(title + i.ToString()))
DocumentWindow doc = new DocumentWindow();
doc.Title = title+i.ToString();
Points of interest
Exactly how is docking realized? I implement a simple algorithm here to manage relations between windows that are docked.
DockingGrid contains code to organize
Pane in a logical tree.
DockingGrid.ArrangeLayout in order to organize the window layout. You also use
DockingGrid.ChangeDock when you need to dock a
Pane to a main window border. The following image shows a logical tree of panes. Don't get confused with the WPF logical tree.
Each node is a group of either two
Panes or a
Pane and a child. To arrange the layout,
DockingGrid creates a WPF grid for each group with two columns or two rows, depending on split orientation. I hope the image is self-explanatory. Anyway, feel free to ask for more details if you are interested.
From version 0.1, the library supports floating windows, as you can see in VS. A floating window is an instance of the
FloatingWindow class. It' s a very particular window because it needs to "float" between two windows. One is the parent window,
MainWindow in this case, which is usually the main application window. The other is a transparent window owned by
FloatingWindow itself, which is shown during dragging operations.
As you can see in the previous image,
FloatingWindow supports useful switching options through a context menu on the title bar. In WinForms, controlling a non-client window area means overriding WndProc and managing relevant messages like
In WPF, we have no access to messages posted to a window. This is because everything is controlled by the WPF engine that draws window content, fires keyboard and mouse events and does a lot of other things. The only way I know of to intercept messages is to install
HwndSourceHook with a delegate to our functions. The following code shows how to manage a
protected void OnLoaded(object sender, EventArgs e)
WindowInteropHelper helper = new WindowInteropHelper(this);
_hwndSource = HwndSource.FromHwnd(helper.Handle);
private IntPtr HookHandler(
ref bool handled
handled = false;
if (HostedPane.State == PaneState.DockableWindow &&
wParam.ToInt32() == HTCAPTION)
short x = (short)((lParam.ToInt32() & 0xFFFF));
short y = (short)((lParam.ToInt32() >> 16));
this, new Point(x, y), new Point(x - Left, y - Top));
handled = true;}
Although the delegate signature looks like a window procedure signature, it's not the same thing. You can, however, handle every message, even
- 13/05/07 -- First preliminary release
- 17/05/07 -- Update
- 11/06/07 -- Version 0.1: Floating window, many improvements and bug fixes
- 16/07/07 -- Version 0.1.1: Some annoying bug fixes
I bought my first computer in Nov 1991, 21st and I started programming with QBasic under MSDOS.
Today my main interest is developping applications with .NET and HTML5 stack.