Fudging the non-client area of Custom Chrome Windows using WPF and Win32  

Friday, January 15 2010

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.


  • Posted by Charlie Robbins

Post a comment


(required, but not displayed)

(optional)