如何编写自动扩展到系统字体和dpi设置的WinForms代码?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何编写自动扩展到系统字体和dpi设置的WinForms代码?相关的知识,希望对你有一定的参考价值。
简介:有很多评论说“WinForms不能自动扩展到DPI /字体设置;切换到WPF”。但是,我认为这是基于.NET 1.1;看起来他们在.NET 2.0中实现自动扩展实际上做得非常好。至少基于我们迄今为止的研究和测试。但是,如果你们中的一些人知道的更好,我们很乐意听取你的意见。 (请不要打扰我们应该切换到WPF ...现在不是一个选项。)
Questions:
- WinForms中的内容不能正确自动缩放,因此应该避免?
- 程序员在编写WinForms代码时应遵循哪些设计指南,以便自动扩展?
Design Guidelines we have identified so far:
这些是不正确还是不充分?我们应采用的其他指导方针?是否还有其他需要避免的模式?对此的任何其他指导将非常感激。
Controls which do not support scaling properly:
Label
与AutoSize = False
和Font
继承。在控件上显式设置Font
,使其在“属性”窗口中以粗体显示。ListView
列宽不规模。覆盖表单的ScaleControl
来代替。见this answerSplitContainer
的Panel1MinSize
,Panel2MinSize
和SplitterDistance
属性TextBox
与MultiLine = True
和Font
继承。在控件上显式设置Font
,使其在“属性”窗口中以粗体显示。ToolStripButton
的形象。在窗体的构造函数中: 设置ToolStrip.AutoSize = False
根据ToolStrip.ImageScalingSize
和CreateGraphics.DpiX
设置.DpiY
如果需要,设置ToolStrip.AutoSize = True
。 有时AutoSize
可以留在True
,但有时如果没有这些步骤就无法调整大小。没有改变的工作与.NET Framework 4.5.2和EnableWindowsFormsHighDpiAutoResizing
。TreeView
的图像。根据ImageList.ImageSize
和CreateGraphics.DpiX
设置.DpiY
。对于StateImageList
,没有这个的作品会随着.NET Framework 4.5.1和EnableWindowsFormsHighDpiAutoResizing
的变化而变化。Form
的大小。创建后手动缩放固定大小的Form
。
Design Guidelines:
- 所有ContainerControl必须设置为相同的
AutoScaleMode = Font
。 (字体将处理DPI更改和系统字体大小设置的更改; DPI将仅处理DPI更改,而不是更改系统字体大小设置。) - 所有ContainerControls也必须使用
AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
设置,假设为96dpi(参见下一个项目符号)。这是由设计人员根据您打开设计器的DPI自动添加的......但是我们许多最古老的设计器文件中都缺少这些。也许Visual Studio .NET(VS 2005之前的版本)没有正确地添加它。 - 你所有的设计师都以96dpi工作(我们可能会切换到120dpi;但互联网上的智慧说要坚持96dpi;实验是有序的;按设计,它应该无关紧要因为它只是改变了
AutoScaleDimensions
线设计师插入)。要将Visual Studio设置为在高分辨率显示器上以虚拟96dpi运行,请找到其.exe文件,右键单击以编辑属性,然后在兼容性下选择“覆盖高DPI缩放行为。缩放执行:系统”。 - 确保您从未在容器级别设置Font ...仅在叶控件上。 (在容器上设置字体似乎会关闭该容器的自动缩放。)
- 不要使用Anchor
Right
或Bottom
锚定到UserControl ...它的定位不会自动缩放;相反,将Panel或其他容器放入UserControl并将其他控件锚定到该Panel;让Panel在您的UserControl中使用DockRight
或DockBottom
。 - 只有在调用
ResumeLayout
末尾的InitializeComponent
时控件列表中的控件才会自动缩放...如果你动态添加控件,那么在添加它之前你需要在该控件上使用SuspendLayout();
AutoScaleDimensions = new SizeF(6F, 13F);
AutoScaleMode = AutoScaleMode.Font;
ResumeLayout();
。你的定位如果您不使用Dock模式或FlowLayoutPanel
或TableLayoutPanel
等布局管理器,也需要进行调整。 - 从
ContainerControl
派生的基类应该将AutoScaleMode
设置为Inherit
(类ContainerControl
中设置的默认值;但不是设计者设置的默认值)。如果你把它设置为其他任何东西,然后你的派生类试图将它设置为Font(就像它应该),那么设置为Font
的行为将清除设计者的AutoScaleDimensions
设置,从而导致实际切换自动缩放! (此指南与前一个指南相结合意味着您永远无法在设计器中实例化基类...所有类都需要设计为基类或叶类!) - 避免在Designer中静态使用
Form.MaxSize
。表格上的MinSize
和MaxSize
不像其他任何东西那样扩展。所以,如果你以96dpi完成所有工作,那么当DPI较高时你的MinSize
不会引起问题,但可能没有你预期的限制,但你的MaxSize
可能会限制你的尺寸缩放,这可能会导致问题。如果你想要MinSize == Size == MaxSize
,不要在Designer中这样做...在构造函数或OnLoad
覆盖中执行此操作...将MinSize
和MaxSize
设置为适当缩放的大小。 - 特定
Panel
或Container
上的所有控件应使用锚定或对接。如果你混合它们,那个Panel
所做的自动缩放通常会以微妙的奇怪方式行为不端。
我的经验与当前最高投票的答案完全不同。通过逐步完成.NET框架代码并仔细阅读参考源代码,我得出结论,所有内容都适用于自动扩展工作,并且只有一个微妙的问题在某处弄乱它。事实证明这是真的。
如果您创建一个正确可重排/自动调整大小的布局,那么几乎所有内容都应该自动运行,使用Visual Studio使用的默认设置(即,父窗体上的AutoSizeMode = Font,以及其他所有内容的Inherit)。
唯一的问题是,如果您在设计器中的表单上设置了Font属性。生成的代码将按字母顺序对赋值进行排序,这意味着AutoScaleDimensions
将在Font
之前分配。不幸的是,这完全打破了WinForms自动缩放逻辑。
修复很简单。要么根本不在设计器中设置Font
属性(在表单构造函数中设置它),要么手动重新排序这些赋值(但是每次在设计器中编辑表单时都必须继续这样做)。 Voila,几乎完美和全自动缩放,最小的麻烦。甚至表单大小也可以正确缩放。
我会在遇到它们时列出已知问题:
- 嵌套的
TableLayoutPanel
calculates control margins incorrectly。没有已知的解决方法,完全避免边距和填充 - 或避免嵌套的表布局面板。
针对.Net Framework 4.7的应用程序,并在Windows 10 v1703(Creators Update Build 15063)下运行它。随着.Net 4.7 under Windows 10 (v1703), MS made a lot of DPI improvements。
从.NET Framework 4.7开始,Windows窗体包括对常见高DPI和动态DPI方案的增强。这些包括:
- 改进了许多Windows窗体控件的缩放和布局,例如MonthCalendar控件和CheckedListBox控件。
- 单通缩放。在.NET Framework 4.6及更早版本中,通过多次传递执行缩放,这导致某些控件的缩放比必要的更多。
- 支持动态DPI方案,其中用户在启动Windows窗体应用程序后更改DPI或比例因子。
要支持它,请向应用程序添加应用程序清单,并表明您的应用程序支持Windows 10:
<compatibility xmlns="urn:schemas-microsoft.comn:compatibility.v1">
<application>
<!-- Windows 10 compatibility -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
接下来,添加一个app.config
并声明应用程序Per Monitor Aware。这是现在在app.config中完成而不是像以前一样在清单中完成!
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
自Windows 10 Creators更新以来,这个PerMonitorV2是新的:
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
也称为Per Monitor v2。优于原始每监视器DPI感知模式的进步,使应用程序能够在每个顶级窗口的基础上访问新的DPI相关缩放行为。
- 子窗口DPI更改通知 - 在Per Monitor v2上下文中,将通知整个窗口树发生的任何DPI更改。
- 扩展非客户区域 - 所有窗口将自动以DPI敏感方式绘制非客户区域。不需要调用EnableNonClientDpiScaling。
- 扩展Win32菜单 - 在Per Monitor v2上下文中创建的所有NTUSER菜单都将按监视器方式进行扩展。
- Dialog Scaling - 在Per Monitor v2上下文中创建的Win32对话框将自动响应DPI更改。
- 改进了comctl32控件的缩放 - 各种comctl32控件改进了Per Monitor v2上下文中的DPI缩放行为。
- 改进了主题行为 - 在Per Monitor v2窗口的上下文中打开的UxTheme句柄将根据与该窗口关联的DPI进行操作。
现在,您可以订阅3个新事件以获得有关DPI更改的通知:
- Control.DpiChangedAfterParent,被触发在控件的DPI设置在其父控件或窗体发生DPI更改事件后以编程方式更改时发生。
- Control.DpiChangedBeforeParent,在控件的DPI设置在其父控件或窗体的DPI更改事件发生之前以编程方式更改时触发。
- Form.DpiChanged,当DPI设置在当前显示表单的显示设备上更改时触发。
您还有3个关于DPI处理/缩放的辅助方法:
- Control.LogicalToDeviceUnits,它将值从逻辑转换为设备像素。
- Control.ScaleBitmapLogicalToDevice,它将位图图像缩放为设备的逻辑DPI。
- Control.DeviceDpi,返回当前设备的DPI。
如果你仍然看到问题,你可以opt-out of the DPI improvements via app.config entries。
如果您无权访问源代码,可以在Windows资源管理器中转到应用程序属性,转到兼容性并选择System (Enhanced)
它激活GDI缩放以改善DPI处理:
对于基于GDI的Windows应用程序,现在可以在每个监视器的基础上对DPI进行扩展。这意味着,这些应用程序将神奇地成为每个监视器DPI感知的。
执行所有这些步骤,您应该为WinForms应用程序获得更好的DPI体验。但请记住,您需要将应用程序定位到.net 4
以上是关于如何编写自动扩展到系统字体和dpi设置的WinForms代码?的主要内容,如果未能解决你的问题,请参考以下文章
Win10 字体模糊解决(DPI缩放禁用),设置默认输入法英文
如何配置应用程序以在具有高 DPI 设置(例如 150%)的机器上正确运行?
如何配置应用程序以在具有高 DPI 设置(例如 150%)的机器上正确运行?