You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在不修改Win10 DPI设置的情况下,正确缩放WinForms窗体?

WinForms & WPF 高DPI缩放完美适配方案(匹配系统级效果)

我之前也踩过类似的高DPI缩放坑,尤其是WinForms和WPF与OWL框架字体/尺寸不一致的问题。结合微软官方的高DPI最佳实践,给你梳理一套能实现系统级正确缩放的方案,不用手动修改系统DPI设置:

一、先解决WinForms的核心问题:启用原生Per-Monitor DPI支持

手动调用Scale()方法只缩控件不缩字体,本质是因为你没启用WinForms的原生高DPI感知。从.NET Framework 4.7+(或.NET Core 3.0+)开始,WinForms已经支持Per-Monitor DPI v2,能自动处理控件、字体的缩放,完全匹配系统级效果:

  1. 修改项目的app.manifest文件
    <application>节点下添加DPI感知配置,告诉系统你的应用支持高DPI:

    <application xmlns="urn:schemas-microsoft-com:asm.v3">
      <windowsSettings>
        <!-- 兼容Windows 8.1及以下 -->
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
        <!-- 优先启用Windows 10+的PerMonitorV2模式 -->
        <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
      </windowsSettings>
    </application>
    

    如果你的项目没有app.manifest,可以右键项目→添加→新建项→选择“应用程序清单文件”。

  2. 在Program.cs中启用高DPI模式
    Application.Run()之前添加以下代码,确保应用启动时就进入正确的高DPI模式:

    static void Main()
    {
        // 仅对Windows 8.1及以上系统启用PerMonitorV2
        if (Environment.OSVersion.Version >= new Version(6, 3, 0))
        {
            Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
        }
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new YourMainForm());
    }
    
  3. 放弃手动Scale操作
    启用原生高DPI支持后,系统会自动处理窗体、控件、字体的缩放,完全不需要调用Form.Scale()。这时候WinForms的表现会和OWL一致,字体大小也能匹配。

二、解决WPF与WinForms互操作的缩放叠加问题

你提到用微软的WPF示例时,启用字体缩放后对话框尺寸超预期(1.25→1.6),这是因为WPF和WinForms的缩放逻辑叠加了

  • WPF在Per-Monitor模式下会自动缩放界面
  • 示例中的字体缩放函数又手动做了一次字体放大,导致总缩放比例变成1.25 * 1.28 ≈ 1.6(WPF的DPI计算和WinForms略有差异)

解决方法:

  1. 确保WPF也启用Per-Monitor DPI感知
    同样在WPF项目的app.manifest中添加上述的DPI配置。
  2. WinForms宿主控件无需额外手动缩放
    当WPF的WinFormsHost加载WinForms窗体时,只要WinForms已经启用PerMonitorV2,它会自动跟随WPF的DPI变化,不需要再调用示例中的字体缩放函数。如果必须调整,只需要同步一次缩放比例,不要重复叠加:
    private void MainWindow_DpiChanged(object sender, DpiChangedEventArgs e)
    {
        var scaleFactor = e.NewDpi.PixelsPerDip / 96;
        if (winFormsHost.Child is Form childForm)
        {
            // 只设置一次缩放,避免重复执行
            childForm.Scale(new SizeF(scaleFactor, scaleFactor));
        }
    }
    

三、统一三个框架的字体表现

OWL的字体看起来更大,是因为它默认适配了系统DPI缩放。要让WinForms/WPF和它对齐:

  • WinForms中使用系统字体,比如SystemFonts.DefaultFontSystemFonts.MessageBoxFont,这些字体大小会自动随DPI缩放,不要用固定大小的Font("Arial", 10)
  • WPF中同样绑定系统字体:
    <TextBlock FontFamily="{x:Static SystemFonts.MessageBoxFontFamily}" 
               FontSize="{x:Static SystemFonts.MessageBoxFontSize}" />
    

总结

核心思路是让系统自动处理缩放,不要手动造轮子

  1. 升级项目到支持高DPI的.NET版本
  2. 配置app.manifest启用Per-Monitor DPI感知
  3. 启用WinForms/WPF的原生高DPI模式
  4. 使用系统字体而非固定大小字体

这样你的WinForms/WPF窗体就能实现和OWL一致、和Windows 10系统级相同的缩放效果,不用修改系统DPI设置。

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

火山引擎 最新活动