很遗憾,用户3225503的答案似乎不起作用了(还是说不再起作用了?)
我的情况:WIN10 20H2,WPF-App具有dpi感知“PerMonitor”,框架4.7.2,2个分辨率和不同屏幕缩放比例的显示器(“恐怖场景”):
DEVMODE结构的dm.dmPelsWidth成员始终具有我的监视器的物理分辨率,因此缩放始终为1.0。
我们想要的就是像上次会话一样恢复我们的程序及其窗口,对吧?这似乎非常困难,多亏了微软!
但是,不同的方法似乎有效:
在应用程序的清单文件中开启每个监视器的 DPI 意识:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- 下面两个标签的组合具有以下效果:
1)大于等于 Windows 10 周年更新版本的每个监视器
2)小于 Windows 10 周年更新版本的系统 -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
始终使用 GetPlacement 和 SetPlacement Win32 API 调用来存储/恢复窗口位置。
如果对话框位于辅助显示器上,并且每个显示器具有不同的缩放比例,则 SetPlacement 将设置错误的对话框宽度/高度。因此,我们需要一个新的因子,该因子取决于每个显示器的缩放因子,在窗口的 Loading 事件中进行更正:
事件代码:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
- 代码示例中还有一些好东西,例如WINDOWPLACEMENT结构的序列化。不要忘记在应用程序设置中创建一个名为“Placement”的成员!如果这对您有用,请告诉我:
示例代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}