18

我有一个问题,即应用程序(用 Delphi 编写)在所有系统上的默认 96 DPI 设置下表现正常,但在不同系统上的“150% 文本大小”(内部 144 dpi)设置下表现不一致。似乎在某些系统上,我的应用程序的某些文本/字体部分正在被拉伸,而在其他系统上,它们不是。我会认为我的应用程序,在某个版本的 Windows (Win7) 上,在某个 DPI 上,应该以相同的方式运行。

我的应用程序要么让 Windows 知道它不需要 DPI 虚拟化功能,要么不需要。我就这么理解。我不明白的是,DPI 变化如何导致两台机器上出现不同的外观,两台机器都运行 Windows 7,都是 144 dpi,以相同的固定大小显示相同的字体和表单。

我需要在 Windows(注册表等)中检查 DPI 虚拟化中涉及的一些与配置相关的元素吗?否则,您如何排除故障并知道是否在您的客户端窗口上进行了 DPI 虚拟化?

在 Delphi 中,如果不想缩放,则必须将 TForm.Scaled 属性设置为 false。但我不明白的是,当主窗体的 Scaled 属性为真时,我不能总是预测结果。

在我的应用程序中最令我困惑的是,我有一个控件,它只会在我的大型实际应用程序中出现异常,但在我尝试仅调试控件的独立应用程序中不会出现异常。为了理解独立应用程序中的控制行为,我被迫制作了一个演示应用程序,我通过清单文件强制 DPI 感知。然后我可以重现控制绘图故障,尽管形式不同。

这是我在演示应用程序中使用的清单文件,它暴露了我的控件在处理 Windows 中的高 dpi 设置时遇到的问题。但是,我发现的一件奇怪的事情是应用程序可以

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
 <asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
    <asmv3:windowsSettings
         xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
      <dpiAware>true</dpiAware>
    </asmv3:windowsSettings>
  </asmv3:application>
  <assemblyIdentity version="14.0.3615.26342" processorArchitecture="*"            
   name="TestProject" type="win32"></assemblyIdentity>
  <description>High DPI Controls Test App</description>
</assembly>

这是当我在我的应用程序中禁用 DPI 虚拟化时,我的应用程序中的控件被弄乱的大约 30 个地方之一的示例。通过在我的表单中关闭 Scaled 属性解决了这个特殊的故障。但在其他地方,有 TForm.Scaled=false 会导致问题,而在某些形式中,它可以解决它:

具有非 DPI 虚拟化应用程序清单的 DPI 故障示例,但主要形式是 Scaled

更新:事实证明,我的某些控件使用 GDI+,并且 GDI+ 上下文中的字体行为与普通 GDI 上下文中的字体行为不同,至少对于某些使用 GDI+ 的第三方控件而言。这是头痛的主要来源。其次,对于 DPI 意识,VCL 中存在参差不齐的测试覆盖率和定义不明确的要求。一些 VCL 控件基于 MS 通用控件,虽然可以公平地说底层的通用控件在高 DPI 情况下可能工作正常,但并非所有 VCL 控件包装器都能保证正常工作。因此,检查应用程序在其所有控件中的高 DPI 意识,以及所有可用 Windows 7 主题中的正确行为:

  1. aero glass on,96dpi(大多数现代硬件上的默认 Win7 外观)
  2. 基本主题(航空玻璃关闭)但启用了 xp 主题
  3. 经典的win2000外观玻璃关闭,以及xp级别的主题,
  4. 高对比度白色
  5. 高对比度黑色
  6. 各种非 96 DPI 设置

..而且不胜枚举。作为应用程序开发人员,您的负担相当沉重。无论您是使用 VCL 的 delphi 用户,还是 MFC/ATL C++ 开发人员,在我看来,支持所有各种古怪的 windows 模式几乎是一个难以承受的负担。所以大多数人都不会打扰。我对吗?

4

4 回答 4

11

您需要通过如下部分来证明您的应用是 DPI 感知的:

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

如果您这样做,那么您将无法获得 DPI 虚拟化。

您不打算使用 DPI 虚拟化,所以我认为试图弄清楚它是如何工作的没有什么意义。它很容易依赖于显卡驱动程序。我们几乎不可能解释为什么虚拟化会以这种方式运行:您甚至没有给出任何屏幕截图、硬件详细信息等。但是,您根本不应该费心尝试诊断这一点。清单为 dpiaware,这不是问题。

作为参考,我为您提供:

于 2011-02-22T15:57:26.893 回答
9

这实际上是一个不同的问题。

您的表单不应该随着用户的DPI变大,它们应该随着font size变大。

Delphi 的默认表单字体8pt Tahoma平均8pt Tahoma 字符是
:.6.08px * 13px

从 Windows Vista 开始,默认字体为9pt Segoe UI平均9pt Segoe UI 字符是
:.6.81px * 15px

在此处输入图像描述

您的 Windows 应用程序应该尊重用户的字体偏好(即IconTitleFont)。

我的Windows 字体偏好是12pt Segoe UI,其平均字符大小是8.98px * 21px

在此处输入图像描述

这意味着,如果您在 Tahoma 8pt(13px 高)处设计表单需要将表单和所有子控件缩放 162%:

scaleFactor = (userFontSize / designedFontSize)
            = 21px / 13px
            = 1.615

如果您小心,您会注意到更改 DPI 只是更改字体大小的一种特殊情况。您的8pt字体仍然是8pt,但 8pt 转换为更多像素。如果您运行 131dpi(Windows 7 中的 136% 缩放设置),则:

9pt Segoe UI,  96dpi = 6.81px x 15px
9pt Segoe UI, 131dpi = 8.98px x 21px

在此处输入图像描述

注意:我选择131dpi12pt作为我的示例并非巧合。在工作中,我运行 96dpi 但 12pt。在家里我跑 9pt 但 131dpi。两者都有相同的像素字体高度,21px。


最后你需要ScaleControl通过大小差异来调用:

procedure StandardizeFormFont(Form: TForm);    
var
   iconTitleFontName: string;
   iconTitleFontSizePixels: Integer;
   currentFontSizePixels: Integer;
begin
   GetIconTitleFont(iconTitleFontName, iconTitleFontSizePixels);

   //Change font face first
   //Some fonts are inheriently taller than others
   //(even at the same point size)
   Form.Font.Name := iconTitleFontName;     

   //Get the form's current font height (in pixels)
   currentFontSizePixels := Form.Font.Height; //negative number, but so is iconTitleFontSizePixels

   Form.ScaleBy(iconTitleFontSizePixels, currentFontSizePixels);
end;

实际上,这段代码非常简单。许多子控件需要手动更新:

  • listview 列需要变宽
  • ParentFont = false需要调整字体的控件
  • TImage 控件需要将其图像拉伸到新的大小
  • 工具栏需要使用更大的图像

实际上,我们使用了一个超级花哨的版本,StandardizeFormFont它递归地遍历表单上的所有控件,并且最好根据每个控件的内容来调整每个控件。

每个控件都是 Delphi 应该覆盖它的ScaleControl方法,并进行所需的调整:

protected
   procedure ChangeScale(M, D: Integer); override;
于 2011-06-24T00:44:47.387 回答
7

在此处输入图像描述“自定义 DPI 设置”窗口有一个“使用 Windows XP 风格的 DPI 缩放”。这可能解释了不同的行为。

于 2011-02-22T18:56:50.667 回答
6

事实证明,当系统 DPI 更改为远离默认的 96 dpi 值时,我的应用程序中的怪癖分为三个一般阵营:

  1. 一些应用程序的控件使用 GDI,而一些控件使用 GDI+。至少在我使用的控件中,GDI 和 GDI+ 字体在不同 DPI 下的呈现方式存在一些差异。

  2. 我在 delphi 中使用了一个名为 VCL 的框架。在这个Delphi VCL框架中,有的表格有TForm.Scaled=true,有的有TForm.Scaled=false。因为它要求您以缩放的形式考虑每个控件,所以作为 UI 设计人员会发现缩放形式的“丑陋”或不可接受的事情是很常见的。关闭 Scaled 后,您会留下由 Windows 7 本身拉伸、处于高 DPI 设置(DPI 虚拟化模式)或看起来很小的表单,因此,如果您愿意,可以忽略用户的“请求”,对于 144 dpi 版本您的 96 dpi 用户界面。其他人可能正在使用其他语言的其他框架,或者甚至可能正在使用一些非常老式的东西,例如 Visual C++ 的对话框编辑器,您可以在“对话框单元”中设计对话框,这是另一种尝试将一般对话框布局从 1:1 对应像素中分离出来的方法。缩放、拉伸和布局控件是 UI 设计的一般部分,必须以符合平台要求的方式解决。就我而言,VCL 在让我设计支持 96 DPI 玻璃的航空应用程序方面做得很好,并且在其他 DPI 等级上也能很好地工作,但我的大多数自定义控件都没有。因此,坚持使用 VCL 附带的控件的另一个原因是:如果您关心高 DPI 支持,那么当您尝试使高 DPI 支持工作时,您的工作就会变得更加困难。VCL 在让我设计一个支持 96 DPI 玻璃的航空应用程序方面做得很好,并且在其他 DPI 评级方面表现出色,但我的大多数自定义控件都没有。因此,坚持使用 VCL 附带的控件的另一个原因是:如果您关心高 DPI 支持,那么当您尝试使高 DPI 支持工作时,您的工作就会变得更加困难。VCL 在让我设计一个支持 96 DPI 玻璃的航空应用程序方面做得很好,并且在其他 DPI 评级方面表现出色,但我的大多数自定义控件都没有。因此,坚持使用 VCL 附带的控件的另一个原因是:如果您关心高 DPI 支持,那么当您尝试使高 DPI 支持工作时,您的工作就会变得更加困难。

  3. 反过来,DPI 虚拟化由您必须明确包含在应用程序中的清单设置控制。由于我的应用程序已经有一个自定义清单(不是当您单击项目设置中的 Enable-windows-themes 复选框时包含在您的 Delphi 应用程序中的清单),我能够再次打开和关闭此 DPI 虚拟化,并测试我在这两种情况下的申请。我发现我的应用程序没有准备好在没有 DPI 虚拟化的情况下运行,因此我将 Windows 保留为默认行为。其他人的应用程序可能很容易在禁用 DPI 虚拟化的情况下工作,如果他们使用 100% vcl 控件,使用表单缩放或其他技术的表单,以在各种字体大小和 DPI 间距。)。在我看来,说到底,VCL功能够用,但真正工业级的解决方案,需要比VCL更高级的框架,才能全面妥善处理“高DPI环境”等问题,一般第三方控制在这些情况下,它甚至不能像当前的 VCL 那样工作。此类框架问题在 WPF 框架(Microsoft)和 Java(wing)中非常明显,但不属于 VCL 的经典“Win16/Win32 通用控制框架”的一部分。并且在这些情况下,第三方控件通常不会像当前的 VCL 那样工作。此类框架问题在 WPF 框架(Microsoft)和 Java(wing)中非常明显,但不属于 VCL 的经典“Win16/Win32 通用控制框架”的一部分。并且在这些情况下,第三方控件通常不会像当前的 VCL 那样工作。此类框架问题在 WPF 框架(Microsoft)和 Java(wing)中非常明显,但不属于 VCL 的经典“Win16/Win32 通用控制框架”的一部分。

总而言之,这些变化现在并没有那么不同(复杂),与过去相比,Windows XP 和其他版本的 Windows 为您提供“字体大小”的选择,而现在,Windows 7 UI 体验试图掩埋字体点大小选项非常深入,而是为您提供“文本大小”更改用户界面,该界面可修改表面下方的系统 DPI。这两种改变用户体验的方法都会导致几乎每个用户都遇到至少一个主要商业应用程序的问题,这些应用程序看起来或无法正常使用由此产生的变化。随着超高点距显示器在消费类 PC 领域变得越来越普遍,这个问题可能会越来越严重,需要围绕更合适的框架进行 UI 设计。

于 2011-02-24T17:30:26.000 回答