如何检查窗口在用户屏幕上是否完全可见?

17
有没有一种方法可以检查WinForm是否完全显示在屏幕上(例如并不超出屏幕范围)?
我尝试使用SystemInformation.VirtualScreen来解决这个问题,当虚拟屏幕是一个矩形时,它非常有效,但是一旦它不是(例如L形的3个屏幕),SystemInformation.VirtualScreen会返回包含所有可见像素的最小矩形(因此,在L的右上角的窗口将不可见,即使它在虚拟屏幕中)。
我尝试实现这个功能的原因是我想让我的程序在子窗口的最后位置打开,但如果用户更改设置(例如从笔记本电脑上拔掉额外的屏幕),我不希望这些窗口超出视野。
5个回答

15

这是我最终的实现方法:

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]

4
我喜欢这个解决方案。不过请注意,应该是 p.X < s.Bounds.Right - Anders Carstensen
可以简化为 if(s.Bounds.Contains(p))。而且在涉及到多显示器时,也许使用 s.WorkingArea 会更好,因为边界不一致... - joe
我真的很喜欢这个解决方案,肯定是+1。我使用了LINQ而不是foreach循环,代码如下:var result = Screen.AllScreens.Any(x => x.Bounds.Contains(point)); - Nate W

7
以下是我的翻译:

这是我会的操作方式:

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

    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
1
根据微软文档中Screen.GetWorkingArea的说明(http://msdn.microsoft.com/en-us/library/45zswy9x.aspx),该函数可以“检索包含指定控件最大区域的显示器的工作区”。简单来说,就是将控件移动到其大部分位于屏幕内的位置。 - deepee1
看起来前三行没有任何作用,可以直接删除。 - GSerg

6
这是我的解决方案。它解决了“孔洞”问题。
    /// <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;
    }

4
我试图做同样的事情,即检测窗口是否在屏幕外打开,然后将其重新定位到先前找到的最近位置。我在互联网上搜索了很多解决方案,但都没有奏效。
因此,我自己编写了一个类来完成这个任务,它可以完美地工作。
以下是我的代码:
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转换应该很容易,将Window更改为Form并使用FromHandle(Form.Handle)即可。

我喜欢这个解决方案。因为它是静态的,所以很容易包装成一个附加属性。 - gregsdennis
非常好,谢谢。然而,简单的数学计算还可以更加简化: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; - Jan Zeman

2

检查是否Screen.AllScreens.Any(s => s.WorkingArea.Contains(rect))

该代码用于检查屏幕是否包含指定的矩形区域。

6
这种方法是一个不错的开始,但如果你有多个显示器并且屏幕被拆分在它们两个之间,那么你会得到一个错误的结果。 - deepee1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接