Как я могу проверить, что окно полностью видно на экране пользователя?

Есть ли способ проверить, что WinForm полностью виден на экране (например, не выходит за пределы экрана?)

Я пробовал использовать для этого SystemInformation.VirtualScreen, который отлично работает, пока виртуальный экран представляет собой прямоугольник, но как только он не является (например, 3 экрана в форме буквы L), SystemInformation.VirtualScreen возвращает наименьший прямоугольник, содержащий все видимые пиксели (поэтому окно в правом верхнем углу буквы L не будет видно, хотя оно находится на виртуальном экране)


Причина, по которой я пытаюсь добиться этого, заключается в том, что я хотел бы, чтобы моя программа открывала свои дочерние окна в последнем местоположении, в котором они были, но я не хочу, чтобы эти окна были вне поля зрения, если пользовательские изменения настроены ( например отключает лишний экран от своего ноутбука)


person Brann    schedule 06.07.2011    source источник


Ответы (5)


Вот как я в итоге это сделал:

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]
person Brann    schedule 18.07.2011
comment
Мне нравится это решение. Хотя обратите внимание, что это должно быть p.X < s.Bounds.Right. - person Anders Carstensen; 09.05.2013
comment
можно упростить до if(s.Bounds.Contains(p)) И, возможно, s.WorkingArea может быть лучше, когда речь идет о нескольких мониторах, границы не согласованы... - person joe; 14.12.2020

Вот как бы я это сделал:

Это переместит элемент управления (форму) внутри границ дисплея как можно ближе к исходному местоположению.

    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.

person deepee1    schedule 06.07.2011
comment
Что произойдет, если у вас будет несколько мониторов? - person Sam; 18.04.2012
comment
@Sam из документации Microsoft для Screen.GetWorkingArea по адресу msdn.microsoft.com/en -us/library/45zswy9x.aspx в нем указано: Извлекает рабочую область для дисплея, содержащую наибольшую область указанного элемента управления. в основном переместите элемент управления так, чтобы он был полностью внутри экрана, он в основном находится внутри. - person deepee1; 18.04.2012
comment
Казалось бы, первые три строки ничего не делают и их можно просто удалить. - person GSerg; 12.12.2019

Я пытался сделать то же самое, чтобы определить, открылось ли окно за пределами экрана, а затем, соответственно, переместить его в ближайшее место, где оно было ранее найдено. Я просматриваю весь Интернет, и из всех предложенных людьми решений ничего не работает.

Поэтому я взял на себя задачу создать свой собственный класс, который делает именно это и работает на 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 должно быть простым, если вы знаете, как это сделать, Изменить окно на форму и FromHandle(Form.Handle) должны работать.

person Joey Johnson    schedule 19.02.2015
comment
Мне нравится это решение. Поскольку оно статично, его было очень легко обернуть присоединенным свойством. - person gregsdennis; 29.09.2015
comment
Хорошо работает, спасибо. Однако простую математику можно упростить еще больше: if (!leftSideTest) window.Left = screen.WorkingArea.Left; если (!topSideTest) window.Top = screen.WorkingArea.Top; if (!bottomSideTest) window.Top = screen.WorkingArea.Bottom - sizeY; if (!rightSideTest) window.Left = screen.WorkingArea.Right - sizeX; - person Jan Zeman; 04.06.2020

Это мое решение. Решает проблему "дырок".

    /// <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;
    }
person SirTechington    schedule 23.04.2015

Проверьте, Screen.AllScreens.Any(s => s.WorkingArea.Contains(rect))

person SLaks    schedule 06.07.2011
comment
Этот метод является хорошим началом, но если у вас несколько мониторов и экран разделен между ними обоими, вы получите ложное срабатывание. - person deepee1; 06.07.2011