【发布时间】:2011-06-15 22:04:17
【问题描述】:
我需要知道最大化的窗口的位置。
WPF Window 具有指定窗口位置的 Top 和 Left 属性。但是,如果您最大化窗口,这些属性会使窗口的值保持在正常状态。
如果您在单屏设置上运行,则最大化位置自然是 (0,0)。但是,如果您有多个屏幕,则不一定正确。只有在主屏幕上最大化时,窗口才会有位置 (0,0)。
那么...有没有办法找出最大化窗口的位置(最好与 Top 和 Left 属性在相同的逻辑单元中)?
【问题讨论】:
我需要知道最大化的窗口的位置。
WPF Window 具有指定窗口位置的 Top 和 Left 属性。但是,如果您最大化窗口,这些属性会使窗口的值保持在正常状态。
如果您在单屏设置上运行,则最大化位置自然是 (0,0)。但是,如果您有多个屏幕,则不一定正确。只有在主屏幕上最大化时,窗口才会有位置 (0,0)。
那么...有没有办法找出最大化窗口的位置(最好与 Top 和 Left 属性在相同的逻辑单元中)?
【问题讨论】:
这是我根据之前的讨论提出的解决方案(谢谢!)。
这个解决方案...
main方法GetAbsolutePosition在这里实现为扩展方法。如果您有一个名为 myWindow 的 Window,请这样称呼它:
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);
}
}
【讨论】:
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);
}
}
【讨论】:
我终于找到了适合我的解决方案:
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;
}
【讨论】:
适用于所有显示器和所有高 DPI 变体的通用单线答案:
Point leftTop = this.PointToScreen(new Point(0, 0));
例如,对于在 1920 宽屏幕上最大化的窗口返回 (-8, -8),其 ActualWidth 返回 1936。
只是对其他答案的咆哮:永远不要将逻辑 96 dpi WPF 像素(使用 double)与本机真实像素(使用 int)混合 - 特别是通过将 double 转换为 int!
【讨论】:
我知道您在 WPF 中工作,并且此答案使用了 Forms 技术,但应该没有太大困难。
您可以通过 My.Settings.Screens.AllScreens 获取屏幕集合。从那里您可以访问屏幕当前工作的分辨率。
由于 WPF 窗口保留了它们最大化时的 Top/Left 值,因此您可以通过确定 Top/Left 坐标所指的屏幕来确定它们所在的屏幕,然后获取 top/left 坐标对于那个屏幕。
很遗憾,我正在路上,目前无法测试这个。如果您确实实施,我很想看看您的想法。
【讨论】:
这似乎是 System.Windows.Window 的问题!!!
最大化窗口为 Left、Width、ActualWidth、Top、Height 和 ActualHeight 提供不可靠的值。
最大化一个窗口后,它通常可以保留预先最大化窗口的 Left 和 Width 值。
对于其他人阅读 - 窗口未最大化时没有问题。
另外,我觉得奇怪的是您读取的值在 WPF DPI 坐标中,[即1936x1096,从 (-8, -8) 到 (1928, 1088)],但是当您设置这些值时,您必须使用屏幕像素坐标,[即1920x1080,使用 (0,0) 等...]
@tgr 在上面提供了一个可靠的部分解决方案,我在下面进行了改进:
这是 C# 解决方案:
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);
}
}
【讨论】:
我还没有找到解决您问题的方法,但如果您需要定位窗口只是为了创建一个新窗口,您可以执行以下操作:
...
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;
}
}
【讨论】: