在 Delphi 中获取自定义 DPI 百分比
Posted
技术标签:
【中文标题】在 Delphi 中获取自定义 DPI 百分比【英文标题】:Getting custom DPI percentage in Delphi 【发布时间】:2011-10-22 11:12:57 【问题描述】:尝试在 Windows 7 中以高 DPI 模式让我的 Delphi 2010 应用程序更加用户友好我一直在尝试几种方法来检索 PixelsPerInch 并与 96 进行比较。唉,无论我尝试什么,我总是得到 96。我的问题是:
-
获得自定义 DPI 模式的最佳做法是什么?
无论我的分数是多少,我得到一个恒定的 96 是否意味着我错过了什么?
这是我尝试过的
dpiX := Form1.PixelsPerInch
和
dpiX := Screen.PixelsPerInch
最后:
D2DFactoryOptions.DebugLevel := D2D1_DEBUG_LEVEL_NONE;
pD2DFactoryOptions := @D2DFactoryOptions;
if D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
IID_ID2D1Factory,
PD2DFactoryOptions,
D2DFactory
) <> S_OK then exit;
D2DFactory.GetDesktopDpi(dpiX, dpiY)
想猜吗?没错,dpiX 在 100%、125% 和 150% 中是一个常数 96
请指教。
【问题讨论】:
【参考方案1】:我认为您需要通过在应用程序清单中包含以下内容来将您的应用程序标记为支持高 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>
here 提供了有关声明 DPI 意识的详细信息。
您目前似乎正在回退到所谓的DPI Virtualization。
【讨论】:
谢谢@david-heffernan,我完全同意。问题是,了解 DPI 意味着我必须自己处理所有的自定义,而且工作量很大……有没有更便宜的方法? 如果您在每个表单上都将 Scaled 设置为 True,那么 VCL 框架将缩放您的 GUI。我就是这么做的。 谢谢,页面已缩放,但我需要知道我处于高 DPI 状态,以便自定义控件。 一旦您将应用程序标记为 DPI 感知,Screen.PixelsPerInch
将返回 120、144 等。
@Altaveron 那么你为什么要否决我对另一个问题的回答?编写一个在非常高的 DPI 下运行良好的应用程序非常容易。你的没有。但我的有。那么,为什么要攻击这个答案呢?如果你愿意,我可以帮助你。【参考方案2】:
您可以在
查看注册表值 AppliedDPIHKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics\AppliedDPI
感谢 Andreas Rejbrand。
【讨论】:
这对于-检测-如果它是非标准设置当然很有用,处理事情可以在此之后进行。谢谢!【参考方案3】:你的申请是DPI aware吗?如果不是,请声明。
编辑:
如果您真的只想检测当前设置,但不想为整个应用程序启用 DPI 感知,那么最好的方法是编写一个小型(dpi 感知)应用程序,其唯一的工作就是进行这种检测。运行它并将结果传输到您的主应用程序。或者提供一个启动器,它检测然后运行主应用程序,将设置作为命令行参数提供。这当然只有在您的应用程序没有其他 API 或“秘密”调用来确定设置时,即使在运行 DPI 虚拟化时也是如此。
【讨论】:
您好 Heinrich,调用 SetProcessDPIAware 确实使 D2DFactory 能够获得正确的 DPI。 Screen.PixelPerInch 保持在 96,唉。 @asafadd 您可能不能尽早致电SetProcessDPIAware
。您可能需要它在 VCL 初始化之前运行。您可以通过将其放在不使用其他单元(我的意思是)的单元的初始化部分中并将其放在您的 .dpr 文件中来实现这一点。但是清单方法要好得多。遗憾的是,Delphi 生成的应用程序清单无法自定义。我总是链接我自己的,然后我就能得到我想要的。
@asafadd David 有一个观点 - 尝试早点致电 SetProcessDPIAware
。将它放在初始化部分的某个单元中,右键单击您的项目,单击查看源代码,将这个单元放在首位。这应该保证尽早初始化。
将它集成到我的清单中,它解决了我所有的问题。非常感谢!【参考方案4】:
因为 Delphi 的 IDE 甚至没有提供标记应用程序 DPI 感知的方法,正如 David 向您展示的那样,您需要制作自己的清单并向其中添加内容,并且因为进入 DPI 感知模式意味着您必须自己处理很多工作,你可能会发现这不值得。经过大约 100 个小时徒劳无功的工作后,我当然放弃了尝试。
如果您的应用使用 100% 通用控件,并且您不介意重新设计所有其他控件,直到它们对您正常工作,那么就去做吧。
一旦关闭 DPI 虚拟化,即使是基于通用控件的控件(例如页面控件)在高 dpi 环境中似乎也无法正常工作(至少它们的 VCL 包装器无法正常工作)。
短版:这些天的最佳实践是,除非绝对必要,否则根本不要打扰 VCL 应用程序,然后,您不妨留出时间来重写和修复 VCL 中的每个控件和每个第三方控件,解决故障。
【讨论】:
让我看看我是否明白了:不要使用 Delphi,因为它缺乏对高 DPI 的良好支持? 我不同意我的应用程序使用普通的 Vcl 控件并在 200% dpi 下正常工作。我需要做的唯一额外工作是确保我的应用程序范围的图像列表加载了适当大小的图标资源。 你的 UI 控件一定比我的简单。 :-) 我所说的只是“我遇到了很多麻烦,你也可能”。图像尺寸在布局问题列表中的位置太低了,以至于我什至没有关心在按钮上使用小图像。 @asafadd:德尔福并不孤单。大多数框架(WPF 除外)在 Windows 高 DPI 模式下都有很多问题。我的看法是设计糟糕的是 Windows,而不是 Delphi。 @Warren 不要责怪你的工具!当然,对于不同的 DPI 模式,您需要有不同的图像。将您的图像放在作为资源链接的图标中,并在运行时将最佳尺寸加载到图像列表中。我的解决方案涉及在低级别集中管理的单个应用程序范围的图像列表。我可以给你一个 200% DPI 的漂亮屏幕截图,你喜欢吗!【参考方案5】:不响应用户的DPI设置。
响应用户的字体偏好。
如果您只响应用户的 DPI 设置,那么如果用户使用比您在设计时使用的更大的字体,您的表单将不会变大,例如
您使用 8pt MS Sans Serif 或 Tahoma 进行设计 用户正在运行 Windows Vista 或 Windows 7(9pt Segoe UI) 你在我的电脑上运行(14pt Segoe UI)通过响应字体大小(以像素为单位),您可以免费获得高 DPI 支持(并且您正在成为一名优秀的开发人员!):
8pt Tahoma @96dpi:13px
Windows 2000/XP(你设计的)
8pt Tahoma @131dpi: 15px
(将设计的表单缩放 115%)
9pt Segue UI @96dpi: 15px
Windows Vista/7(将您设计的表单缩放 115%)
9pt Segoe UI@131dp:16px
我的家用机(将表单缩放 123%)
12pt Tahoma @96dpi: 16px
我的工作机器(将您设计的表格缩放 123%)
您想关闭所有表单的Scaled
属性;所以他们不会尝试响应 dpi 设置。您需要与 AutoScaleMode.Font
等效的 .NET WinForms。
是的,一旦您缴纳了税款,并且可以处理不同的 DPI 设置,请使用清单声明您的应用程序 highDpi 感知 - 正如其他答案所给出的那样。
不要使用SetProcessDPIAware
,因为DLLs that you depend on who already read the current DPI可能为时已晚:
注意 如果 DLL 在初始化期间缓存 dpi 设置,SetProcessDPIAware 可能会出现竞争条件。因此,建议通过应用程序 (.exe) 清单设置 dpi-aware,而不是通过调用 SetProcessDPIAware。
【讨论】:
虽然您使用的是 DirectX 而不是DFM
表单,但这些概念仍然有效。它们的概念也适用于 Windows 对话框、.NET WinForms、WPF 和 Direct2D。【参考方案6】:
如果它对某人有帮助, 我注意到 Screen->PrimaryMonitor->PixelsPerInch 确实会根据显示缩放比例发生变化。
【讨论】:
以上是关于在 Delphi 中获取自定义 DPI 百分比的主要内容,如果未能解决你的问题,请参考以下文章
Power Query M - 使用自定义聚合(百分位)按列值分组
高 DPI Delphi 程序中的 Sisulizer 运行时变化
如何获取自定义单元格并通过 indexpath 使用它的自定义项 - swift3?