Как найти положение максимального окна?
Мне нужно знать положение максимизированного окна.
В окне WPF есть свойства Top и Left, которые определяют расположение окна. Однако, если вы максимизируете окно, эти свойства сохраняют в нем значения нормального состояния.
Если вы используете одноэкранную настройку, максимизированное положение естественно (0,0). Однако, если у вас несколько экранов, это необязательно. Окно будет иметь только положение (0,0), если оно будет максимизировано на главном экране.
Итак... есть ли способ узнать положение максимизированного окна (желательно в тех же логических единицах, что и свойства Top и Left)?
Ответы
Ответ 1
Здесь решение, которое я придумал, основывалось на предыдущем обсуждении здесь (спасибо!).
Это решение...
- возвращает положение окна в текущем состоянии
- обрабатывает все состояния окон (максимизируется, минимизируется, восстанавливается)
- не зависит от Windows Forms (но вдохновлен им)
- использует дескриптор окна для надежного определения правильного монитора
Основной метод GetAbsolutePosition
реализуется здесь как метод расширения. Если у вас есть Window
, называемый myWindow
, назовите его следующим образом:
Point p = myWindow.GetAbsolutePosition();
Здесь полный код:
using System;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;
static class OSInterop
{
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int smIndex);
public const int SM_CMONITORS = 80;
[DllImport("user32.dll")]
public static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info);
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromWindow(HandleRef handle, int flags);
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public int width { get { return right - left; } }
public int height { get { return bottom - top; } }
}
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
public class MONITORINFOEX
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
public RECT rcMonitor = new RECT();
public RECT rcWork = new RECT();
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szDevice = new char[32];
public int dwFlags;
}
}
static class WPFExtensionMethods
{
public static Point GetAbsolutePosition(this Window w)
{
if (w.WindowState != WindowState.Maximized)
return new Point(w.Left, w.Top);
Int32Rect r;
bool multimonSupported = OSInterop.GetSystemMetrics(OSInterop.SM_CMONITORS) != 0;
if (!multimonSupported)
{
OSInterop.RECT rc = new OSInterop.RECT();
OSInterop.SystemParametersInfo(48, 0, ref rc, 0);
r = new Int32Rect(rc.left, rc.top, rc.width, rc.height);
}
else
{
WindowInteropHelper helper = new WindowInteropHelper(w);
IntPtr hmonitor = OSInterop.MonitorFromWindow(new HandleRef((object)null, helper.EnsureHandle()), 2);
OSInterop.MONITORINFOEX info = new OSInterop.MONITORINFOEX();
OSInterop.GetMonitorInfo(new HandleRef((object)null, hmonitor), info);
r = new Int32Rect(info.rcWork.left, info.rcWork.top, info.rcWork.width, info.rcWork.height);
}
return new Point(r.X, r.Y);
}
}
Ответ 2
Наконец-то я нашел решение для меня:
private System.Drawing.Rectangle getWindowRectangle()
{
System.Drawing.Rectangle windowRectangle;
if (this.WindowState == System.Windows.WindowState.Maximized)
{
/* Here is the magic:
* Use Winforms code to find the Available space on the
* screen that contained the window
* just before it was maximized
* (Left, Top have their values from Normal WindowState)
*/
windowRectangle = System.Windows.Forms.Screen.GetWorkingArea(
new System.Drawing.Point((int)this.Left, (int)this.Top));
}
else
{
windowRectangle = new System.Drawing.Rectangle(
(int)this.Left, (int)this.Top,
(int)this.ActualWidth, (int)this.ActualHeight);
}
return windowRectangle;
}
Ответ 3
public static System.Drawing.Rectangle GetWindowRectangle(this Window w)
{
if (w.WindowState == WindowState.Maximized) {
var handle = new System.Windows.Interop.WindowInteropHelper(w).Handle;
var screen = System.Windows.Forms.Screen.FromHandle(handle);
return screen.WorkingArea;
}
else {
return new System.Drawing.Rectangle(
(int)w.Left, (int)w.Top,
(int)w.ActualWidth, (int)w.ActualHeight);
}
}
Ответ 4
Универсальный однострочный ответ для всех мониторов и всех вариантов с высоким разрешением DPI:
Point leftTop = this.PointToScreen(new Point(0, 0));
Это, например, возвращает (-8, -8) для окна, которое максимизируется на широкоэкранном экране 1920 года, чей ActualWidth
возвращает 1936.
Просто скажите другим ответам: НИКОГДА не смешивайте логические пиксели WPF с 96 точек на дюйм (используя double
) с собственными реальными пикселями (используя int
) - особенно, просто используя двойное преобразование в int!
Ответ 5
Я понимаю, что вы работаете в WPF, и этот ответ использует технологию Forms, но он должен работать без особых трудностей.
Вы можете получить коллекцию экранов через My.Settings.Screens.AllScreens. Оттуда вы можете получить доступ к разрешению, на котором в данный момент работает экран.
Так как окна WPF сохраняют значения Top/Left, которые у них были, когда они были максимальны, вы можете определить, на каком экране они находятся, выясняя, на каком экране находятся координаты Top/Left, а затем получить верхнюю/левую координату для этого экрана.
К сожалению, я нахожусь в дороге и не могу проверить это на данный момент. Если вы это сделаете, я бы с удовольствием посмотрел, что вы придумали.
Ответ 6
Это похоже на проблему с System.Windows.Window !!!
Развернутое окно дает ненадежные значения для Left, Width, ActualWidth, Top, Height и ActualHeight.
После максимизации окна оно часто может сохранять значения Left и Width из предварительно максимизированного окна.
Для других читателей - нет проблем, когда окно не развернуто.
Кроме того, я нахожу странным то, что значения, которые вы читаете, находятся в координатах WPF DPI, [то есть 1936x1096, от (-8, -8) до (1928, 1088)], но когда вы устанавливаете эти значения, вы должны использовать координаты пикселя на экране, [то есть 1920x1080, используя (0,0) и т.д.]
@tgr предоставил надежное частичное решение выше, которое я улучшил ниже:
- Исправление intellisense для методов расширения путем перемещения вспомогательного класса в подкласс
- создание метода GetAbsoluteRect() для обеспечения ширины/высоты и указания всех в одном вызове
- рефакторинг общего кода
Вот решение С#:
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
public static partial class Extensions
{
static class OSInterop
{
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int smIndex);
public const int SM_CMONITORS = 80;
[DllImport("user32.dll")]
public static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info);
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromWindow(HandleRef handle, int flags);
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public int width { get { return right - left; } }
public int height { get { return bottom - top; } }
}
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
public class MONITORINFOEX
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
public RECT rcMonitor = new RECT();
public RECT rcWork = new RECT();
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public char[] szDevice = new char[32];
public int dwFlags;
}
}
static Int32Rect _getOsInteropRect(Window w)
{
bool multimonSupported = OSInterop.GetSystemMetrics(OSInterop.SM_CMONITORS) != 0;
if (!multimonSupported)
{
OSInterop.RECT rc = new OSInterop.RECT();
OSInterop.SystemParametersInfo(48, 0, ref rc, 0);
return new Int32Rect(rc.left, rc.top, rc.width, rc.height);
}
WindowInteropHelper helper = new WindowInteropHelper(w);
IntPtr hmonitor = OSInterop.MonitorFromWindow(new HandleRef((object)null, helper.EnsureHandle()), 2);
OSInterop.MONITORINFOEX info = new OSInterop.MONITORINFOEX();
OSInterop.GetMonitorInfo(new HandleRef((object)null, hmonitor), info);
return new Int32Rect(info.rcWork.left, info.rcWork.top, info.rcWork.width, info.rcWork.height);
}
public static Rect GetAbsoluteRect(this Window w)
{
if (w.WindowState != WindowState.Maximized)
return new Rect(w.Left, w.Top, w.ActualWidth, w.ActualHeight);
var r = _getOsInteropRect(w);
return new Rect(r.X, r.Y, r.Width, r.Height);
}
public static Point GetAbsolutePosition(this Window w)
{
if (w.WindowState != WindowState.Maximized)
return new Point(w.Left, w.Top);
var r = _getOsInteropRect(w);
return new Point(r.X, r.Y);
}
}
Ответ 7
Я не нашел решения вашей проблемы, но если вам нужно расположить окно просто для создания нового, вы можете сделать следующее:
...
Window windowNew = new Window();
ConfigureWindow(this, windowNew);
Window.Show();
...
static public void ConfigureWindow(Window windowOld, Window windowNew)
{
windowNew.Height = windowOld.ActualHeight;
windowNew.Width = windowOld.ActualWidth;
if (windowOld.WindowState == WindowState.Maximized)
{
windowNew.WindowState = WindowState.Maximized;
}
else
{
windowNew.Top = windowOld.Top;
windowNew.Left = windowOld.Left;
}
}