WPF PerMonitorV2 下的跨窗口问题

WPF PerMonitorV2 下的跨窗口问题

一、问题背景

本人在开发素材管理姬的时候,遇到了个需求:截图后弹出的贴屏窗口,位置和尺寸都要与框选的屏幕区域严丝合缝。单屏幕下一切正常;但当框选区域落在 DPI 不同的另一块显示器上时,位置和尺寸都出现偏差。

WPF 默认只做系统级 (System)DPI 感知:整套 UI 按主屏 DPI 缩放,拖到 DPI 不同的屏上会被位图拉伸、变模糊。为了让每块屏都清晰,本项目在清单里显式声明了 PerMonitorV2 感知。

复现环境:主屏 DPI = 1.25、副屏 DPI = 1.0,框选区域在副屏。

联网查证后确认,这是 WPF 的一个长期缺陷(官方 issue #4127 / #3105)。

二、分析:

Window.Left/Top 的单位是 DIP (设备无关像素)。WPF 在 DIP ↔ 物理像素之间换算时,采用的是窗口当前所在显示器的 DPI。窗口跨屏、DPI 变化会触发 WM_DPICHANGED,WPF 用新 DPI 把坐标重新换算一遍,因此坐标跳变(被 ×/÷ DPI)。

由此可见,在窗口生命周期的哪个时机设置 Left/Top,决定了它会不会、以及怎样被 DPI 换算。基于 Stylet 的 MVVM 框架,Screen 类中有三个可选时机来写入 Left/Top 绑定,三者表现截然不同。

三、三种情况对照

WPF 窗口事件顺序:构造函数 → Initialized → SourceInitialized → Activated → Loaded 。在其中不同时机赋值 Left/Top

情况 赋值时机 HWND / DPI 状态 结果
OnInitialActivate SourceInitialized 之前 HWND 还没建 值被缓存,窗口创建时原样落位,未经跨屏 DPI 换算 ------ 不正确
OnViewLoaded DPI 尚未稳定的中途(跨屏 WM_DPICHANGED 仍在途中) HWND 已建,DPI 仍停在主屏 1.25 赋值不立即刷新 UI;之后窗口真正跨屏时,LeftTop一次性、同时 重新换算 ------ 正确
Execute.PostToUIThread SourceInitialized 之后、DPI 已稳定在主屏 1.25,且同步按序跑在 UI 线程 HWND 已建,DPI 已稳定 Left立即 触发跨屏、切换 DPI,Left 被正确换算;随后设的 Top 已在新 DPI 下、不再被换算 ------ 只有 Left 正确、Top 错误
相关推荐
闲云自留地2 小时前
云平台存储管理员:Cinder 创建挂载卷 + Swift 分布式存储原理
分布式·wpf·swift
XiaoZhenHua985 小时前
C#工业机器视觉Recipe配方系统设计:多型号、多相机、多工位参数如何统一管理
c#·软件架构·机器视觉·工业自动化·工业软件·recipe
fujisheng6616 小时前
FUI 导航实践:拆开 Layer、History、Coverage 与 Cache 的组合语义
c#·unity3d
淡海水8 小时前
10-05-高级-模式匹配-CSharp如何改变与数据结构的交互方式
数据结构·算法·c#·模式匹配
XiaoZhenHua988 小时前
C# WinForms + 西门子S7 + SQLite + EPPlus生产数据采集系统架构设计
系统架构·sqlite·c#
Lost of 程序猿9 小时前
船岸数据同步链路实战:SendFlag、单调版本号与断网三天后的续传
后端·c#·asp.net
敲代码的嘎仔1 天前
从集群锁失效到异步领券:我用分布式锁 + Redisson + MQ 把领券接口 RT 从 200ms 压到 15ms
java·数据库·redis·分布式·缓存·ai·wpf
cjp5601 天前
012.UG二次开发,UG管道服务器与WPF管道通讯,传输JSON数据
c#
jiushidt1 天前
ArcGIS Pro Add‑in 打包流程详解
arcgis·c#·.net·arcgispro
闲云自留地1 天前
OpenStack 的身份管家:Keystone 令牌、RBAC 权限模型拆解
wpf·openstack