【问题标题】:How can I check that a window is fully visible on the user's screen?如何检查窗口在用户屏幕上是否完全可见?
【发布时间】:2016-09-21 14:36:49
【问题描述】:

有没有办法检查 WinForm 在屏幕上是否完全可见(例如没有超出屏幕范围?)

我已经尝试为此使用 SystemInformation.VirtualScreen,只要虚拟屏幕是矩形,它就可以很好地工作,但一旦不是(例如,L 形的 3 个屏幕),SystemInformation.VirtualScreen 返回最小的矩形包含所有可见像素(因此 L 右上角的窗口虽然在虚拟屏幕中,但将不可见)


我试图实现这一点的原因是我希望我的程序在它们所在的最后一个位置打开其子窗口,但我不希望这些窗口在用户更改时消失已设置(例如,从他的笔记本电脑上拔下额外的屏幕)

【问题讨论】:

    标签: c# windows multiple-monitors virtual-screen


    【解决方案1】:

    这是我最终的做法:

    bool isPointVisibleOnAScreen(Point p)
    {
        foreach (Screen s in Screen.AllScreens)
        {
            if (p.X < s.Bounds.Right && p.X > s.Bounds.Left && p.Y > s.Bounds.Top && p.Y < s.Bounds.Bottom)
                return true;
        }
        return false;
    }
    
    bool isFormFullyVisible(Form f)
    {
        return isPointVisibleOnAScreen(new Point(f.Left, f.Top)) && isPointVisibleOnAScreen(new Point(f.Right, f.Top)) && isPointVisibleOnAScreen(new Point(f.Left, f.Bottom)) && isPointVisibleOnAScreen(new Point(f.Right, f.Bottom));
     }
    

    如果用户的显示设置中有“漏洞”(参见下面的示例),可能会出现一些误报,但我认为我的任何用户都不会遇到这种情况 :)

       [1]
    [2][X][3]
    

    【讨论】:

    • 我喜欢这个解决方案。不过,请注意它应该是p.X &lt; s.Bounds.Right
    • 可以简化为if(s.Bounds.Contains(p)) 可能s.WorkingArea 在多显示器方面会更好,边界不一致...
    【解决方案2】:

    我会这样做:

    这会将显示边界内的控件(表单)移动到尽可能靠近原始位置的位置。

        private void EnsureVisible(Control ctrl)
        {
            Rectangle ctrlRect = ctrl.DisplayRectangle; //The dimensions of the ctrl
            ctrlRect.Y = ctrl.Top; //Add in the real Top and Left Vals
            ctrlRect.X = ctrl.Left;
            Rectangle screenRect = Screen.GetWorkingArea(ctrl); //The Working Area fo the screen showing most of the Ctrl
    
            //Now tweak the ctrl's Top and Left until it's fully visible. 
            ctrl.Left += Math.Min(0, screenRect.Left + screenRect.Width - ctrl.Left - ctrl.Width);
            ctrl.Left -= Math.Min(0, ctrl.Left - screenRect.Left);
            ctrl.Top += Math.Min(0, screenRect.Top + screenRect.Height - ctrl.Top - ctrl.Height);
            ctrl.Top -= Math.Min(0, ctrl.Top - screenRect.Top);
    
        }
    

    当然,要回答您最初的问题,而不是移动控件,您只需检查 4 个 Math.Min 中的任何一个是否返回 0 以外的值。

    【讨论】:

    • 如果你有一个多显示器设置会发生什么?
    • @Sam 来自 Microsoft docs for Screen.GetWorkingArea msdn.microsoft.com/en-us/library/45zswy9x.aspx 它声明“检索包含指定控件的最大区域的显示器的工作区域”。基本上将控件移动到完全在屏幕内,它主要在里面。
    • 前三行好像没什么作用,直接删掉就好了。
    【解决方案3】:

    我试图做同样的事情来检测窗口是否在屏幕外打开,然后相应地将其重新定位到之前找到它的最近位置。我浏览了整个互联网,但人们提供的所有解决方案都没有奏效。

    因此,我自己承担起创建自己的课程的责任,该课程恰好做到了这一点,并且 100% 有效。

    这是我的代码

    public static class ScreenOperations
    {
        public static bool IsWindowOnAnyScreen(Window Window, short WindowSizeX, short WindowSizeY, bool AutoAdjustWindow)
        {
            var Screen = System.Windows.Forms.Screen.FromHandle(new WindowInteropHelper(Window).Handle);
    
            bool LeftSideTest = false, TopSideTest = false, BottomSideTest = false, RightSideTest = false;
    
            if (Window.Left >= Screen.WorkingArea.Left)
                LeftSideTest = true;
    
            if (Window.Top >= Screen.WorkingArea.Top)
                TopSideTest = true;
    
            if ((Window.Top + WindowSizeY) <= Screen.WorkingArea.Bottom)
                BottomSideTest = true;
    
            if ((Window.Left + WindowSizeX) <= Screen.WorkingArea.Right)
                RightSideTest = true;
    
            if (LeftSideTest && TopSideTest && BottomSideTest && RightSideTest)
                return true;
            else
            {
                if (AutoAdjustWindow)
                {
                    if (!LeftSideTest)
                        Window.Left = Window.Left - (Window.Left - Screen.WorkingArea.Left);
    
                    if (!TopSideTest)
                        Window.Top = Window.Top - (Window.Top - Screen.WorkingArea.Top);
    
                    if (!BottomSideTest)
                        Window.Top = Window.Top - ((Window.Top + WindowSizeY) - Screen.WorkingArea.Bottom);
    
                    if (!RightSideTest)
                        Window.Left = Window.Left - ((Window.Left + WindowSizeX) - Screen.WorkingArea.Right);
                }
            }
    
            return false;
        }
    }
    

    用法:ScreenOperations.IsWindowOnAnyScreen(WPFWindow, WPFWindowSizeX, WPFWindowSizeY, true); 这将检查窗口是否完全不在屏幕上,即任务栏下方 1 个像素或用户当前监视器下方 1 个像素。

    它会检测哪个监视器窗口首先打开,因此它应该与多监视器一起使用。

    如果窗口在屏幕上,则此方法返回 true,否则返回 false。

    最后一个参数用于自动将窗口调整到屏幕上最近的部分。如果您将该参数设置为 false,它将不会为您调整窗口。

    所以这是针对此问题的完整 WPF 解决方案,如果您知道如何操作,WinForm 转换应该很容易,Change Window to Form 和FromHandle(Form.Handle) 应该可以工作。

    【讨论】:

    • 我喜欢这个解决方案。因为它是静态的,所以很容易包装在附加属性中。
    • 效果很好,谢谢。简单的数学可以进一步简化: if (!leftSideTest) window.Left = screen.WorkingArea.Left; if (!topSideTest) window.Top = screen.WorkingArea.Top; if (!bottomSideTest) window.Top = screen.WorkingArea.Bottom - sizeY; if (!rightSideTest) window.Left = screen.WorkingArea.Right - sizeX;
    【解决方案4】:

    这是我的解决方案。它解决了“洞”问题。

        /// <summary>
        /// True if a window is completely visible 
        /// </summary>
        static bool WindowAllVisible(Rectangle windowRectangle)
        {
            int areaOfWindow = windowRectangle.Width * windowRectangle.Height;
            int areaVisible = 0;
            foreach (Screen screen in Screen.AllScreens)
            {
                Rectangle windowPortionOnScreen = screen.WorkingArea;
                windowPortionOnScreen.Intersect(windowRectangle);
                areaVisible += windowPortionOnScreen.Width * windowPortionOnScreen.Height;
                if (areaVisible >= areaOfWindow)
                {
                    return true;
                }
            }
            return false;
        }
    

    【讨论】:

      【解决方案5】:

      检查是否Screen.AllScreens.Any(s =&gt; s.WorkingArea.Contains(rect))

      【讨论】:

      • 这个方法是一个好的开始,但是如果你有多个显示器并且屏幕在它们之间分割,那么你会得到一个误报。
      最近更新 更多