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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6591090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 17:18:12  来源:igfitidea点击:

How can I check that a window is fully visible on the user's screen?

c#windowsmultiple-monitorsvirtual-screen

提问by Brann

Is there a way to check that a WinForm is fully visible on the screen (eg is not out of bounds of the screen?)

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

I've tried using SystemInformation.VirtualScreen for this, which works great as long as the virtual screen is a rectangle, but as soon as it's not (eg 3 screens in a L shape), SystemInformation.VirtualScreen returns the smallest rectangle containing all the visible pixels (so a window on the upper right corner of the L won't be visible although it's in the virtual screen)

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



The reason I'm trying to achieve this is that I'd like my program to open its child windows in the last location they were on, but I don't want those window to be out of view if the user changes is setup (eg unplugs the extra screen from his laptop)

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

采纳答案by Brann

Here's how I eventually did it :

这是我最终做到的:

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));
 }

There might be some false positive if the user has a "hole" in his display setup (see example below) but I don't think any of my users will ever be in such a situation :)

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

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

回答by deepee1

Here's how I would do it:

这是我将如何做到的:

This will move the control (form) inside the Display bounds as close as it can to the original location.

这会将 Display 边界内的控件(窗体)移动到尽可能靠近原始位置的位置。

    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);

    }

Of course to answer your original question instead of moving the control you could just check if any of the 4 Math.Min's returned something other than 0.

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

回答by SirTechington

This is my solution. It solves the "hole" problem.

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

    /// <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;
    }

回答by SLaks

Check whether Screen.AllScreens.Any(s => s.WorkingArea.Contains(rect))

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

回答by Joey Johnson

I was trying to do this exact same thing detect if the window opened off screen then accordingly reposition it to the nearest location where it was previously found at. I look all over the internet and nothing worked from all the solutions people offered.

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

So i took it upon myself to make my own class that does just exactly this and it works 100%.

所以我自己动手制作了我自己的课程,它完全可以做到这一点,并且 100% 有效。

Here is my code

这是我的代码

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;
    }
}

Usage: ScreenOperations.IsWindowOnAnyScreen(WPFWindow, WPFWindowSizeX, WPFWindowSizeY, true);this will check if the window is offscreen at all, that being 1 pixel under the taskbar or 1 pixel off the users current monitor.

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

It detects which monitor the window if on first so it should work with multi-monitors.

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

this method returns true if the window is on the screen and false if its not.

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

The last parameter is for auto adjusting the window to the nearest part on the screen for you. if you put false for that parameter it will not adjust the window for you.

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

So this is a complete WPF solution to this issue, and WinForm converting should be easy if you know how to do it, Change Window to Form and FromHandle(Form.Handle)should work.

因此,这是针对此问题的完整 WPF 解决方案,如果您知道如何操作,WinForm 转换应该很容易,将 Window 更改为 Form 并且FromHandle(Form.Handle)应该可以工作。