Search code examples
c#winformswinapiaero-glass

Click event not raised in expanded glass area?


so I just expanded the glass area of my form into the client area with DwmExtendFrameIntoClientArea (Vista / 7 Aero stuff).

I have already sent a Windows message in the override Form class method OnMouseDown() causing the window to be movable with the glass area, as explained here Make a borderless form movable?.

However because of this, I am not receiving any form Click / MouseClick / DoubleClick etc. events when clicking on the expanded glass area.

I actually want the form to maximize when I double click the top expanded glass area, like normal titlebars do.

Here's the code of the Form-inheriting class:

protected override void OnMouseDown(MouseEventArgs e)
{
    // Fensterverschiebung in Glass-Regionen
    if (_glassMovable && e.Button == MouseButtons.Left
        && (e.X < _glassPadding.Left || e.X > Width - _glassPadding.Right
        || e.Y < _glassPadding.Top || e.Y > Height - _glassPadding.Bottom))
    {
        NativeMethods.ReleaseCapture();
        NativeMethods.SendMessage(Handle, NativeMethods.WM_NCLBUTTONDOWN,
            NativeMethods.HT_CAPTION, 0);
    }

    base.OnMouseDown(e);
}

protected override void OnMouseDoubleClick(MouseEventArgs e)
{
    // Fenstermaximierung / Minimierung in Glass-Regionen
    if (MaximizeBox && e.Button == MouseButtons.Left && e.Y < _glassPadding.Top)
    {
        if (WindowState == FormWindowState.Normal)
        {
            WindowState = FormWindowState.Maximized;
        }
        else if (WindowState == FormWindowState.Maximized)
        {
            WindowState = FormWindowState.Normal;
        }
    }

    base.OnMouseDoubleClick(e);
}

Is there any way to get this to work?


Solution

  • BoltClocks link to a solution for WPF inspired me for the following, similar code for WinForms.

    I now override WndProc instead of the OnMouse* events.

    The glass region behaves exactly like a title bar, e.g. drag'n'drop with dock support in Windows 7 and double click for maximizing / restoring. Additionally, this solution now supports the system window context menu when right clicking the glass area.

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
    }
    
    private const int WM_NCHITTEST     = 0x0084;
    private const int WM_NCRBUTTONDOWN = 0x00A4;
    private const int WM_SYSCOMMAND    = 0x0112;
    private const int HT_CAPTION       = 0x02;
    private const int TPM_RIGHTBUTTON  = 0x0002;
    private const int TPM_RETURNCMD    = 0x0100;
    
    [DllImport("user32.dll")]
    private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    
    [DllImport("user32.dll")]
    private static extern int TrackPopupMenu(int hMenu, int wFlags, int x, int y,
        int nReserved, int hwnd, ref RECT lprc);
    
    [DllImport("user32.dll")]
    private static extern int PostMessage(int hWnd, int Msg, int wParam,
        int lParam);
    
    protected override void WndProc(ref Message m)
    {
        if (!DesignMode)
        {
            switch (m.Msg)
            {
                case WM_NCHITTEST:
                    if (MouseInClientArea())
                    {
                        if (MouseInGlassArea())
                        {
                            m.Result = (IntPtr)HT_CAPTION;
                        }
                        else
                        {
                            m.Result = IntPtr.Zero;
                        }
                        return;
                    }
                    break;
                case WM_NCRBUTTONDOWN:
                    if (MouseInClientArea())
                    {
                        IntPtr menuHandle = GetSystemMenu(Handle, false);
                        RECT rect = new RECT();
                        int menuItem = TrackPopupMenu(menuHandle.ToInt32(),
                            TPM_RIGHTBUTTON | TPM_RETURNCMD,
                            Cursor.Position.X, Cursor.Position.Y, 0,
                            Handle.ToInt32(), ref rect);
                        if (menuItem != 0)
                        {
                            PostMessage(Handle.ToInt32(), WM_SYSCOMMAND,
                                menuItem, 0);
                        }
                    }
                    break;
            }
        }
        base.WndProc(ref m);
    }
    
    private bool MouseInClientArea()
    {
        Point p = PointToClient(Cursor.Position);
        return (p.X > 0 && p.X < ClientRectangle.Width
            && p.Y > 0 && p.Y < ClientRectangle.Height);
    }
    
    private bool MouseInGlassArea()
    {
        if (_glassPadding.Left == -1 || _glassPadding.Right == -1
            || _glassPadding.Top == -1 || _glassPadding.Bottom == -1)
        {
            return true;
        }
        else
        {
            Point p = PointToClient(Cursor.Position);
            return (p.X < _glassPadding.Left
                || p.X > ClientRectangle.Width - _glassPadding.Right
                || p.Y < _glassPadding.Top
                || p.Y > ClientRectangle.Height - _glassPadding.Bottom);
        }
    }