Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I tell if the mouse is over a top-level window?

How can I efficiently tell if the mouse is over a top-level window?

By "over", I mean that the mouse pointer is within the client rectangle of the top-level window and there is no other top-level window over my window at the location of the mouse pointer. In other words, if the user clicked the event would be sent to my top-level window (or one of its child windows).

I am writing in C# using Windows Forms, but I don't mind using p/invoke to make Win32 calls.

like image 529
Daniel Stutzbach Avatar asked Oct 10 '10 16:10

Daniel Stutzbach


2 Answers

You could use the WinAPI function WindowFromPoint. Its C# signature is:

[DllImport("user32.dll")]
static extern IntPtr WindowFromPoint(POINT Point);

Note that POINT here is not the same as System.Drawing.Point, but PInvoke provides a declaration for POINT that includes an implicit conversion between the two.

If you don’t already know the mouse cursor position, GetCursorPos finds it:

[DllImport("user32.dll")]
static extern bool GetCursorPos(out POINT lpPoint);

However, the WinAPI calls lots of things “windows”: controls inside a window are also “windows”. Therefore, you might not get a top-level window in the intuitive sense (you might get a radio button, panel, or something else). You could iteratively apply the GetParent function to walk up the GUI hierarchy:

[DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);

Once you find a window with no parent, that window will be a top-level window. Since the point you originally passed in belongs to a control that is not covered by another window, the top-level window is necessarily the one the point belongs to.

like image 159
Timwi Avatar answered Nov 13 '22 04:11

Timwi


After you obtain the window handle you can use Control.FromHandle() to get a reference to the control. Then check the relative mouse position to see if its it is the client area of the form or control. Like this:

    private void timer1_Tick(object sender, EventArgs e) {
        var hdl = WindowFromPoint(Control.MousePosition);
        var ctl = Control.FromHandle(hdl);
        if (ctl != null) {
            var rel = ctl.PointToClient(Control.MousePosition);
            if (ctl.ClientRectangle.Contains(rel)) {
                Console.WriteLine("Found {0}", ctl.Name);
                return;
            }
        }
        Console.WriteLine("No match");
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr WindowFromPoint(Point loc);
like image 23
Hans Passant Avatar answered Nov 13 '22 03:11

Hans Passant