在Windows Vista和Windows 7中对DPI虚拟化和DPI感知应用程序进行故障诊断

时间:2011-02-22 15:48:26

标签: delphi windows-7 windows-vista dpi highdpi

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

我的应用程序将使Windows知道它不需要DPI虚拟化功能,或者它不会。我明白了。我不明白的是DPI的变化如何在两台机器上造成不同的外观,两台机器都以144 dpi运行Windows 7,以相同的固定大小显示相同的字体和形式。

我需要在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 Glitch Example with a non-DPI-virtualized app manifest, but main form is Scaled

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

  1. 航空玻璃,96dpi(大多数现代硬件上的默认Win7外观)
  2. 基本主题(航空玻璃关闭)但启用了xp主题
  3. 经典的win2000外观玻璃关闭,以及xp级主题,
  4. 高对比度白色
  5. 高对比度黑色
  6. 各种96-DPI以外的设置
  7. ..并且列表继续。作为应用程序开发人员,你有一个相当沉重的负担。无论您是delphi用户还是使用VCL,或者您是MFC / ATL C ++开发人员,在我看来,支持所有各种奇特的Windows模式是一种几乎无法承受的负担。所以大多数人都不打扰。我是对的吗?

4 个答案:

答案 0 :(得分:10)

您需要通过以下部分表明您的应用具有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,这是一个无问题。

供参考,我为您提供:

答案 1 :(得分:9)

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

根据用户的 DPI ,您的表单不应该越来越大,字体大小时,表单应该越来越大。

Delphi's default form font 8pt Tahoma average 8pt Tahoma字符是:6.08px * 13px

从Windows Vista开始,默认字体为 9pt Segoe UI average 9pt Segoe UI角色为:6.81px * 15px

enter image description here

您的Windows应用程序应该尊重用户的字体首选项(即IconTitleFont)。

我的 Windows字体首选项为 12pt Segoe UI ,其平均字符大小为:8.98px * 21px

enter image description here

这意味着如果您在 Tahoma 8pt (13px high)设计 表单,则需要将表单和所有子控件缩放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

enter image description here

  

注意:我选择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;

答案 2 :(得分:7)

enter image description here“自定义DPI设置”窗口具有“使用Windows XP样式DPI缩放”。这可能解释了不同的行为。

答案 3 :(得分:6)

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

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

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

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

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