Windows 窗体中的自动缩放
更新:2007 年 11 月
通过自动缩放,可以使在具有特定显示分辨率或系统字体的计算机上设计的窗体及其控件也适合在另一台具有不同的显示分辨率或系统字体的计算机上显示。这样可以确保通过智能化方式调整窗体及其控件的大小,使其同时与用户计算机和其他开发人员计算机上的本地窗口和其他应用程序保持一致。由于 .NET Framework 支持自动缩放和视觉样式,使得 .NET Framework 应用程序在与每个用户计算机的本机 Windows 应用程序进行比较时可以保持一致的外观。
自动缩放的必要性
在没有使用自动缩放的情况下,当改变显示分辨率或字体时,为该分辨率或字体设计的应用程序不是显得太大,就是显得太小。例如,假设应用程序是使用 Tahoma 9 point 为基准设计的,当在系统字体为 Tahoma 12 point 的计算机上运行时,如果不对它进行任何调整,该应用程序会显得很小。呈现的文本元素(如标题、菜单、文本框内容等)与其他应用程序相比都要小一些。此外,包含文本的用户界面 (UI) 元素(如标题栏、菜单和许多控件)的大小也与使用的字体相关联。在这种情况下,这些元素也会显得相对小些。
如果应用程序是针对某种显示分辨率设计的,也会发生类似的情况。最常用的显示分辨率是每英寸 96 点 (DPI),但是支持 120、133、170 及以上的更高分辨率的显示器也变得越来越常用。如果某个应用程序(特别是基于图形的应用程序)是为某种分辨率设计的,如果不进行调整,当该应用程序在另一种分辨率下运行时不是显得太大,就是显得太小。
自动缩放寻求解决这些问题,方法是按照相对字体大小或显示分辨率自动对窗体及其子控件进行调整。Windows 操作系统支持使用一种被称为对话框单位的相对度量单位自动对对话框进行缩放。对话框单位基于系统字体,它与像素的关系可以通过 Win32 SDK 函数 GetDialogBaseUnits 确定。当用户更改了 Windows 使用的主题时,会相应地自动调整所有对话框。另外,
.NET Framework 支持根据默认系统字体或显示分辨率自动缩放。也可以选择在应用程序中禁用自动缩放。
先前的自动缩放支持
在 .NET Framework 版本 1.0 和 1.1 中支持的自动缩放采用一种很直接的方法,这种方法依赖于 UI 使用的 Windows 默认字体,该字体由 Win32 SDK 值 DEFAULT_GUI_FONT 表示。这种字体通常只在显示分辨率改变时才会改变。下面的机制用于实现自动缩放:
设计时,AutoScaleBaseSize 属性(现在已被废弃)被设置为开发人员计算机上默认系统字体的高度和宽度。
在显示窗体之前,会调用 ApplyAutoScaling 方法来缩放窗体。此方法通过 AutoScaleBaseSize 和 Font 计算相对缩放尺寸,然后调用 Scale 方法实际缩放窗体及其子控件。
AutoScaleBaseSize 值已更新,这样后来调用 ApplyAutoScaling 时不会累进调整窗体。
虽然此机制在大部分情况下可满足需要,但是它具有下列缺陷:
由于 AutoScaleBaseSize 属性将基线字体大小表示为整数值,所以某个窗体在经历多次分辨率更改后,发生的舍入误差会变得很明显。
仅在 Form 类中实现了自动调整,但没有在 ContainerControl 类中实现。所以,只有当用户控件与窗体在相同的分辨率下进行设计并在设计阶段将控件放入窗体中,用户控件才能正确缩放。
只有在多个开发人员的计算机分辨率都相同的情况下,才能由这些开发人员同时设计窗体及其子控件。同样,这还会使窗体的继承依赖于与父窗体关联的分辨率。
它与 .NET Framework 版本 2.0 中引入的更新的布局管理器(如 FlowLayoutPanel 和 TableLayoutPanel)不兼容。
它不支持直接根据与 .NET Compact Framework 兼容所需的显示分辨率进行缩放。
虽然此机制在 .NET Framework 版本 2.0 中被保留以维护向后兼容性,但它已被下述更健壮的缩放机制所取代。因而,AutoScale、ApplyAutoScaling、AutoScaleBaseSize 和某些 Scale 重载已被标记为过时。
说明: |
---|
在将旧版代码升级到 .NET Framework 版本 2.0 时,可以安全地删除对这些成员的引用。 |
现在的自动缩放支持
在 .NET Framework 版本 2.0 中,通过对 Windows 窗体的自动缩放进行下列更改而克服了前面提到的缺陷:
对缩放的基本支持已经移到 ContainerControl 类,这样窗体、本机复合控件和用户控件都会获得统一的缩放支持。此外,添加了 AutoScaleFactor、AutoScaleDimensions、AutoScaleMode 和 PerformAutoScale 这些新成员。
Control 类也拥有若干新成员,这些新成员允许该类参与缩放并支持在同一窗体上进行混合缩放。具体地说,是 Scale、ScaleChildren 和 GetScaledBounds 这些成员支持缩放。
作为系统字体支持的补充,已经添加了建立在屏幕分辨率基础上的缩放支持,如 AutoScaleMode 枚举所定义。此模式与 .NET Compact Framework 支持的自动缩放是兼容的,这样更易于进行应用程序的迁移。
与诸如 FlowLayoutPanel 和 TableLayoutPanel 等布局管理器的兼容性已经添加到自动缩放的实现中。
缩放比例因子现在表示为浮点值,通常使用 SizeF 结构,这样实际上已经消除了舍入误差。
警告: |
---|
目前,还不支持 DPI 和字体缩放模式的任意混合。您可以使用一种模式(例如,DPI)缩放某个用户控件,然后使用另一模式(字体)将该控件放置在窗体上,尽管这样做时不会产生任何问题,但是当将某个模式下的一个基窗体与另一个模式下的派生窗体混合在一起时会导致不可预料的结果。 |
自动缩放的执行过程
Windows 窗体现在使用下面的逻辑自动对窗体及其内容进行缩放:
设计时,每一个 ContainerControl 分别在 AutoScaleMode 和 AutoScaleDimensions 中记录缩放模式和它的当前分辨率。
运行时,实际分辨率存储在 CurrentAutoScaleDimensions 属性中。AutoScaleFactor 属性会动态计算运行时分辨率与设计时分辨率的比值。
当加载窗体时,如果 CurrentAutoScaleDimensions 和 AutoScaleDimensions 的值不同,则会调用 PerformAutoScale 方法对该控件及其子控件进行缩放。此方法会挂起布局并调用 Scale 方法执行实际缩放。然后,会更新 AutoScaleDimensions 值以避免累进缩放。
在下面的情况下还会自动调用 PerformAutoScale:
在缩放模式为 Font 时响应 OnFontChanged 事件。
当继续执行容器控件的布局时检测到 AutoScaleDimensions 或 AutoScaleMode 属性发生更改。
与上面的情况类似,检测到父 ContainerControl 正在被缩放。每个容器控件只负责使用自己的比例因子缩放自己的子控件,并不负责缩放其父容器中的控件。
子控件可以通过下面的若干方式修改其缩放行为:
可以重写 ScaleChildren 属性以确定是否应缩放其子控件。
可以重写 GetScaledBounds 方法以调整要将控件缩放至的边界,但不调整缩放逻辑。
可以重写 ScaleControl 方法以更改当前控件的缩放逻辑。