Windows 11下WS_EX_TOOLWINDOW窗口无法接收WM_DPICHANGED消息问题
WPF工具窗口(WS_EX_TOOLWINDOW)在Win11下不响应WM_DPICHANGED的解决方案
问题说明
不少WPF开发者都遇到过这个问题:设置了WS_EX_TOOLWINDOW扩展样式的窗口,在Windows 11中无法接收WM_DPICHANGED消息、不会自动缩放,但Windows 10中正常工作。这属于Windows 11的行为变更,并非系统bug——微软调整了工具窗口的DPI处理逻辑,默认不再触发自动DPI适配。
解决方案
要保留WS_EX_TOOLWINDOW样式(不在ALT+TAB列表中显示)同时让窗口响应DPI变化,推荐以下两种方案:
方案1:添加WS_EX_NOREDIRECTIONBITMAP扩展样式(推荐)
在设置WS_EX_TOOLWINDOW时,额外添加WS_EX_NOREDIRECTIONBITMAP样式,强制系统向窗口发送WM_DPICHANGED消息,触发WPF原生的DPI缩放。
修改后的工具窗口样式设置代码:
public static int SetToolWindow(IntPtr hwnd) { const int GWL_EX_STYLE = -20; const int WS_EX_TOOLWINDOW = 0x00000080; const int WS_EX_APPWINDOW = 0x00040000; const int WS_EX_NOREDIRECTIONBITMAP = 0x00200000; var extendedStyle = GetWindowLong(hwnd, GWL_EX_STYLE); // 同时启用WS_EX_TOOLWINDOW和WS_EX_NOREDIRECTIONBITMAP,移除WS_EX_APPWINDOW extendedStyle = (extendedStyle | WS_EX_TOOLWINDOW | WS_EX_NOREDIRECTIONBITMAP) & ~WS_EX_APPWINDOW; return SetWindowLong(hwnd, GWL_EX_STYLE, extendedStyle); } [DllImport("user32.dll", SetLastError = true)] private static extern int GetWindowLong(IntPtr hWnd, int nIndex); [DllImport("user32.dll", SetLastError = true)] private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
该方案无需手动处理缩放逻辑,完全利用WPF原生的PMv2级DPI感知能力,效果最稳定。
方案2:手动监听DPI变化并触发缩放
如果方案1不适用,可通过监听系统显示设置变化事件,手动获取当前DPI并调整窗口缩放:
private void Window_SourceInitialized(object sender, EventArgs e) { // 注册显示设置变化事件 SystemEvents.DisplaySettingsChanged += OnDisplaySettingsChanged; } private void OnDisplaySettingsChanged(object? sender, EventArgs e) { var hwnd = new WindowInteropHelper(this).Handle; uint currentDpi = GetDpiForWindow(hwnd); double scaleFactor = currentDpi / 96.0; this.Dispatcher.Invoke(() => { // 更新WPF的设备转换矩阵 var presentationSource = PresentationSource.FromVisual(this); if (presentationSource != null) { Matrix transformMatrix = Matrix.Identity; transformMatrix.Scale(scaleFactor, scaleFactor); presentationSource.CompositionTarget.TransformToDevice = transformMatrix; } // 手动调整窗口大小(可选,根据布局需求) this.Width *= scaleFactor; this.Height *= scaleFactor; }); } [DllImport("user32.dll")] private static extern uint GetDpiForWindow(IntPtr hwnd);
注意:手动缩放可能需要额外调整控件布局,避免出现错位或排版问题。
前置要求
确保应用已正确启用PMv2级DPI感知,在app.manifest中添加如下配置:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness> </windowsSettings> </application>
内容的提问来源于stack exchange,提问作者ZHANG Luyao
相关产品推荐
相关产品推荐

