War*_* P 18 delphi dpi highdpi windows-vista windows-7
我有一个问题,在所有系统上,应用程序(用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>
Run Code Online (Sandbox Code Playgroud)
这是我在应用程序中禁用DPI虚拟化时,我的应用程序中的控件混乱的大约30个地方之一的示例.通过关闭我的表单中的Scaled属性来解决这个特殊的故障.但在其他地方,TForm.Scaled = false导致问题,而在某些形式,它修复它:

更新:事实证明我的一些控件使用GDI +并且GDI +上下文中的字体行为与普通GDI上下文中的字体行为不同,至少对于使用GDI +的某些第三方控件而言.这是头痛的主要原因.其次,对于VCL中的DPI感知,存在不稳定的测试覆盖率和不明确的要求.一些VCL控件基于MS公共控件,虽然公平地说底层公共控件在高DPI情况下可能正常工作,但并不是所有的VCL控件包装都能保证正常工作.因此,在所有控件中查看高DPI感知应用程序,以及所有可用Windows 7主题中的正确行为:
..并且列表继续.作为应用程序开发人员,你有一个相当沉重的负担.无论您是delphi用户还是使用VCL,或者您是MFC/ATL C++开发人员,在我看来,支持所有各种奇特的Windows模式是一种几乎无法承受的负担.所以大多数人都不打扰.我对吗?
Dav*_*nan 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>
Run Code Online (Sandbox Code Playgroud)
如果您这样做,那么您将无法获得DPI虚拟化.
您并不打算使用DPI虚拟化,所以我认为尝试解决它的工作原理并没有什么意义.它很容易依赖于显卡驱动程序.我们几乎不可能解释为什么虚拟化会以这种方式运行:您甚至没有给出任何屏幕截图,硬件细节等等.但是,您根本不应该费心去尝试诊断它.显示为dpiaware,这是一个无问题.
作为参考,我为您提供:
这实际上是一个不同的问题.
您的表单不应该随用户的DPI变大,它们应该随着字体大小变大.
Delphi的默认表格字体是8pt Tahoma.
该平均 8PT宋体字是:6.08px * 13px.
从Windows Vista开始,默认字体为9pt Segoe UI.
该平均宋体的Segoe UI特点是:6.81px * 15px.

您的Windows应用程序应该尊重用户的字体首选项(即IconTitleFont).
我的 Windows字体首选项是12pt Segoe UI,其平均字符大小为8.98px * 21px:

这意味着,如果你设计你的表格宋体8磅(13像素高),您需要缩放的形式,所有的子控件,通过162%:
scaleFactor = (userFontSize / designedFontSize)
= 21px / 13px
= 1.615
Run Code Online (Sandbox Code Playgroud)
如果您小心,您会注意到更改DPI只是更改字体大小的特殊情况.您的8pt字体仍为8pt,但8pt转换为更多像素.如果您运行131dpi(Windows 7中的136%缩放设置),则:
9pt Segoe UI, 96dpi = 6.81px x 15px
9pt Segoe UI, 131dpi = 8.98px x 21px
Run Code Online (Sandbox Code Playgroud)

注意:我选择
131dpi并12pt作为我的例子并非巧合.在工作中,我运行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;
Run Code Online (Sandbox Code Playgroud)
实际上,这段代码非常简单.许多子控件需要手动更新:
ParentFont = false需要调整字体的控件实际上,我们使用超级花哨的版本StandardizeFormFont,递归地遍历表单上的所有控件,并且最好根据它是什么来调整每个控件.
每个控件都是Delphi应该覆盖它的ScaleControl方法,并进行所需的调整:
protected
procedure ChangeScale(M, D: Integer); override;
Run Code Online (Sandbox Code Playgroud)
"自定义DPI设置"窗口具有"使用Windows XP样式DPI缩放".这可能解释了不同的行为.
War*_* P 6
事实证明,当系统DPI从默认的96 dpi值改变时,我的应用程序中的怪癖在三个一般阵营中:
某些应用程序的控件使用GDI,而某些控件使用GDI +.GDI和GDI +字体在不同DPI下的呈现方式存在一些差异,至少在我使用的控件中是这样.
我在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支持工作时,您的工作会变得更加困难.
反过来,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通用控制框架"的一部分.
总而言之,这些变化现在并没有那么不同(复杂),而在过去,Windows XP和其他版本的Windows为您提供了"字体大小"选择,而现在,Windows 7 UI体验试图埋葬字体点大小选项非常深入,而是为您提供"文本大小"更改用户界面,修改表面下方的系统DPI.这两个与至少一个主要的商业应用程序,它不看,或者用所产生的变化正常工作改变几乎每一个用户有问题,那么你的用户体验结果这些方式.随着超高点距显示在消费者PC领域变得越来越普遍,这个问题可能会变得越来越糟,并且需要围绕更合适的框架进行UI设计.