Graphics.DpiX/DpiY仅返回主显示器DPI,如何获取当前扩展显示器DPI?
问题原因
你遇到的问题不是接口本身的缺陷,根源如下:
- WinForm项目默认未开启Per-Monitor DPI感知模式,系统会对非感知模式的程序做DPI虚拟化,统一返回主显示器的DPI值,屏蔽不同显示器的DPI差异
CreateGraphics()返回的Graphics对象默认关联主显示器的渲染上下文,不会随窗口所在显示器变化自动切换
解决方案
第一步:开启Per-Monitor DPI感知
.NET 6+ WinForm项目可直接在Program.cs入口添加配置:
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
低版本.NET项目可修改app.manifest文件,添加如下配置:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware> </windowsSettings> </application>
第二步:获取当前窗口所在显示器DPI
方案1:适配Win10 1607及以上版本(推荐)
先声明Win32 API,直接调用GetDpiForWindow获取窗口对应DPI:
// 导入API声明 [DllImport("user32.dll")] static extern uint GetDpiForWindow(IntPtr hWnd); // 调用示例 private void getDPIButton1_Click(object sender, EventArgs e) { uint currentDpi = GetDpiForWindow(this.Handle); // X、Y方向DPI通常一致,直接使用currentDpi即可 }
方案2:兼容Win7及以上版本
先获取窗口所在显示器的句柄,再查询对应显示器的DPI:
// 导入API声明 [DllImport("user32.dll")] static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); const uint MONITOR_DEFAULTTONEAREST = 0x00000002; [DllImport("shcore.dll")] static extern int GetDpiForMonitor(IntPtr hmonitor, int dpiType, out uint dpiX, out uint dpiY); const int MDT_EFFECTIVE_DPI = 0; // 调用示例 private void getDPIButton1_Click(object sender, EventArgs e) { IntPtr hMonitor = MonitorFromWindow(this.Handle, MONITOR_DEFAULTTONEAREST); GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, out uint dpiX, out uint dpiY); // dpiX、dpiY分别为当前显示器的水平、垂直DPI }
额外提示
开启Per-Monitor DPI感知后,窗口跨显示器移动时会收到WM_DPICHANGED系统消息,你可以重写窗口的WndProc方法监听该消息,自动更换对应DPI的图片资源,无需手动触发查询刷新。
内容的提问来源于stack exchange,提问作者Mohanram Anbukkarasu
相关产品推荐
相关产品推荐

