WinUI3中获取窗口所属显示器信息的技术求助
在WinUI3中获取当前窗口所属显示器的对应信息(替代UWP的DisplayInformation.GetForCurrentView)
核心思路
通过Win32 API结合WinUI3窗口句柄,关联当前窗口到对应显示器,再提取所需的像素、DPI、物理尺寸等信息,同时利用WinUI3原生事件监听DPI变化。
步骤1:获取当前WinUI3窗口的HWND
WinUI3窗口可通过WindowNative.GetWindowHandle获取底层句柄,这是后续关联显示器的基础:
using WinRT.Interop; // 在MainWindow类中 nint hwnd = WindowNative.GetWindowHandle(this);
步骤2:通过HWND获取对应显示器句柄
使用Win32的MonitorFromWindow获取当前窗口所属的显示器句柄(依赖CsWin32包):
using Windows.Win32; using Windows.Win32.Graphics.Gdi; HMONITOR hMonitor = PInvoke.MonitorFromWindow( hwnd, MONITOR_FROM_WINDOW_FLAGS.MONITOR_DEFAULTTONEAREST );
步骤3:获取屏幕原始像素宽高
通过GetMonitorInfoW提取显示器的物理像素范围:
MONITORINFOEX monitorInfo = new MONITORINFOEX(); monitorInfo.cbSize = (uint)Marshal.SizeOf(monitorInfo); if (PInvoke.GetMonitorInfo(hMonitor, ref monitorInfo)) { int screenWidthRaw = monitorInfo.rcMonitor.Right - monitorInfo.rcMonitor.Left; int screenHeightRaw = monitorInfo.rcMonitor.Bottom - monitorInfo.rcMonitor.Top; // 对应UWP的ScreenWidthInRawPixels/ScreenHeightInRawPixels }
步骤4:获取RawPixelsPerViewPixel(DPI缩放比)
可选择获取窗口DPI(和窗口绑定的缩放比)或显示器原生DPI:
// 获取窗口关联的DPI缩放比(和UWP的RawPixelsPerViewPixel一致) uint dpiX, dpiY; PInvoke.GetDpiForWindow(hwnd, out dpiX, out dpiY); double rawPixelsPerViewPixel = dpiX / 96.0; // 96为标准DPI基准值 // 或者获取显示器原生DPI PInvoke.GetDpiForMonitor( hMonitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out dpiX, out dpiY );
步骤5:获取显示器对角线尺寸(DiagonalSizeInInches)
通过DisplayConfigGetDeviceInfo获取物理尺寸,需先匹配当前显示器的DisplayConfig路径:
using Windows.Win32.Graphics.Display; using System.Runtime.InteropServices; DISPLAYCONFIG_TARGET_DEVICE_NAME targetDeviceName = new DISPLAYCONFIG_TARGET_DEVICE_NAME(); targetDeviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME; targetDeviceName.header.size = (uint)Marshal.SizeOf(targetDeviceName); // 枚举当前活跃的显示路径,匹配目标显示器 bool foundTarget = false; uint pathCount, modeCount; PInvoke.GetDisplayConfigBufferSizes(QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, out pathCount, out modeCount); DISPLAYCONFIG_PATH_INFO[] paths = new DISPLAYCONFIG_PATH_INFO[pathCount]; DISPLAYCONFIG_MODE_INFO[] modes = new DISPLAYCONFIG_MODE_INFO[modeCount]; if (PInvoke.QueryDisplayConfig( QUERY_DEVICE_CONFIG_FLAGS.QDC_ONLY_ACTIVE_PATHS, ref pathCount, paths, ref modeCount, modes, IntPtr.Zero) == 0) { foreach (var path in paths) { HMONITOR monitor = PInvoke.MonitorFromPoint( path.targetInfo.targetPosition, MONITOR_FROM_WINDOW_FLAGS.MONITOR_DEFAULTTONEAREST ); if (monitor == hMonitor) { targetDeviceName.header.adapterId = path.targetInfo.adapterId; targetDeviceName.header.id = path.targetInfo.id; foundTarget = true; break; } } } if (foundTarget && PInvoke.DisplayConfigGetDeviceInfo(ref targetDeviceName.header) == 0) { // 物理尺寸单位为厘米,转换为英寸(1英寸=2.54厘米) double diagonalInches = targetDeviceName.monitorPhysicalSize.cx / 2.54; // 对应UWP的DiagonalSizeInInches }
步骤6:监听DPI变化事件
直接使用WinUI3窗口原生的DpiChanged事件,替代UWP的DisplayInformation.DpiChanged:
// 在窗口构造函数或初始化逻辑中订阅 this.DpiChanged += MainWindow_DpiChanged; private void MainWindow_DpiChanged(object sender, WindowDpiChangedEventArgs args) { double newRawPixelsPerViewPixel = args.NewDpi / 96.0; // 处理DPI变化后的业务逻辑 }
注意事项
- 确保已安装CsWin32 NuGet包,并在项目中启用Win32 API的生成(可通过项目属性配置)。
DisplayConfigGetDeviceInfo返回31(ERROR_GEN_FAILURE)通常是因为未正确设置DISPLAYCONFIG_DEVICE_INFO_HEADER的adapterId和id,必须通过枚举显示路径匹配当前显示器。- 多显示器场景下,
MonitorFromWindow的MONITOR_DEFAULTTONEAREST参数会保证获取离窗口最近的显示器,避免错误关联。
内容的提问来源于stack exchange,提问作者Pavlo Datsiuk
相关产品推荐
相关产品推荐

