如何编写自动扩展到系统字体和dpi设置的WinForms代码?

Bri*_*edy 138 .net c# fonts dpi winforms

简介:有很多评论说"WinForms不能自动扩展到DPI /字体设置;切换到WPF".但是,我认为这是基于.NET 1.1; 看起来他们在.NET 2.0中实现自动扩展实际上做得非常好.至少基于我们迄今为止的研究和测试.但是,如果你们中的一些人知道的更好,我们很乐意听取你的意见.(请不要打扰我们应该切换到WPF ...现在不是一个选项.)

问题:

  • WinForms中的内容不能正确自动缩放,因此应该避免?

  • 编写WinForms代码时程序员应遵循哪些设计指南,以便自动扩展?

到目前为止我们已确定的设计指南:

请参阅下面的社区维基答案.

这些是不正确还是不充分?我们应采用的其他指导方针?是否还有其他需要避免的模式?对此的任何其他指导将非常感激.

kjb*_*tel 117

不支持正确缩放的控件:

  • LabelAutoSize = FalseFont继承.显式设置Font控件,使其在"属性"窗口中以粗体显示.
  • ListView列宽不会缩放.覆盖表单ScaleControl来代替.看到这个答案
  • SplitContainerPanel1MinSize,Panel2MinSizeSplitterDistance属性
  • TextBoxMultiLine = TrueFont继承.显式设置Font控件,使其在"属性"窗口中以粗体显示.
  • ToolStripButton的形象.在窗体的构造函数中:

    • ToolStrip.AutoSize = False
    • ToolStrip.ImageScalingSize根据CreateGraphics.DpiX和设置.DpiY
    • ToolStrip.AutoSize = True如果需要,设置.

    有时候AutoSize可以留下来True但有时候如果没有这些步骤就无法调整大小.使用.NET Framework 4.5.2EnableWindowsFormsHighDpiAutoResizing.

  • TreeView的图像.ImageList.ImageSize根据CreateGraphics.DpiX和设置.DpiY.对于StateImageList,使用.NET Framework 4.5.1EnableWindowsFormsHighDpiAutoResizing.
  • Form的大小.Form创建后手动缩放固定大小.

设计指南:

  • 所有ContainerControl必须设置为相同AutoScaleMode = Font.(字体将处理DPI更改和系统字体大小设置的更改; DPI将仅处理DPI更改,而不是更改系统字体大小设置.)

  • AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);假设96dpi,所有ContainerControls也必须设置(参见下一个项目符号).这是由设计人员根据您打开设计器的DPI自动添加的......但是我们许多最古老的设计器文件中都缺少这些.也许Visual Studio .NET(VS 2005之前的版本)没有正确地添加它.

  • 你所有的设计师都以96dpi工作(我们可能会切换到120dpi;但互联网上的智慧说要坚持96dpi;实验是有序的;根据设计,它应该无关紧要,因为它只是改变了AutoScaleDimensions那条线设计师插入).要将Visual Studio设置为在高分辨率显示器上以虚拟96dpi运行,请找到其.exe文件,右键单击以编辑属性,然后在兼容性下选择"覆盖高DPI缩放行为.缩放执行:系统".

  • 确保您从未在容器级别设置Font ...仅在叶控件上.(在容器上设置字体似乎会关闭该容器的自动缩放.)

  • 不要使用Anchor AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);Right锚定到UserControl ...它的定位不会自动缩放; 相反,将Panel或其他容器放入UserControl并将其他控件锚定到该Panel; 让Panel 在您的UserControl中使用Dock Bottom或Dock Right.

  • 只有在调用Bottom结束时控件列表中的控件Fill才会自动缩放...如果你动态添加控件,那么ResumeLayout InitializeComponent SuspendLayout(); AutoScaleDimensions = new SizeF(6F, 13F);在添加控件之前你需要在该控件上.你的定位也需要调整如果您没有使用Dock模式或布局管理器,请使用AutoScaleMode = AutoScaleMode.Font;ResumeLayout();.

  • 派生自的基类FlowLayoutPanelTableLayoutPanel设置为ContainerControl(在类中设置的默认值AutoScaleMode;但不是设计者设置的默认值).如果你把它设置为其他任何东西,然后你的派生类试图将它设置为Font(就像它应该),那么设置它的行为Inherit将清除设计者的设置ContainerControl,导致实际上切换自动缩放!(此指南与前一个指南相结合意味着您永远无法在设计器中实例化基类...所有类都需要设计为基类或叶类!)

  • 避免Font在Designer中静态使用/.AutoScaleDimensions并且Form.MaxSize在表格上不会像其他一切那样扩展.因此,如果你以96dpi完成所有工作,那么当DPI较高时你MinSize不会出现问题,但可能没有你想象的那么严格,但你MaxSize可能会限制你的尺寸缩放,这可能会导致问题.如果你想MinSize,不这样做,在设计......这样做,在你的构造或MaxSize覆盖......同时设置MinSize == Size == MaxSizeOnLoad你正确地缩放大小.

  • 所有控件在一个特定的MinSizeMaxSize应该要么使用锚泊或停靠.如果你混合使用它们,那么自动缩放Panel通常会以微妙的奇怪方式行为不当.


Rom*_*kov 25

我的经验与当前最高投票的答案完全不同.通过逐步完成.NET框架代码并仔细阅读参考源代码,我得出结论认为自动扩展工作的所有内容都已到位,并且只有一个微妙的问题在某处弄乱它.事实证明这是真的.

如果您创建一个正确可重排/自动调整大小的布局,那么几乎所有内容都应该自动完成,使用Visual Studio使用的默认设置(即,父窗体上的AutoSizeMode = Font,以及其他所有内容的Inherit).

唯一的问题是,如果您在设计器中的表单上设置了Font属性.生成的代码将按字母顺序对分配进行排序,这意味着AutoScaleDimensions将在之前 分配Font.不幸的是,这完全打破了WinForms自动缩放逻辑.

修复很简单.要么根本不在设计器中设置Font属性(在表单构造函数中设置它),要么手动重新排序这些赋值(但是每次在设计器中编辑表单时都必须继续这样做).Voila,几乎完美和全自动缩放,最小的麻烦.甚至表单大小也可以正确缩放.


我会在遇到它们时列出已知问题:

  • 嵌套TableLayoutPanel 计算控制边距不正确.没有已知的解决方法,完全避免边距和填充 - 或避免嵌套的表布局面板.


mag*_*981 20

针对.Net Framework 4.7的应用程序,并在Windows 10 v1703(Creators Update Build 15063)下运行它.在Windows 10(v1703)下使用.Net 4.7,MS进行了大量的DPI改进.

从.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>
Run Code Online (Sandbox Code Playgroud)

接下来,添加app.config并声明应用程序Per Monitor Aware.这是现在在app.config中完成而不是像以前一样在清单中完成!

<System.Windows.Forms.ApplicationConfigurationSection>
   <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection> 
Run Code Online (Sandbox Code Playgroud)

自Windows 10 Creators更新以来,此PerMonitorV2是新的:

DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2

也称为Per Monitor v2.优于原始每监视器DPI感知模式的进步,使应用程序能够在每个顶级窗口的基础上访问新的DPI相关缩放行为.

  • 子窗口DPI更改通知 - 在Per Monitor v2上下文中,将通知整个窗口树发生的任何DPI更改.

  • 扩展非客户区域 - 所有窗口将自动以DPI敏感方式绘制非客户区域.不需要调用EnableNonClientDpiScaling.

  • 小号的Win32菜单caling -在每个监视器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.

如果您仍然遇到问题,可以通过app.config条目选择退出DPI改进.

如果您无权访问源代码,则可以转到Windows资源管理器中的应用程序属性,转到兼容性并选择 System (Enhanced)

在此输入图像描述

它激活GDI缩放以改善DPI处理:

对于基于GDI的Windows应用程序,现在可以在每个监视器的基础上对DPI进行扩展.这意味着,这些应用程序将神奇地成为每个监视器DPI感知的.

执行所有这些步骤,您应该为WinForms应用程序获得更好的DPI体验.但请记住,您需要将应用程序定位到.net 4.7并至少需要Windows 10 Build 15063(创建者更新).在下一个Windows 10 Update 1709中,我们可能会获得更多改进.


Col*_*nic 12

我在工作中写的指南:

WPF在"设备无关单元"中工作,这意味着所有控件都可以完美地扩展到高dpi屏幕.在WinForms中,它需要更多的关注.

WinForms以像素为单位.文本将根据系统dpi进行缩放,但通常会通过未缩放的控件进行裁剪.要避免此类问题,您必须避免显式调整大小和定位.遵循以下规则:

  1. 无论您在何处找到它(标签,按钮,面板),都将AutoSize属性设置为True.
  2. 对于布局,使用FlowLayoutPanel(一个WPF StackPanel)和TableLayoutPanel(一个WPF网格)进行布局,而不是使用vanilla Panel.
  3. 如果您在高dpi机器上进行开发,Visual Studio设计师可能会感到沮丧.当您设置AutoSize = True时,它会将控件的大小调整为您的屏幕.如果控件具有AutoSizeMode = GrowOnly,则对于正常dpi的人,它将保持此大小,即.比预期更大.要解决此问题,请在具有正常dpi的计算机上打开设计器,然后单击鼠标右键,重置.

  • 对于可以在所有内容上调整AutoSize大小的对话框,将是一场噩梦,我不希望按钮越来越大,因为我在运行程序时手动增加对话框的大小。 (3认同)

小智 8

我发现很难让WinForms在高DPI下玩得很好.所以,我写了一个VB.NET方法来覆盖表单行为:

Public Shared Sub ScaleForm(WindowsForm As System.Windows.Forms.Form)
    Using g As System.Drawing.Graphics = WindowsForm.CreateGraphics
        Dim sngScaleFactor As Single = 1
        Dim sngFontFactor As Single = 1
        If g.DpiX > 96 Then
            sngScaleFactor = g.DpiX / 96
            'sngFontFactor = 96 / g.DpiY
        End If
        If WindowsForm.AutoScaleDimensions = WindowsForm.CurrentAutoScaleDimensions Then
            'ucWindowsFormHost.ScaleControl(WindowsForm, sngFontFactor)
            WindowsForm.Scale(sngScaleFactor)
        End If
    End Using
End Sub
Run Code Online (Sandbox Code Playgroud)


Kno*_*ech 6

我最近遇到了这个问题,特别是在高dpi系统上打开编辑器时,与Visual Studio重新缩放结合使用。我发现最好保留AutoScaleMode = Font,但是将Forms Font设置为默认字体,但是指定像素大小,而不是点,即:Font = MS Sans; 11px。在代码中,然后将字体重置为默认字体:Font = SystemFonts.DefaultFont一切都很好。

只是我的两分钱。我以为我可以分享,因为在Internet上找不到“保持AutoScaleMode = Font”“以像素为单位设置字体大小”

我的博客上有更多详细信息:http : //www.sgrottel.de/? p = 1581&lang= en


Bra*_*non 4

除了锚点工作得不太好之外:我会更进一步说,精确定位(也称为使用 Location 属性)对于字体缩放来说效果不佳。我必须在两个不同的项目中解决这个问题。在这两者中,我们必须将所有 WinForms 控件的位置转换为使用 TableLayoutPanel 和 FlowLayoutPanel。在 TableLayoutPanel 中使用 Dock(通常设置为 Fill)属性效果非常好,并且可以根据系统字体 DPI 进行良好缩放。