|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionRecently, 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 codeThere are three fundamental classes: <Window x:Class="DockingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DockingDemo" Height="500" Width="500"
xmlns:custom="clr-namespace:DockingLibrary;assembly=DockingLibrary"
Loaded="OnLoaded" Background="LightGray" >
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="New" Click="NewDocument"/>
<MenuItem Header="Exit" Click="ExitApplication"/>
</MenuItem>
<MenuItem Header="Edit"/>
<MenuItem Header="Window">
<MenuItem Header="Explorer" Click="ShowExplorerWindow"/>
<MenuItem Header="Output" Click="ShowOutputWindow"/>
<MenuItem Header="Property" Click="ShowPropertyWindow"/>
<MenuItem Header="ToDoList" Click="ShowListWindow"/>
</MenuItem>
<MenuItem Header="?"/>
</Menu>
<ToolBar DockPanel.Dock="Top">
<Button>OK</Button>
</ToolBar>
<custom:DockManager Name ="DockManager"/>
</DockPanel>
</Window>
Notice that to use 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();
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, EventArgs e)
{
//set window parent for dragging operations
dockManager.ParentWindow = this;
//Show PropertyWindow docked to the top border
propertyWindow.DockManager = dockManager;
propertyWindow.Show(Dock.Top);
//Show ExplorerWindow docked to the right border as default
explorerWindow.DockManager = dockManager;
explorerWindow.Show();
//Show ListWindow in documents pane
listWindow.DockManager = dockManager;
listWindow.ShowAsDocument();
}
}
Ib order to be dockable, a window must derive from private bool ContainsDocument(string docTitle)
{
foreach (DockingLibrary.DocumentContent doc in DockManager.Documents)
if (string.CompareOrdinal(doc.Title, docTitle) == 0)
return true;
return false;
}
private void NewDocument(object sender, EventArgs e)
{
string title = "Document";
int i = 1;
while (ContainsDocument(title + i.ToString()))
i++;
DocumentWindow doc = new DocumentWindow();
doc.Title = title+i.ToString();
DockManager.AddDocumentContent(doc);
}
Points of interestExactly how is docking realized? I implement a simple algorithm here to manage relations between windows that are docked.
Each node is a group of either two From version 0.1, the library supports floating windows, as you can see in VS. A floating window is an instance of the
As you can see in the previous image, 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 protected void OnLoaded(object sender, EventArgs e)
{
WindowInteropHelper helper = new WindowInteropHelper(this);
_hwndSource = HwndSource.FromHwnd(helper.Handle);
_hwndSource.AddHook(new HwndSourceHook(this.HookHandler));
}
private IntPtr HookHandler(
IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam,
ref bool handled
)
{
handled = false;
switch(msg)
{
case WM_NCLBUTTONDOWN:
if (HostedPane.State == PaneState.DockableWindow &&
wParam.ToInt32() == HTCAPTION)
{
short x = (short)((lParam.ToInt32() & 0xFFFF));
short y = (short)((lParam.ToInt32() >> 16));
HostedPane.ReferencedPane.DockManager.Drag(
this, new Point(x, y), new Point(x - Left, y - Top));
handled = true;}
break;
}
}
}
Although the delegate signature looks like a window procedure signature, it's not the same thing. You can, however, handle every message, even History
|
||||||||||||||||||||||