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

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

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


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

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]

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

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

    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.

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

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

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

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

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

Другие вопросы по тегам