You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:02:04