How to detect if the mouse is inside the whole form and child controls?

rfgamaral picture rfgamaral · Jun 12, 2009 · Viewed 18.7k times · Source

I need to detect when the user moves the mouse over the Form and all its child controls and also when it leaves the Form. I tried the MouseEnter and MouseLeave events of the Form, I tried the WM_MOUSEMOVE & WM_MOUSELEAVE and WM_NCMOUSEMOVE & WM_NCMOUSELEAVE pairs of windows messages but none seem to work as I want...

Most of my Form is occupied by child controls of many sorts, there's not much client area visible. This means that if I move the mouse very quickly the mouse movement will not be detected, although the mouse is inside the Form.

For instance, I have a TextBox that is docked at the bottom and between the desktop and the TextBox, there's only a very small border. If I quickly move the mouse from the bottom into the TextBox, the mouse movement won't be detected, but the mouse is inside the TextBox, therefore, inside the Form.

How can I achieve what I need?

Answer

TcKs picture TcKs · Jun 12, 2009

You can hook the main message loop and preprocess/postprocess any (WM_MOUSEMOVE) message what you want.

public class Form1 : Form {
    private MouseMoveMessageFilter mouseMessageFilter;
    protected override void OnLoad(EventArgs e) {
        base.OnLoad( e );

        this.mouseMessageFilter = new MouseMoveMessageFilter();
        this.mouseMessageFilter.TargetForm = this;
        Application.AddMessageFilter(this.mouseMessageFilter);
    }

    protected override void OnClosed(EventArgs e) {
        base.OnClosed(e);
        Application.RemoveMessageFilter(this.mouseMessageFilter);
    }

    private class MouseMoveMessageFilter : IMessageFilter {
        public Form TargetForm { get; set; }

        public bool PreFilterMessage( ref Message m ) {
            int numMsg = m.Msg;
            if ( numMsg == 0x0200 /*WM_MOUSEMOVE*/)
                this.TargetForm.Text = string.Format($"X:{Control.MousePosition.X}, Y:{Control.MousePosition.Y}");

            return false;
        }
    }
}