自动缩放

自动缩放使在一台具有特定显示分辨率或字体的计算机上设计的窗体及其控件,能够在另一台具有不同显示分辨率或字体的计算机上正常显示。 表单和控制可智能调整大小,以与用户和其他开发人员计算机上的本机窗口和其他应用程序保持一致。 与每个用户计算机上的本机Windows应用程序相比,自动缩放和视觉样式有助于Windows 窗体应用程序保持一致的外观。

在大多数情况下,Windows 窗体中的自动缩放能够按预期运行。 但是,字体方案更改可能会有问题。 有关如何解决此问题的示例,请参阅如何:响应Windows 窗体应用程序中的字体方案更改

.NET 中的 DPI 感知能力

DPI 感知通过项目文件中的 ApplicationHighDpiMode 属性进行配置,该属性默认设置为 SystemAware。 此配置适用于应用程序启动系统,以在应用程序启动时自动配置 DPI 处理。

Windows 窗体应用程序的默认和建议 DPI 模式为 SystemAware。 此模式在应用程序启动时查询 DPI 设置一次,并在应用程序的整个生存期内使用该值,从而提供一致的缩放行为。

在项目文件中配置 DPI 模式:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <ApplicationHighDpiMode>SystemAware</ApplicationHighDpiMode>
  </PropertyGroup>
</Project>

注释

虽然建议的方法是通过项目文件配置 DPI,但仍可以使用应用程序清单文件(app.manifest)来替代这些设置。 但是,不建议使用清单,因为它可能会导致与应用程序配置冲突。 有关详细信息,请参阅 编译器警告WFO0003

Visual Studio设计器注意事项

在Visual Studio中设计窗体时,可能需要将设计器的 DPI 感知与应用程序分开配置。 从 Visual Studio 2022 版本 17.8 开始,可以在项目文件中设置ForceDesignerDPIUnaware属性,使Windows 窗体设计器在 DPI-unaware 模式下运行。 此设置有助于避免在高 DPI 监视器上设计窗体时出现呈现问题:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <ForceDesignerDPIUnaware>true</ForceDesignerDPIUnaware>
  </PropertyGroup>
</Project>

此属性仅影响Visual Studio设计器,并且不会更改应用程序运行方式。 有关详细信息,请参阅 “禁用 DPI 感知”以解决缩放问题

可用的 DPI 模式包括:

  • SystemAware— 应用程序在启动时查询 DPI 一次,并在应用的生存期内使用此值。
  • PerMonitor—应用程序会按每个监视器分别检测 DPI 变化,并在 DPI 发生变化时重新缩放。
  • PerMonitorV2—与 PerMonitor 类似,但会启用子窗口 DPI 更改通知和改进的缩放行为。
  • DpiUnaware—应用程序不会针对 DPI 更改进行缩放,而是由 Windows 处理缩放。
  • DpiUnawareGdiScaled- 类似于 DpiUnaware ,但为基于 GDI 的内容提供更好的质量。

有关配置 DPI 设置的详细信息,请参阅 .NET 6 Windows 窗体中的新增功能。

自动缩放的必要性

如果没有自动缩放,设计用于一个显示分辨率或字体的应用程序在分辨率或字体更改时显示过小或太大。 例如,如果你在设计应用程序时以 9 磅 Tahoma 字体为基准,那么如果不做调整,在系统字体为 12 磅 Tahoma 的计算机上运行时,应用程序看起来会显得太小。 文本元素(如标题、菜单和文本框内容)的显示尺寸比其他应用程序中的更小。 此外,包含文本的用户界面 (UI) 元素的大小(如标题栏、菜单和许多控件)取决于使用的字体。 在此示例中,这些元素看起来也相对较小。

为特定显示分辨率设计应用程序时,会出现类似的情况。 最常见的显示分辨率是每英寸 96 点(DPI),等于 100% 显示缩放,但更高的分辨率显示支持 125%、150%、200%(分别等于 120、144 和 192 DPI)和更高版本越来越常见。 如果不进行调整,为某一种分辨率设计的应用程序,尤其是基于图形的应用程序,在另一种分辨率下运行时,显示出来会显得过大或过小。

自动缩放通过根据相对字号或显示分辨率自动调整窗体及其子控件的大小来解决这些问题。 Windows 操作系统支持使用称为对话框单位的相对度量单位自动缩放对话框。 对话单元基于系统字体,其与像素的关系可以通过 Win32 SDK 函数 GetDialogBaseUnits确定。 当用户更改Windows使用的主题时,所有对话框都会相应地调整。 Windows 窗体支持根据默认系统字体或显示分辨率自动缩放。 (可选)可以在应用程序中禁用自动缩放。

谨慎

不支持 DPI 和字体缩放模式的任意混合。 虽然可以使用一种模式(例如 DPI)缩放用户控件,并使用另一种模式(Font)将其放置到窗体上,而且通常不会有问题,但如果基窗体使用一种模式,而派生窗体使用另一种模式,则可能会导致意外结果。

操作中的自动缩放

Windows 窗体使用以下逻辑自动缩放窗体及其内容:

  1. 在设计时,每个ContainerControl都会分别在AutoScaleModeAutoScaleDimensions属性中记录缩放模式及其当前分辨率。

  2. 在运行时,实际分辨率存储在 CurrentAutoScaleDimensions 属性中。 AutoScaleFactor 属性动态计算运行时和设计时缩放分辨率之间的比率。

  3. 当窗体加载时,如果 CurrentAutoScaleDimensionsAutoScaleDimensions 的值不同,则会调用 PerformAutoScale 方法来缩放该控件及其子控件。 此方法暂停布局并调用 Scale 方法进行缩放。 之后,将更新该值 AutoScaleDimensions 以避免渐进式缩放。

  4. 在以下情况下,也会自动调用 PerformAutoScale

    • 如果缩放模式为 OnFontChanged,则响应 Font 事件。

    • 当容器控件的布局继续执行并在 AutoScaleDimensionsAutoScaleMode 属性中检测到更改时。

    • 在缩放父级 ContainerControl 时。 每个容器控件负责使用自己的比例因子(而不是其父容器的比例因子)缩放其子控件。

  5. 子控件可通过多种方式修改其缩放行为:

    • 重写 ScaleChildren 属性,以确定是否应缩放其子控件。

    • 重写 GetScaledBounds 方法以调整控件将缩放到的边界范围,而不是缩放逻辑本身。

    • 重写 ScaleControl 方法以更改当前控件的缩放逻辑。

高 DPI 改进

.NET 在高 DPI 渲染方面包含了显著改进,尤其是在 PerMonitorV2 模式下:

  • 每监视器 DPI 感知—当应用程序在具有不同 DPI 设置的监视器之间移动时,会动态调整。
  • 改进了缩放行为 — DPI 更改时控件可正确缩放,包括嵌套控件和容器控件(.NET 6+)。
  • 窗体大小缩放 -MaximumSizeMinimumSize属性根据当前监视器 DPI 设置缩放(.NET 7+,默认在 .NET 8+ 中启用)。
  • DPI 更改事件 - 新事件允许以编程方式处理动态 DPI 更改:
    • DpiChanged—当当前显示该窗体的显示设备上的 DPI 设置更改时引发。
    • DpiChangedBeforeParent- 当控件的 DPI 设置在发生其父控件或窗体的 DPI 更改事件之前以编程方式更改时触发。
    • DpiChangedAfterParent- 当控件的 DPI 设置在其父控件或窗体发生 DPI 更改事件后以编程方式更改时触发。

有关高 DPI 改进的详细信息,请参阅 .NET 6 Windows 窗体 中的新增功能、.NET 7 Windows 窗体 中的新增功能以及 .NET 8 Windows 窗体中的新增功能。

.NET框架差异

.NET框架和.NET处理 DPI 感知的方式不同:

  • 在 .NET Framework 中,通过 app.config 文件中的 <System.Windows.Forms.ApplicationConfigurationSection> 元素配置 DPI 感知。
  • 在 .NET 中,通过项目文件中的 ApplicationHighDpiMode 属性配置 DPI 感知。
  • .NET Framework 使用清单文件进行 DPI 配置,但现已不再建议这样做。
  • .NET提供更好的缩放行为和更可靠的 DPI 更改处理。

有关 .NET 框架 DPI 配置的信息,请参阅 Windows 窗体 中的高 DPI 支持

另请参阅