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

WPF系统DPI缩放场景下窗口尺寸异常问题求助

解决WPF窗口在高DPI缩放系统下固定尺寸不受影响的问题

可行解决方案

1. 配置正确的DPI感知清单

在app.manifest中替换原有DPI相关配置,启用每显示器DPI感知v2,这是实现固定物理尺寸的基础:

<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <!-- 保留其他原有清单内容 -->
  <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>
</assembly>

2. 强制窗口固定物理像素尺寸+修正内部元素缩放

在窗口代码中,通过获取当前显示器DPI计算转换比例,手动锁定窗口物理尺寸,并覆盖WPF的自动缩放逻辑:

using System;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;

public partial class MainWindow : Window
{
    private const int TargetPhysicalWidth = 512;
    private const int TargetPhysicalHeight = 512;

    public MainWindow()
    {
        InitializeComponent();
        SourceInitialized += MainWindow_SourceInitialized;
    }

    private void MainWindow_SourceInitialized(object sender, EventArgs e)
    {
        var hwndSource = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        if (hwndSource == null) return;

        // 获取当前显示器的DPI缩放比例
        var dpiScaleX = hwndSource.CompositionTarget.TransformToDevice.M11;
        var dpiScaleY = hwndSource.CompositionTarget.TransformToDevice.M22;

        // 将目标物理像素转换为WPF设备无关单位,设置窗口尺寸
        Width = TargetPhysicalWidth / dpiScaleX;
        Height = TargetPhysicalHeight / dpiScaleY;

        // 强制内部元素不随DPI缩放,保持1:1物理像素对应
        hwndSource.CompositionTarget.TransformToDevice = Matrix.Identity;
        hwndSource.CompositionTarget.TransformFromDevice = Matrix.Identity;

        // 监听DPI变化事件,窗口跨显示器时保持尺寸不变
        hwndSource.AddHook(HwndSourceHook);
    }

    private IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        const int WM_DPICHANGED = 0x02E0;
        if (msg == WM_DPICHANGED)
        {
            // 解析新显示器的DPI值
            var newDpiX = (int)(wParam.ToInt64() & 0xFFFF);
            var newDpiY = (int)(wParam.ToInt64() >> 16);
            var dpiScaleX = newDpiX / 96.0;
            var dpiScaleY = newDpiY / 96.0;

            // 重新计算窗口尺寸,维持物理像素不变
            Width = TargetPhysicalWidth / dpiScaleX;
            Height = TargetPhysicalHeight / dpiScaleY;

            // 保持内部元素缩放比例为1
            var hwndSource = HwndSource.FromHwnd(hwnd);
            hwndSource.CompositionTarget.TransformToDevice = Matrix.Identity;
            hwndSource.CompositionTarget.TransformFromDevice = Matrix.Identity;

            handled = true;
        }
        return IntPtr.Zero;
    }
}

3. 清理冲突配置

  • 移除AssemblyInfo.cs中的[assembly: System.Windows.Media.DisableDpiAwareness]配置,避免与PerMonitorV2感知冲突
  • 删除app.config中Switch.System.Windows.DoNotScaleForDpiChanges的设置,保持默认即可

原理说明

  • 启用PerMonitorV2后,应用可直接感知当前显示器DPI,避免系统强制缩放整个窗口
  • 通过将CompositionTarget的转换矩阵设为单位矩阵,强制WPF内部元素布局与物理像素1:1对应,解决元素被缩放的问题
  • 监听WM_DPICHANGED事件,在窗口切换到不同DPI显示器时,重新计算窗口尺寸,始终维持目标物理像素大小

内容的提问来源于stack exchange,提问作者Cyclone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 09:15:15