This article describes a base class for WPF windows. It hides all GDI chrome, without losing functionality. WPF promises entire freedom in look-and-feel of your application. In its standard configuration, however, a WPF Window is still hosted in a classic GDI-based WinForm. Its border and header are rendered by the operating system. This takes away a lot of styling opportunities: things like a custom close button or rounded borders become impossible. On top of that, the GDI chrome eats a lot of pixels away from your application. That waste of space becomes obvious if you use custom controls like the popular Ribbon.
I created the NoGdiWindow base class to solve all of these problems. The following screenshots from the attached sample application show the same WPF window - with and without using the base class. If you prefer the left window, you may stop reading now .
Hiding the GDI header
Setting the WindowStyle to System.Windows.WindowStyle.None removes the window header:
this.WindowStyle = System.Windows.WindowStyle.None;
You obviously loose the icon, the title and the minimize, maximize/restore, and close buttons. These are relatively easy to recover: the NoGdiWindow class contains the necessary commands and properties to rapidly create custom buttons in XAML. Here's an overview of the relevant class members:
Here's an example of a custom button:
<Button ToolTip="Maximize" Command="{Binding ElementName=Window, Path=MaximizeCommand}" Visibility="{Binding ElementName=Window, Path=MaximizeButtonVisibility}" />
Restoring the buttons is not sufficient: the beheaded window doesn't have a region anymore by which you can drag it, or minimize/restore by double-clicking. When using NoGdiWindow, all you need to do is create a region at the appropriate position and hook in the MouseLeftButtonDown event handler:
<Grid MouseLeftButtonDown="Header_MouseLeftButtonDown" Background="Transparent" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="32" Margin="80 0 0 0">
You may want to implement this functionality as a behavior instead of an event handler.
In the following screenshot, the region is highlighted. The left margin of 80 pixels keeps the ribbon's application menu accessible:
When you use WindowStyle.None your window will overlap the task bar if it's maximized. I personally don't mind at all. If you (or our end users) have a problem with this, you find some possible workarounds here.
Hiding the GDI border
You can make the remaining GDI border disappear by setting the AllowsTransparency property to True and your form's background to Transparent:
this.AllowsTransparency = true;
this.Background = new SolidColorBrush(Colors.Transparent);
This allows you to provide a rounded border around your window, or create an irregular shape. Unfortunately the native GDI border also hosts the resize handles, so you need to get these back. The NoGdiWindow draws a series of rectangles on the form, with the necessary mouse down handlers. Therefor, your main panel should be a Grid. In the following screenshot these rectangles are highlighted:
When the window is opened, it fetches its Win32 handle:
this.hwndSource = PresentationSource.FromVisual((Visual)sender) as HwndSource;
When you hold the left mouse button inside one of the resize rectangles, the resize message is sent to the operating system through a SendMessage call:
SendMessage(this.hwndSource.Handle, 0x112, (IntPtr)(61440 + direction), IntPtr.Zero);
The resize code is based on this article by Joshua. Here's an overview of the relevant class members:
The AllowsTransparency is implemented through a technique called 'layered windows'. If you type these keywords in your [insert favorite search engine here], you'll immediately see that there are some issues under Windows XP. Your user interface may run without hardware acceleration on older XP releases, and it definitely ruins some WPF controls (tooltips, popups, comboboxes). There there are also issues with WindowsFormHost controls, regardless of the operating system you use. NoGdiWindow is aware or this, and has some defensive code around the property:
if ((Environment.OSVersion.Version.Major > 5) & (!this.UsesWindowsFormsHost))
{
// Activate Transparency
// ...
}
Code
Here's the sample project :U2UConsult.WPF.NoGdiWindow.zip (1.31 mb)
Enjoy!