Aquí está la solución que se me ocurrió sobre la base de la discusión anterior aquí.
Esta solución ...
- devuelve la posición de una ventana en su estado actual
- maneja todos los estados de ventana (maximizada, minimizada, restaurado)
- no depende de las formas de Windows (pero está inspirado por ella)
- utiliza el identificador de ventana para determinar de manera fiable el monitor correcto
el met principal od GetAbsolutePosition
se implementa aquí como un método de extensión. Si usted tiene un Window
llamada myWindow
, lo llaman así:
Point p = myWindow.GetAbsolutePosition();
Aquí está el código completo:
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);
}
}
Creo que si cambia la llamada GetWorkingArea a: windowRectangle = System.Windows.Forms.Screen.GetWorkingArea (new System.Drawing.Point ((int) window.Left + (int) (window.ActualWidth/2), (int) window.Top + (int) (window.ActualHeight/2))); eso lo arreglará Necesita obtener el centro de la ventana, no el topleft, porque ese es el punto que WPF usa para determinar a qué ventana se debe maximizar, si la ventana se encuentra en más de un monitor. – Matt
buena idea, pero ¿esto realmente ayuda? esta rama de la cláusula if solo se usa cuando se maximiza WindowState, entonces ActualWith y -Height serán sobre el tamaño de la pantalla, no sobre el windowsize (restaurado) – eFloh
Sí, entiendo lo que dices. ¿se puede obtener el tamaño de la ventana restaurada de RestoreBounds y luego usar eso en vez de ActualWidth/ActualHeight tal vez? – Matt