开发者

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

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

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)


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 fro开发者_StackOverflowm his laptop)


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]


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.

    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.


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


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%.

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.

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.

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.

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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜