Search code examples
c#winformscursortitlebar

Hide a form's client area (but let the title bar be visible) when mouse is not over the title bar


I'm looking for a way to develop this:

Heberger image
(source: hostingpics.net)

When the mouse is over the form's title bar (rectange 1 on the picture) the form content (the rectangle 2) is visible & when the mouse is not over, it disappears but the rectangle 1 must remain visible!

How could i manage to do that ?

Thanks in advance


Solution

  • There are some mouse events related to the non-client area of the forms (WM_NCMOUSEMOVE, WM_NCMOUSELEAVE, ...) that can be used for this purpose. But this is not simple, because they are not included in Windows Forms. To use this events, you should override WndProc of your form. Catching WM_NCMOUSEMOVE event is somehow simple, but WM_NCMOSUELEAVE is a little tricky. Windows normally does not send mouse leave events to windows, unless they request it explicitly using TrackMouseEvent function.

    Here is the complete code that does exactly what you want:

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == 0xA0) // WM_NCMOUSEMOVE
            {
                TrackNcMouseLeave(this);
                ShowClientArea();
            }
            else if (m.Msg == 0x2A2) // WM_NCMOUSELEAVE
            {
                HideClientAreaIfPointerIsOut();
            }
    
            base.WndProc(ref m);
        }
    
        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            HideClientAreaIfPointerIsOut();
        }
    
        private int previouseHeight;
    
        private void ShowClientArea()
        {
            if (this.ClientSize.Height == 0)
                this.ClientSize = new Size(this.ClientSize.Width, previouseHeight);
        }
    
        private void HideClientAreaIfPointerIsOut()
        {
            if (this.Bounds.Contains(Cursor.Position))
                return;
            previouseHeight = this.ClientSize.Height;
            this.ClientSize = new Size(this.ClientSize.Width, 0);
        }
    
        public static void TrackNcMouseLeave(Control control)
        {
            TRACKMOUSEEVENT tme = new TRACKMOUSEEVENT();
            tme.cbSize = (uint)Marshal.SizeOf(tme);
            tme.dwFlags = 2 | 0x10; // TME_LEAVE | TME_NONCLIENT
            tme.hwndTrack = control.Handle;
            TrackMouseEvent(tme);
        }
    
        [DllImport("user32")]
        public static extern bool TrackMouseEvent([In, Out] TRACKMOUSEEVENT lpEventTrack);
    
        [StructLayout(LayoutKind.Sequential)]
        public class TRACKMOUSEEVENT
        {
            public uint cbSize;
            public uint dwFlags;
            public IntPtr hwndTrack;
            public uint dwHoverTime;
        }
    

    Put this code section in your form class, and that takes care of everything.

    By overriding WndProc we are handling required mouse events. In WM_NCMOUSEMOVE event, we call a method to inform the operating system that we are interested in WM_NCMOUSELEAVE event, and also we show the client area of the form (if not visible).
    In WM_NCMOUSELEAVE event we hide the client area of the form (if the cursor is not on the form). Every time the WM_NCMOUSELEAVE event is called, all tracking events requested by TrackMouseEvent are canceled, so we must call the TrackMouseEvent function every time in WM_NCMOUSEMOVE.

    Be aware that maximizing the form is not considered in this code and you should handle it somehow.