通常,您使用 Form.Visible 来检查 Window 是否完全可见。但有时屏幕上的窗口位于其他窗口下方,因此它实际上是不可见的。

那么如何在 C# Windows 窗体中检查窗口是否确实可见?

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

亲切的问候

其他提示

我GOOGLE了低谷网页,但coudn't找到任何直接的答案,看是否有窗口的一部分是用户真正可见。其实,我需要一种方法来“则hitTest”的形式,如果鼠标目前在窗口的可见部分的顶部。我想我会分享历时数天才能完成的代码:

public class VisibilityTester
{
    private delegate bool CallBackPtr(int hwnd, int lParam);
    private static CallBackPtr callBackPtr;

    /// <summary>
    /// The enumerated pointers of actually visible windows
    /// </summary>
    public static List<IntPtr> enumedwindowPtrs = new List<IntPtr>();
    /// <summary>
    /// The enumerated rectangles of actually visible windows
    /// </summary>
    public static List<Rectangle> enumedwindowRects = new List<Rectangle>();

    /// <summary>
    /// Does a hit test for specified control (is point of control visible to user)
    /// </summary>
    /// <param name="ctrlRect">the rectangle (usually Bounds) of the control</param>
    /// <param name="ctrlHandle">the handle for the control</param>
    /// <param name="p">the point to test (usually MousePosition)</param>
    /// <param name="ExcludeWindow">a control or window to exclude from hit test (means point is visible through this window)</param>
    /// <returns>boolean value indicating if p is visible for ctrlRect</returns>
    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);
    }

    /// <summary>
    /// Window enumeration callback
    /// </summary>
    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顺序,并将其与窗口的Z顺序进行比较。我觉得有人这样做已经这里

要回答这个问题的问,你可以尝试调用的 WindowFromPoint API函数查找的不同点,窗口的形式,并检查其是否返回任何你想到会在这一点上的把手。

嗯...奇怪的问题。:P

也许您可以询问表单的位置,如果两个表单重叠(找出它们的坐标,并制定一个简单的方法),请检查一个表单是否具有 Focus()。如果它有焦点,那么其他一定是“不可见的”(从某种意义上说,用户看不到它,因为它位于其他表单下方).

显然这个方法很hacky 最好, ,但您可以开始使用它。

您也可以.. :)您可以通过AutomationElement对应窗口中的ClickablePoint财产。 我不是100%肯定立法院这是否是完全正确的,但..它在的情况下,我99%的工作了,我仍然在其他1%,问题出在哪里(也许是在我这边还是坏用户检查处理,或。)

我aktually试图执行SLaks建议。虽然我写在VB.NET,而不是C#

Friend Structure PointStruct
    Public x As Int32
    Public y As Int32
End Structure

<System.Runtime.InteropServices.DllImport("user32.dll")> _
Friend Function WindowFromPoint(ByVal Point As PointStruct) As IntPtr
End Function

''' <summary>
''' Checks if a control is actually visible to the user completely
''' </summary>
''' <param name="control">The control to check.</param>
''' <returns>True, if the control is completely visible, false else.</returns>
''' <remarks>This is not 100% accurate, but feasible enough for my purpose.</remarks>
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方法可见。你要控制传递给基类,以便做实际的绘制,但你可以检测是否接收到绘制消息。 更新:不,这是不行的,对不起

在原则上,激活方法应该把你的窗口到前台,但在实践中我总是发现这个问题,如果其他进程具有输入焦点。如果你真的希望有人看到一个窗口,设置最高位,但预计它们是恼火!一个正确的方式得到一些关注的窗口是关闭并重新打开它,如果你能得逞的。

实现你要寻找的一种方法是使用一个通知图标,这将让用户的注意力的方式,符合Windows用户界面的指导方针。

只需设置Form.AlwaysOnTop属性true

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top