Windows 窗体中的自动缩放

借助自动缩放功能,在某台计算机上以某种显示分辨率或系统字体设计的窗体及其控件可以在其他计算机上以不同的显示分辨率或系统字体适当显示。 它确保窗体及其控件可智能调整大小,使其与用户和其他开发人员计算机上的本机窗口和其他应用程序保持一致。 .NET Framework 支持自动缩放和视觉样式,这使 .NET Framework 应用程序可以在与每个用户的计算机上的本机 Windows 应用程序比较时保持一致的外观和感觉。

在大多数情况下,自动缩放在 .NET Framework 版本 2.0 及更高版本中按预期工作。 但是,字体方案更改可能会有问题。 有关如何解决此问题的示例,请参阅如何:在 Windows 窗体应用程序中响应字体方案更改

自动缩放的必要性

如果没有自动缩放,设计用于一个显示分辨率或字体的应用程序会在更改分辨率或字体时显示过小或太大。 例如,如果应用程序使用 Tahoma 9 点作为基线进行设计,且未做调整,那么在系统字体为 Tahoma 12 点的计算机上运行时,它会显得太小。 文本元素(如标题、菜单、文本框内容等)将显示得比其他应用程序更小。 此外,包含文本的用户界面(UI)元素的大小,如标题栏、菜单和许多控件都依赖于使用的字体。 在此示例中,这些元素也会显得相对较小。

当应用程序设计为特定显示分辨率时,会出现类似的情况。 最常见的显示分辨率是每英寸96个点(DPI),相当于100%的缩放比例,但支持125%、150%、200%(分别对应120、144和192 DPI)及更高分辨率的显示器变得越来越常见。 如果不进行调整,应用程序(尤其是基于图形的应用程序)在以另一分辨率运行时,针对一个分辨率设计的分辨率将显得太大或太小。

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

自动缩放的初始支持

.NET Framework 版本 1.0 和 1.1 支持以直接的方式自动缩放,这依赖于用于 UI 的 Windows 默认字体,由 Win32 SDK 值 DEFAULT_GUI_FONT表示。 此字体通常仅在显示分辨率更改时更改。 已使用以下机制来实现自动缩放:

  1. 在设计时,AutoScaleBaseSize 属性(现已弃用)设置为开发人员计算机上默认系统字体的高度和宽度。

  2. 在运行时,用户计算机的默认系统字体用于初始化 Form 类的 Font 属性。

  3. 在显示窗体之前,调用 ApplyAutoScaling 方法来缩放窗体。 此方法计算 AutoScaleBaseSizeFont 的相对缩放大小,然后调用 Scale 方法来实际缩放窗体及其子窗体。

  4. 更新了 AutoScaleBaseSize 的值,以便对 ApplyAutoScaling 的后续调用不会导致窗体不断调整大小。

虽然此机制足以满足大多数目的,但存在以下限制:

  • 由于 AutoScaleBaseSize 属性将基准字体大小表示为整数值,窗体循环使用多个解决方案时舍入误差就变得非常明显。

  • 自动缩放仅在 Form 类中实现,而不是在 ContainerControl 类中实现。 因此,仅当用户控件设计为与窗体相同的分辨率时,用户控件才会正确缩放,并且该控件在设计时放置在窗体中。

  • 表单及其子控件只能由多个开发人员同时设计,前提是其计算机分辨率相同。 同样,它还使窗体的继承依赖于与父窗体相关的分辨率。

  • 它与 .NET Framework 版本 2.0 中引入的较新的布局管理器不兼容,例如 FlowLayoutPanelTableLayoutPanel

  • 它不支持直接基于与 .NET Compact Framework 兼容所需的显示分辨率进行缩放。

尽管此机制在 .NET Framework 版本 2.0 中保留以保持向后兼容性,但它已被下一步描述的更可靠的缩放机制取代。 因此,AutoScaleApplyAutoScalingAutoScaleBaseSize和某些 Scale 重载被标记为已过时。

说明

将旧代码升级到 .NET Framework 版本 2.0 时,可以安全地删除对这些成员的引用。

自动缩放的当前支持

.NET Framework 版本 2.0 通过引入对 Windows 窗体自动缩放的以下更改来替代以前的限制:

  • 对缩放的基本支持已移动到 ContainerControl 类,以便窗体、原生复合控件和用户控件都能获得统一的缩放支持。 添加了新成员 AutoScaleFactorAutoScaleDimensionsAutoScaleModePerformAutoScale

  • Control 类还具有使其可参与缩放并支持在同一个窗体上的混合缩放的几个新成员。 具体而言,ScaleScaleChildrenGetScaledBounds 成员支持缩放。

  • 已按照 AutoScaleMode 枚举定义添加基于屏幕分辨率的缩放支持以补充系统字体支持。 此模式与 .NET Compact Framework 支持的自动缩放兼容,可简化应用程序迁移。

  • 与布局管理器(如 FlowLayoutPanelTableLayoutPanel)的兼容性已添加到自动缩放的实现中。

  • 缩放因子现在表示为浮点值,通常使用 SizeF 结构,以便实际上消除了舍入错误。

谨慎

不支持 DPI 和字体缩放模式的任意混合。 尽管您可以使用一种模式(例如 DPI)缩放用户控件,并使用另一种模式(字体)在窗体上放置该控件,通常不会出现问题,但如果以一种模式为基准形式,并在另一种模式下使用派生形式,可能会产生意外的结果。

操作中的自动缩放

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

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

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

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

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

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

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

    • 如上所述,当父 ContainerControl 正在缩放时。 每个容器控件负责使用自己的比例因子(而不是其父容器的比例因子)缩放其子控件。

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

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

    • 可重写 GetScaledBounds 方法以调整控件缩放到的边界,但不是调整缩放逻辑。

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

另请参阅