如何检查Windows窗体中是否真的可以看到窗口?

通常,您使用Form.Visible来检查Window是否可见。 但有时屏幕窗口在其他窗口下面,所以它真的看不见。

那么如果窗口真的可见,如何检查c#Windows窗体?

我想完成这个:当我在键盘上单击CTRL + K并且我的窗口在我的屏幕上可见时它什么也没做。 但当它在其他窗口下面时,它会弹到顶部(带到前面)。

亲切的问候

您可以在表单上调用Activate方法,如果尚未将其带到前面。

但请注意,如果其他程序处于活动状态,通常只会闪烁桌面按钮(取决于您从哪里调用)。 这是Windows 针对焦点窃取的标准保护 ,您不应该尝试解决它 。

我通过网络搜索,但是没有找到任何直接的答案,看看窗户的一部分是否真的对用户可见。 如果鼠标当前位于窗口可见部分的顶部,我实际上需要一种方法来“测试”表单。 我以为我会分享需要几天才能完成的代码:

 public class VisibilityTester { private delegate bool CallBackPtr(int hwnd, int lParam); private static CallBackPtr callBackPtr; ///  /// The enumerated pointers of actually visible windows ///  public static List enumedwindowPtrs = new List(); ///  /// The enumerated rectangles of actually visible windows ///  public static List enumedwindowRects = new List(); ///  /// Does a hit test for specified control (is point of control visible to user) ///  /// the rectangle (usually Bounds) of the control /// the handle for the control /// the point to test (usually MousePosition) /// a control or window to exclude from hit test (means point is visible through this window) /// boolean value indicating if p is visible for ctrlRect public static bool HitTest(Rectangle ctrlRect, IntPtr ctrlHandle, Point p, IntPtr ExcludeWindow) { // clear results enumedwindowPtrs.Clear(); enumedwindowRects.Clear(); // Create callback and start enumeration callBackPtr = new CallBackPtr(EnumCallBack); EnumDesktopWindows(IntPtr.Zero, callBackPtr, 0); // Go from last to first window, and substract them from the ctrlRect area Region r = new Region(ctrlRect); bool StartClipping = false; for (int i = enumedwindowRects.Count - 1; i >= 0; i--) { if (StartClipping && enumedwindowPtrs[i] != ExcludeWindow) { r.Exclude(enumedwindowRects[i]); } if (enumedwindowPtrs[i] == ctrlHandle) StartClipping = true; } // return boolean indicating if point is visible to clipped (truly visible) window return r.IsVisible(p); } ///  /// Window enumeration callback ///  private static bool EnumCallBack(int hwnd, int lParam) { // If window is visible and not minimized (isiconic) if (IsWindow((IntPtr)hwnd) && IsWindowVisible((IntPtr)hwnd) && !IsIconic((IntPtr)hwnd)) { // add the handle and windowrect to "found windows" collection enumedwindowPtrs.Add((IntPtr)hwnd); RECT rct; if (GetWindowRect((IntPtr)hwnd, out rct)) { // add rect to list enumedwindowRects.Add(new Rectangle(rct.Left, rct.Top, rct.Right - rct.Left, rct.Bottom - rct.Top)); } else { // invalid, make empty rectangle enumedwindowRects.Add(new Rectangle(0, 0, 0, 0)); } } return true; } [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IsWindowVisible(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IsWindow(IntPtr hWnd); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IsIconic(IntPtr hWnd); [DllImport("user32.dll")] private static extern int EnumDesktopWindows(IntPtr hDesktop, CallBackPtr callPtr, int lPar); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [StructLayout(LayoutKind.Sequential)] private struct RECT { public int Left; // x position of upper-left corner public int Top; // y position of upper-left corner public int Right; // x position of lower-right corner public int Bottom; // y position of lower-right corner public override string ToString() { return Left + "," + Top + "," + Right + "," + Bottom; } } } 

您可以使用Windows API枚举所有窗口,检索其Z-Order并将其与窗口的Z-Order进行比较。 我想有人已经在这里做过了 。

要回答问题,您可以尝试调用WindowFromPoint API函数在窗体上的各个点找到窗口,并检查它是否返回您希望在该点处的任何句柄。

嗯……奇怪的问题。 :P

也许你可以询问表格的位置,如果两个表格相互交错(找出它们的坐标,并制作一个简单的方法),检查一个表格是否有Focus()。 如果它具有焦点,则其他必须是“不可见的”( 在某种意义上,用户无法看到它,因为它在另一个forms下面 )。

显然这种方法充其量只是hacky,但是你可以开始使用它。

你也可以.. :)从与窗口对应的AutomationElement中获取ClickablePoint属性。 我不是100%确定这是否完全准确,但它已经在99%的情况下适用于我,我仍在检查另一个1%,问题所在(可能在我身边或坏用户)处理,或。)

我暂时尝试实施SLaks建议。 虽然我是用VB.NET编写的,但不是C#

 Friend Structure PointStruct Public x As Int32 Public y As Int32 End Structure  _ Friend Function WindowFromPoint(ByVal Point As PointStruct) As IntPtr End Function '''  ''' Checks if a control is actually visible to the user completely '''  ''' The control to check. ''' True, if the control is completely visible, false else. ''' This is not 100% accurate, but feasible enough for my purpose. Public Function IsControlVisibleToUser(ByVal control As Windows.Forms.Control) As Boolean If Not control.Visible Then Return False Dim bAllPointsVisible As Boolean = True Dim lPointsToCheck As New List(Of Point) 'Add the points to check. In this case add the edges and some border points 'between the edges. 'Strangely, the exact edge points always return the false handle. 'So we add a pixel into the control. lPointsToCheck.Add(New Point(control.Left + 1, control.Top + 1)) lPointsToCheck.Add(New Point(control.Right - 1, control.Top + 1)) lPointsToCheck.Add(New Point(control.Right - 1, control.Bottom - 1)) lPointsToCheck.Add(New Point(control.Left + 1, control.Bottom - 1)) lPointsToCheck.Add(New Point(control.Left + control.Width / 2, control.Top + 1)) lPointsToCheck.Add(New Point(control.Right - 1, control.Top + control.Height / 2)) lPointsToCheck.Add(New Point(control.Right - control.Width / 2, control.Bottom - 1)) lPointsToCheck.Add(New Point(control.Left + 1, control.Bottom - control.Height / 2)) 'lPointsToCheck.Add(New Point(control.Left + control.Width / 2, control.Top + control.Height / 2)) 'Check each point. If all points return the handle of the control, 'the control should be visible to the user. For Each oPoint In lPointsToCheck Dim sPoint As New PointStruct() With { .x = oPoint.X, _ .y = oPoint.Y _ } bAllPointsVisible = bAllPointsVisible And ( _ (WindowFromPoint(sPoint) = control.Handle) _ ) Next Return bAllPointsVisible End Function 

您应该能够通过覆盖OnPaint方法来确定窗口是否可见。 您需要将控制权传递给基类才能进行实际绘制,但您将能够检测是否收到了绘制消息。 更新:不,这不起作用,抱歉!

原则上,Activate方法应该将窗口置于前台,但实际上,如果其他进程具有输入焦点,我总是发现这有问题。 如果你真的想要有人看到一个窗口,请设置最顶端的位,但期望它们生气! 如果你可以侥幸逃脱,那么关注一个窗口的一个万无一失的方法是关闭它并重新打开它。

实现您所需要的一种方法是使用通知图标,这将以符合Windows UI准则的方式引起用户的注意。

只需将Form.AlwaysOnTop属性设置为true