Как я могу проверить, что окно полностью видно на экране пользователя?
Есть ли способ проверить, что 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))