An issue that came to my attention only recently is that the borders of WPF (update: WPF is not actually to blame) windows without captions/title-bars (that is, with ResizeMode set to ‘CanResize’ and WindowStyle set to ‘None’) are drawn incorrectly when the DWM (read: Aero Glass) is enabled. Specifically, the upper and left borders are drawn one pixel too thin (e.g. 3 pixels when the system setting is 4 pixels) and the colour of the bottom and right borders is different to that of other windows. I’ve tried to illustrate these differences in the screenshot below (the image on the left is of a WPF window).
It is conceivable that this will be fixed in a future version of WPF Windows, but for now we can use the DwmExtendFrameIntoClientArea function to do it manually. This is only necessary when the DWM is enabled, of course.
Extending Aero Glass into the client area of a window has been covered countless times elsewhere, but I’ll reproduce the process here for convenience.
First, we’ll define the PInvoke signature:
1 2 |
[DllImport("dwmapi.dll")] private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins); |
You’ll notice that the function uses a MARGINS type, which is pretty simple:
1 2 3 4 5 6 7 8 |
[StructLayout(LayoutKind.Sequential)] private struct MARGINS { public int cxLeftWidth; public int cxRightWidth; public int cyTopHeight; public int cyBottomHeight; }; |
In our window’s markup, we need to use a Border (or equivalent) as our root element:
1 2 3 |
<Border Name="WindowBorder" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"> ... </Border> |
And finally, in our code behind, we’ll add the following code (you can add it to your Window’s Loaded event if you only want to do this once):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// work out the current screen's DPI Matrix screenmatrix = windowhandlesource.CompositionTarget.TransformToDevice; double dpiX = screenmatrix.M11; // 1.0 = 96 dpi if (GlassEnabled) { // set the root border element's margin to 1 pixel WindowBorder.Margin = new Thickness(1 / dpiX); this.BorderThickness = new Thickness(0); // set the background of the window to transparent (otherwise the inner border colour won't be visible) windowhandlesource.CompositionTarget.BackgroundColor = Colors.Transparent; int xmargin = 1; int ymargin = 1; MARGINS margins = new MARGINS() { cxLeftWidth = xmargin, cxRightWidth = xmargin, cyBottomHeight = ymargin, cyTopHeight = ymargin }; DwmExtendFrameIntoClientArea(windowhandlesource.Handle, ref margins); } else { WindowBorder.Margin = new Thickness(0); // reset the margin if the DWM is disabled this.BorderThickness = new Thickness(1 / dpiX); // set the window's border thickness to 1 pixel } |
It is assumed that the GlassEnabled boolean I reference has been set with the DwmIsCompositionEnabled function, the implementation of which is well documented elsewhere.
The neat trick I used to find the window’s DPI comes from this post by fjparisIII.
Update 2011-06-11: Fixed some DPI issues.