WPF Per Monitor DPI 完整适配指南:3 步解决多显示器缩放模糊
2026/8/24 4:07:21 网站建设 项目流程

WPF Per Monitor DPI 完整适配指南:3 步解决多显示器缩放模糊

【免费下载链接】WPF-SamplesRepository for WPF related samples项目地址: https://gitcode.com/gh_mirrors/wp/WPF-Samples

本文带你过一遍 WPF-Samples 的 WPF Per Monitor DPI 适配流程:app.manifest 开 dpiAwareness、App.Config 配开关、接 DpiChanged 事件、备好多尺寸图片。照做就能修掉窗口跨屏后文字发虚、图片变糊的老毛病 ✅

问题场景:多屏 DPI 不一致,界面为什么会糊

两块显示器,左 100%、右 150% 缩放。窗口拖到右边那块,图标发虚、文字毛糙。

原因:WPF 默认只有 System DPI 感知——窗口按主屏缩放整体绘制,跨到别的屏幕时,系统只能拉伸或压缩整张位图。拉大就糊,压缩就浪费分辨率。

原理速览:Per Monitor DPI 到底是什么

Per Monitor DPI(每显示器 DPI 感知)随 .NET 4.6.2 和 Windows 10 周年更新引入。意思很直白:系统把窗口所在屏幕的真实缩放比例告诉应用,应用按这个比例自己重绘内容。

启用后窗口跨屏会自动切到目标缩放下原生渲染,不再拉伸位图。纯 WPF 内容配好两个文件即可;图片、宿主控件、底层文本 API 还要补几行代码。

上手三步:配置 dpiAwareness 并跑通跨屏验证

第 1 步:app.manifest 开启 PerMonitor 感知 ✅

项目里新建 app.manifest(右键项目 -> 添加 -> 新建项 -> 应用程序清单文件),补上这两个标签:新标签在 Windows 10 周年更新及以上生效,老标签给旧系统兜底。

<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings> </application>

第 2 步:App.Config 加 WPF 侧开关 ✅

目标框架低于 .NET 4.6.2 才需要这一步,加在 App.Config 的 configuration 节里:

<runtime> <AppContextSwitchOverrides value="Switch.System.Windows.DoNotScaleForDpiChanges=false"/> </runtime>

两个坑:它叫 DoNotScale 却要设成 false,双重否定才算开;AppContextSwitchOverrides 一个文件只能出现一次,已有就分号分隔追加。

第 3 步:拖窗口做第一次跨屏验证 🚀

编译运行,把窗口在两块不同缩放的显示器间来回拖。文字锐利、控件自动跟随新屏缩放,基础配置就通了。

运行时响应:窗口跨屏时监听 DpiChanged 事件

布局会自动跟随缩放,但换图片、重缩放宿主控件得你自己接事件 ⚡ 速查表如下:

API什么时候用
Window.DpiChanged窗口移到不同缩放显示器时触发;作为整窗内容重排或重渲染的主入口
Image.DpiChanged路由事件,窗口内每个 Image 都收到;换入对应缩放图片,可在 XAML 根上集中监听
WindowsFormsHost.DpiChanged每个宿主控件都会触发;算出新旧缩放比后对子控件调Control.Scale
VisualTreeHelper.GetDpi(visual)静态方法返回DpiScale;随时查某个视觉当前的渲染 DPI
VisualTreeHelper.SetRootDpi(visual, dpi)仅无父级的根视觉可调;RenderTargetBitmap渲染树外视觉前声明目标 DPI

查当前 DPI 一行搞定:

DpiScale dpi = VisualTreeHelper.GetDpi(myImage);

WinForms 宿主的完整做法参考 PerMonitorDPI/WinFormsHost/ 示例:在 DpiChanged 里算 NewDpi 与 OldDpi 的 PixelsPerDip 之比,传给托管控件的 Scale,字体同步放大。

资源准备:图片按 100/200/400 三档备齐 🖼️

同一张图备 100%、200%、400% 三档,命名统一成 scale-100.png 这种格式。运行时读当前 PixelsPerDip,取最接近且不小于它的那档加载——微软的建议是宁缩勿放,所以留三档而不是两档。

PerMonitorDPI/ImageScaling/ 示例做了个 DpiAwareImage 控件:首次加载用 GetDpi 选图,重写 OnDpiChanged 后每次缩放变化再换一次 Source,思路可以直接抄。

文本侧,用 FormattedText、TextSource 这类 API 自绘文字时,要把目标 DPI 经新增的 PixelsPerDip 传进去,DPI 变化时强制重排重绘,对照 PerMonitorDPI/TextFormatting/ 示例即可。

验收清单:测试环境与自检查项

测试环境要求:

  1. Windows 10 周年更新或更高版本
  2. 两块不同缩放的显示器(如 100% 与 150%)

自检查项:

  • 拖窗跨屏:目标缩放下文字锐利,无整体拉伸感
  • 含图片:跨屏后自动换到对应 scale 资源
  • 含 WinForms 宿主:跨屏后子控件尺寸、字体按比例跟随
  • 用 RenderTargetBitmap 的:渲染前调用 SetRootDpi 声明目标 DPI
  • App.Config 里 AppContextSwitchOverrides 仅出现一次,多开关分号分隔
  • 用到新 API 的项目已把目标框架升到 .NET 4.6.2

已知限制:WinForms 应用经 ElementHost 托管 WPF、Win32 窗口经 HwndSource 托管 WPF,均不支持 Per Monitor DPI——WM_DPICHANGED 只发给顶层窗口,WPF 收不到。

🎯 想动手,直接打开 PerMonitorDPI/ 下的四个示例:FormattedTextExample、ImageScaling、TextFormatting、WinFormsHost,各对应本文一个场景。边跑边拖屏,验证最快。

【免费下载链接】WPF-SamplesRepository for WPF related samples项目地址: https://gitcode.com/gh_mirrors/wp/WPF-Samples

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询