多DPI显示器下WPF全屏窗口跨显示器显示问题求助
解决WPF双显示器不同DPI下全屏窗口位置尺寸异常问题
问题场景
Windows 11双显示器系统:一台4K显示器(缩放比例150%),一台全高清显示器(缩放比例100%)。需在主应用所在显示器之外的副屏显示全屏WPF窗口用于摄像头画面,但不同DPI设置导致窗口位置、尺寸异常。已开启Per-Monitor DPI感知,调整系统缩放因子无效。
现有XAML代码:
<Window x:Class="MyApp.Fullscreen" x:ClassModifier="internal" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="800" Height="600" AllowsTransparency="False" Background="Black" ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True" WindowStyle="None"> <Grid> <Image Name="WebCam" /> </Grid> </Window>
现有初始化代码:
var allSystemScreens = System.Windows.Forms.Screen.AllScreens; var otherScreen = DetermineOtherScreen(allSystemScreens); var window = new Fullscreen(); window.WindowStartupLocation = WindowStartupLocation.Manual; window.Left = otherScreen.WorkingArea.Left; window.Top = otherScreen.WorkingArea.Top; window.Width = otherScreen.WorkingArea.Width; window.Height = otherScreen.WorkingArea.Height; window.Show();
解决方案
1. 核心问题分析
System.Windows.Forms.Screen返回的是物理像素坐标和尺寸,而WPF在Per-Monitor DPI感知模式下,窗口的Left/Top/Width/Height使用的是设备无关像素(DIP)。两者的换算关系为:DIP = 物理像素 / (屏幕DPI / 96),必须进行坐标转换才能匹配WPF的渲染逻辑。
2. 修正初始化代码
添加Win32 API调用获取目标屏幕的DPI,完成物理像素到DIP的转换,并订阅DPI变化事件确保自适应:
using System; using System.Windows; using System.Windows.Forms; using System.Windows.Interop; using System.Runtime.InteropServices; // 获取目标屏幕的DPI值 private static (double DpiX, double DpiY) GetScreenDpi(Screen screen) { IntPtr hwnd = IntPtr.Zero; using (IntPtr dc = GetDC(hwnd)) { double dpiX = GetDeviceCaps(dc, LOGPIXELSX); double dpiY = GetDeviceCaps(dc, LOGPIXELSY); ReleaseDC(hwnd, dc); return (dpiX, dpiY); } } // 导入Win32 API [DllImport("user32.dll")] private static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll")] private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("gdi32.dll")] private static extern int GetDeviceCaps(IntPtr hdc, int nIndex); private const int LOGPIXELSX = 88; private const int LOGPIXELSY = 90; // 窗口初始化逻辑 var allSystemScreens = Screen.AllScreens; var otherScreen = DetermineOtherScreen(allSystemScreens); // 获取目标屏幕的DPI缩放因子(相对于标准96DPI) var (dpiX, dpiY) = GetScreenDpi(otherScreen); double scaleX = dpiX / 96.0; double scaleY = dpiY / 96.0; // 将物理像素转换为WPF的设备无关像素(DIP) double left = otherScreen.WorkingArea.Left / scaleX; double top = otherScreen.WorkingArea.Top / scaleY; double width = otherScreen.WorkingArea.Width / scaleX; double height = otherScreen.WorkingArea.Height / scaleY; var window = new Fullscreen(); window.WindowStartupLocation = WindowStartupLocation.Manual; window.Left = left; window.Top = top; window.Width = width; window.Height = height; // 订阅DPI变化事件,处理窗口跨显示器移动或系统DPI调整的情况 window.DpiChanged += (sender, e) => { var currentWindow = sender as Window; if (currentWindow == null) return; var newDpiScaleX = e.NewDpi.DpiScaleX; var newDpiScaleY = e.NewDpi.DpiScaleY; var currentScreen = Screen.FromHandle(new WindowInteropHelper(currentWindow).Handle); currentWindow.Left = currentScreen.WorkingArea.Left / newDpiScaleX; currentWindow.Top = currentScreen.WorkingArea.Top / newDpiScaleY; currentWindow.Width = currentScreen.WorkingArea.Width / newDpiScaleX; currentWindow.Height = currentScreen.WorkingArea.Height / newDpiScaleY; }; window.Show();
3. 优化XAML配置
移除硬编码的Width和Height,让窗口完全依赖代码动态设置;同时给摄像头画面添加拉伸属性适配窗口:
<Window x:Class="MyApp.Fullscreen" x:ClassModifier="internal" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" AllowsTransparency="False" Background="Black" ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True" WindowStyle="None"> <Grid> <Image Name="WebCam" Stretch="UniformToFill" /> </Grid> </Window>
4. 确认Per-Monitor DPI感知配置
确保app.manifest中已正确配置Per-Monitor DPI感知,这是所有适配生效的前提:
<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,提问作者juergen d
相关产品推荐
相关产品推荐

