【发布时间】:2011-04-23 12:07:21
【问题描述】:
如何有效地判断鼠标是否位于顶级窗口上?
“over”是指鼠标指针位于顶级窗口的客户矩形内并且在鼠标所在的窗口上方没有其他顶级窗口指针。换句话说,如果用户点击了该事件,该事件将被发送到我的顶级窗口(或其子窗口之一)。
我正在使用 Windows 窗体用 C# 编写代码,但我不介意使用 p/invoke 进行 Win32 调用。
【问题讨论】:
如何有效地判断鼠标是否位于顶级窗口上?
“over”是指鼠标指针位于顶级窗口的客户矩形内并且在鼠标所在的窗口上方没有其他顶级窗口指针。换句话说,如果用户点击了该事件,该事件将被发送到我的顶级窗口(或其子窗口之一)。
我正在使用 Windows 窗体用 C# 编写代码,但我不介意使用 p/invoke 进行 Win32 调用。
【问题讨论】:
您可以使用 WinAPI 函数WindowFromPoint。它的 C# 签名是:
[DllImport("user32.dll")]
static extern IntPtr WindowFromPoint(POINT Point);
注意这里的POINT和System.Drawing.Point不一样,但是PInvoke提供了a declaration for POINT that includes an implicit conversion between the two。
如果您还不知道鼠标光标的位置,GetCursorPos 会找到它:
[DllImport("user32.dll")]
static extern bool GetCursorPos(out POINT lpPoint);
但是,WinAPI 将很多东西称为“窗口”:窗口内的控件也是“窗口”。因此,您可能无法获得直观意义上的顶级窗口(您可能会获得单选按钮、面板或其他东西)。您可以迭代地应用 GetParent 函数来遍历 GUI 层次结构:
[DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)]
public static extern IntPtr GetParent(IntPtr hWnd);
一旦您找到一个没有父窗口的窗口,该窗口将成为顶级窗口。由于您最初传入的点属于未被另一个窗口覆盖的控件,因此顶级窗口必然是该点所属的那个。
【讨论】:
Control.MousePosition获取;父窗口可以通过从user32.dll 调用GetAncestor(hwnd, 2) 找到(GetParent 不仅检查父窗口,还检查窗口所有者,这可能不好)。
获取窗口句柄后,您可以使用 Control.FromHandle() 获取对控件的引用。然后检查相对鼠标位置,看它是否是窗体或控件的客户区。像这样:
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);
【讨论】:
GetWindowLongPtr()来检查它是否是一个顶级窗口。将GWL_EXSTYLE 传递给nIndex 参数,然后对函数结果进行按位运算检查是否包含WS_EX_TOPMOST 值。