How to check if window is really visible in Windows Forms?

后端 未结 9 1241
小蘑菇
小蘑菇 2020-12-30 09:59

Normally you use Form.Visible to check if Window is visible at all. But sometimes on the screen window is below other windows so it\'s really invisible.

So how to ch

相关标签:
9条回答
  • 2020-12-30 10:30

    I googled trough the web, but coudn't find any straight answer to see if a part of a window is truly visible to the user. I actually needed a way to "hittest" the form, if the mouse is currently on top of the visible part of the window. I thought I'd share the code which took several days to accomplish:

    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;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-30 10:31

    Hm... weird question. :P

    Perhaps you could ask the location of the forms, and if two forms interlap (figure out their coords, and make a simple method) check if one form has Focus(). If it has focus, then other must be "invisible" (in the sense that a user can't see it because it's underneath the other form).

    Obviously this method is hacky at best, but it's something you can start working with.

    0 讨论(0)
  • 2020-12-30 10:35

    You could use Windows API to enumerate all windows, retrieve their Z-Order and compare it with the Z-Order of your window. I think someone did this already here.

    0 讨论(0)
提交回复
热议问题