I need to know the position of a window that is maximized.
WPF Window has Top and Left properties that specifies the window\'s location. However, if you maximize the
I realize that you are working in WPF, and this answer makes use of Forms technology, but it should work without much difficulty.
You can get a collection of the screens through My.Settings.Screens.AllScreens. From there you can access the resolution that the screen is currently working on.
Since WPF windows retain the Top/Left values that they had when they were maximized, then you can determine which screen they are on by figuring out which screen that Top/Left coordinates refer to, and then get the top/left coordinate for that screen.
Unfortunately, I am on the road, and can't test this at the moment. If you do implement, I would love to see what you come up with.
Here's the solution I came up with based on previous discussion here (thanks!).
This solution...
The main method GetAbsolutePosition
is implemented here as an extension method. If you have a Window
called myWindow
, call it like this:
Point p = myWindow.GetAbsolutePosition();
Here's the complete code:
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);
}
}
I finally found a solution working for me:
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;
}
Universal one line answer working for all monitors and all high DPI variants:
Point leftTop = this.PointToScreen(new Point(0, 0));
This for example returns (-8, -8) for a window which is maximized on a 1920 wide screen whose ActualWidth
returns 1936.
Just a rant to the other answers: NEVER mix up logical 96 dpi WPF pixels (using double
) with native real pixels (using int
) - especially by just casting double to int!
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);
}
}
This seems to be a problem with System.Windows.Window!!!
Maximized window give unreliable values for Left, Width, ActualWidth, Top, Height and ActualHeight.
After maximizing a window, it can often keep the Left and Width values from the pre-maximized window.
For others reading - there is no problem when the window is un-maximized.
Also, what I find odd is that the values you read are in WPF DPI coordinates, [i.e. 1936x1096, from (-8, -8) to (1928, 1088)], but when you set these values you have to use screen pixel coordinates, [i.e. 1920x1080, using (0,0) etc...]
@tgr provided a reliable partial solution above, which I've improved below:
Here's the C# solution:
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);
}
}