WPF切换主显示器后Screen类识别真实分辨率错误如何解决
问题原因
WinForms 内置的 Screen 类会对屏幕参数做内部缓存,系统显示设置(包括主显示器切换、分辨率、缩放比例变更)发生修改时,Screen 类不会主动刷新缓存,因此读取到的 Bounds 属性还是旧的数值,只有重启应用才会重新读取最新的显示参数。
解决方法
直接调用 Win32 API 绕过 Screen 类的缓存,读取系统当前生效的真实显示参数,无需重启应用。
完整代码实现
using System; using System.Runtime.InteropServices; using System.Windows.Forms; public static class DisplayInfoHelper { [DllImport("user32.dll", CharSet = CharSet.Ansi)] private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] private struct DEVMODE { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; public short dmSpecVersion; public short dmDriverVersion; public short dmSize; public short dmDriverExtra; public int dmFields; public int dmPositionX; public int dmPositionY; public int dmDisplayOrientation; public int dmDisplayFixedOutput; public short dmColor; public short dmDuplex; public short dmYResolution; public short dmTTOption; public short dmCollate; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] public string dmFormName; public short dmLogPixels; public int dmBitsPerPel; public int dmPelsWidth; public int dmPelsHeight; public int dmDisplayFlags; public int dmDisplayFrequency; public int dmICMMethod; public int dmICMIntent; public int dmMediaType; public int dmDitherType; public int dmReserved1; public int dmReserved2; public int dmPanningWidth; public int dmPanningHeight; } /// <summary> /// 获取指定显示器的真实物理分辨率(未经过缩放的原始分辨率) /// </summary> /// <param name="deviceName">显示器设备名,可从 Screen.DeviceName 属性获取</param> /// <returns>真实宽度、高度的元组</returns> public static (int PhysicalWidth, int PhysicalHeight) GetScreenRealResolution(string deviceName) { const int ENUM_CURRENT_SETTINGS = -1; DEVMODE devMode = new DEVMODE(); devMode.dmSize = (short)Marshal.SizeOf(devMode); if (EnumDisplaySettings(deviceName, ENUM_CURRENT_SETTINGS, ref devMode)) { return (devMode.dmPelsWidth, devMode.dmPelsHeight); } return (0, 0); } }
调用示例
你已经可以通过 Screen.PrimaryScreen.DeviceName 正确拿到当前主显示器的设备名,直接传入方法即可:
// 切换主显示器后直接调用即可拿到正确的真实分辨率 var primaryDeviceName = Screen.PrimaryScreen.DeviceName; var (realWidth, realHeight) = DisplayInfoHelper.GetScreenRealResolution(primaryDeviceName);
额外优化
可以订阅系统显示设置变更事件,在主显示器切换、分辨率修改时自动更新分辨率数值,无需主动轮询:
Microsoft.Win32.SystemEvents.DisplaySettingsChanged += (sender, args) => { // 显示设置变更后重新读取主显示器分辨率即可 var primaryDeviceName = Screen.PrimaryScreen.DeviceName; var (realWidth, realHeight) = DisplayInfoHelper.GetScreenRealResolution(primaryDeviceName); // 这里处理后续的业务逻辑 };
注意事项
请确保项目已开启 DPI 感知,可在项目的 app.manifest 中取消以下配置的注释:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings> </application>
内容的提问来源于stack exchange,提问作者steam3d
相关产品推荐
相关产品推荐

