如何配置应用程序以在高DPI设置(例如150%)的计算机上正确运行?

118

我用C#创建了一个简单的Winforms应用程序。当我在高DPI设置的机器上运行该应用程序(例如150%)时,应用程序会被放大。到目前为止一切都还好! 但是,与其使用更高的字体大小呈现字体,所有文本都只是被放大了。这导致所有控件(如按钮等)的文本非常模糊。

难道Windows不能正确地渲染文字吗?例如,我的应用程序标题栏被清晰而清晰地呈现。

7个回答

150

当你超过100%(或勾选“XP风格DPI缩放”复选框后超过125%)的比例时,Windows默认接管您UI的缩放。它通过使应用程序将其输出呈现为位图并将该位图绘制到屏幕上来实现。那个位图的重新缩放会使文本不可避免地变得模糊。一个名为“DPI虚拟化”的功能,使旧程序可以在高分辨率监视器上使用。

您必须显式告诉它,您可以处理更高的DPI设置,方法是向您的清单添加<dpiAware>元素。 MSDN页面在此处,但它不完整,因为它省略了UAC设置。 项目+添加新项,选择“应用程序清单文件”。编辑清单文本或复制/粘贴此内容:

<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
    <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <requestedExecutionLevel level="asInvoker" uiAccess="false" />
            </requestedPrivileges>
        </security>
    </trustInfo>
    <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
            <dpiAware>true</dpiAware>
        </asmv3:windowsSettings>
    </asmv3:application>
</assembly>

如果你使用ClickOnce发布应用程序,你也可以在Main()方法中调用SetProcessDPIAware()函数,这是必需的:

    [STAThread]
    static void Main() {
        if (Environment.OSVersion.Version.Major >= 6) SetProcessDPIAware();
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());             // Edit as needed
    }

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool SetProcessDPIAware();

更新:如果您使用的是VS2015 Update 1或更高版本,则这个常见需求会变得更加容易。已经添加了相关指令的清单,只需删除注释即可。


用于搜索的关键字,以便我可以找回这篇文章:dpiAware


谢谢,你的解决方案很好用。唯一剩下的问题是所有的图片现在都是它们原来的大小。我想我得找到一种方法来为我的GUI添加额外的“视网膜”图标... - Boris
@HansPassant 我也有模糊字体的问题,但是在应用了这个解决方案后,我的控件不会缩放并且无法适应。如何让两者都正常工作? - gajo357
4
对于任何调查 Win8 疯狂情况的人,SetProcessDPIAware 已经被弃用,并且它也无法正常工作(至少在 Win8.1 中),会导致不同控件上的不可预测缩放。我强烈建议使用清单方法代替。 - Jason Williams
5
嗨呀,Windows 8.1 获得了逐像素 DPI。我之前并不知道我需要它。 - Hans Passant
1
如果您要使用ClickOnce进行部署,就不能在清单中使用dpiAware选项,而应该使用SetProcessDPIAware()。 - Tute

19

应用程序可以以两种不同的模式开发。

第一种是声明我们的应用程序为非 DPI-aware(不声明任何内容将默认为此)。在这种情况下,操作系统会以预期的96 DPI呈现我们的应用程序,然后执行我们之前讨论过的位图缩放。结果将是一个模糊的应用程序外观,但布局是正确的。

第二个选项是声明应用程序为DPI-aware。在这种情况下,操作系统不会进行任何缩放,而是让您的应用程序根据屏幕的原始DPI进行渲染。在每个单独的监视器DPI环境中,您的应用程序将以所有屏幕中最高的DPI进行呈现,然后将该位图缩小到每个监视器的适当大小。向下缩放比向上缩放更好,但您可能仍然会注意到一些模糊。

如果要避免这种情况,则必须将应用程序声明为per-monitor-DPI-aware。然后,您必须检测应用程序何时在不同的监视器之间拖动,并根据当前监视器的DPI进行渲染。

声明DPI感知是在清单文件中完成的。

请参阅以下链接 stackoverflow


如果用户有一个处于“恢复”大小的窗口,并将其移动,使其部分位于不同的监视器上,我们需要渲染两次并使用监视器边界作为边界框吗? WinForms库覆盖了多少内容? - Cee McSharpface

8

使用.NET Framework 4.7和Windows 10 Creators Update(1703)或更新版本,您必须执行以下操作来配置Windows窗体应用程序的高DPI支持:

声明与Windows 10的兼容性。

为此,请将以下内容添加到您的manifest文件中:

<compatibility xmlns="urn:schemas-microsoft.com:compatibility.v1">
  <application>
    <!-- Windows 10 compatibility -->
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
  </application>
</compatibility>

app.config文件中启用每个监视器的DPI感知。

Windows Forms引入了一个新的System.Windows.Forms.ApplicationConfigurationSection元素,以支持从.NET Framework 4.7开始添加的新功能和自定义。为了利用支持高DPI的新功能,请将以下内容添加到应用程序配置文件中。

<System.Windows.Forms.ApplicationConfigurationSection>
  <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>

重要提示

在之前的.NET Framework版本中,您使用清单来添加高DPI支持。由于覆盖了在app.config文件中定义的设置,因此不再推荐使用这种方法。

调用静态的EnableVisualStyles方法。

这应该是您的应用程序入口点中的第一个方法调用。例如:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());   
}

这样做的好处是支持动态DPI场景,在这种场景下,用户在启动Windows Forms应用程序后更改DPI或比例因子。
来源:Windows Forms中的高DPI支持

4

对我来说,这些建议都没有起作用,但在我从 Form.Design.cs 中移除了 Form.Font = new ... 后,某些事情发生了,窗体开始正常重新缩放,如果字体在构造函数中定义或根本没有定义,则此方法有效。为什么?其他人可能能够解释,我只能谈论我所做的更改,花了我几分钟才找出这是我正在处理的窗体的根本原因。希望能有所帮助。


3

自 Visual Studio 2017 起,您只需添加一个清单文件并取消注释此部分即可:

<application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
        <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </windowsSettings>
</application>

2
在 Windows 11 和 Visual Studio 2022 (17.1.6) 中,这对我没有起作用。 - gridtrak
@gridtrak 谢谢您的评论。我会关注这个问题。但是我还没有安装 Windows 11。 - Aranxo

0

这不是一个答案。这是我的解决方法。以上的答案或评论对我都没有用。我也搜索并尝试了其他方法。

自 Visual Studio.NET 发布以来,我一直在使用 C# 和 Windows.Forms。直到今年的 VS 2022 和 Windows 11,设置比例模式似乎运行良好。但出现了一个问题,某些表单高度值在运行时被减小。对于表单宽度的更改,目前还没有遇到任何问题。对我而言,这个问题始于 2022 年 4 月 1 日,所以我一开始以为这是愚人节的玩笑!

无论如何,我现在已经放弃尝试解决方案,并决定在构造器代码中直接设置表单大小,这对我来说更加实际。

我注意到设计师 UI 使用 Size,将其转换为以下代码中的 ClientSize

  this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
  this.ClientSize = new System.Drawing.Size(744, 109);
  this.ControlBox = false;
  this.DoubleBuffered = true;
  this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
  this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;

我的解决方法在我的窗体构造函数中看起来像这样:

/// <summary>
/// Constructor
/// </summary>
public MyForm()
{
  // In the designer, MyForm.Size was entered and displayed as 760, 148
  InitializeComponent();

  // At runtime, MyForm.Size is changed to 760, 111
  // I will Reset this form's Size here so I can get full height again.
  this.Size = new Size(760, 148);
}

平台:

  • Windows 11 专业版
  • Microsoft Visual Studio 2022 专业版
  • 版本 17.1.6
  • VisualStudio.17.Release/17.1.6+32421.90
  • Microsoft .NET Framework 版本 4.8.04161
  • C# 工具 4.1.0-5.22165.10+e555772db77ca828b02b4bd547c318387f11d01f
  • HDMI 1920x1080 视频(100% 或无缩放)

0

我已经为此奋斗多年了。有时我会做出有效的更改,有时则不行。现在我终于找到了一个适用于所有监视器的设置:

在您的 app.config 文件中添加以下部分。请注意,值为 "SystemDPIAware"。

您的屏幕将在每个监视器上看起来相同。

    <System.Windows.Forms.ApplicationConfigurationSection>
    <add key="DpiAwareness" value="SystemDPIAware"/>
  </System.Windows.Forms.ApplicationConfigurationSection>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接