Now that I’m back from staycation I’ve been looking around at some miscellany that needs to be done at the Lab. One of those items used my open-source (MIT ftw)Custom Chrome Window library. In the process of using it I came across a little bug that’s discussed on the WPF Window Chrome site here.
Basically, even though the “Caption bar” (aka “Title bar,” aka “Window bar,” aka “Non-client Window area”) was hidden, the Window.Top and Window.Left calculations take it into account. After a colleague of mine did some serious Google-foo on Win32 we found this solution which has been added to the library on GitHub. The solution is outlined below, starting with including some Win32 API calls:
public static class SafeNativeMethods
{
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
internal const int WS_CHILD = 0x40000000;
internal const int WS_VISIBLE = 0x10000000;
internal const int LBS_NOTIFY = 0x00000001;
internal const int HOST_ID = 0x00000002;
internal const int LISTBOX_ID = 0x00000001;
internal const int WS_VSCROLL = 0x00200000;
internal const int WS_BORDER = 0x00800000;
public const int GWL_EXSTYLE = -20;
public const int GWL_STYLE = -16;
public const int WS_EX_DLGMODALFRAME = 0x0001;
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOZORDER = 0x0004;
public const int SWP_FRAMECHANGED = 0x0020;
public const uint WM_SETICON = 0x0080;
public const int WS_DLGFRAME = 0x00400000;
public const int WS_SYSMENU = 0x80000;
public const int WS_MINIMIZEBOX = 0x20000;
public const int WS_MAXIMIZEBOX = 0x10000;
}
After which we need to call into these methods in the CustomChromeWindow class:
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(this).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = SafeNativeMethods.GetWindowLong(hwnd, SafeNativeMethods.GWL_EXSTYLE);
int style = SafeNativeMethods.GetWindowLong(hwnd, SafeNativeMethods.GWL_STYLE);
SafeNativeMethods.SetWindowLong(
hwnd,
SafeNativeMethods.GWL_STYLE,
style & ~SafeNativeMethods.WS_SYSMENU & ~SafeNativeMethods.WS_MINIMIZEBOX & ~SafeNativeMethods.WS_MAXIMIZEBOX & SafeNativeMethods.WS_DLGFRAME);
// Update the window's non-client area to reflect the changes
SafeNativeMethods.SetWindowPos(
hwnd,
IntPtr.Zero,
0, 0, 0, 0,
SafeNativeMethods.SWP_NOMOVE | SafeNativeMethods.SWP_NOSIZE | SafeNativeMethods.SWP_NOZORDER | SafeNativeMethods.SWP_FRAMECHANGED);
}
Hopefully you’ll never have to wallow in the pain of custom window chrome, but if you do take a look at the library on GitHub.