[C#] 如何编写自动缩放到系统字体和dpi设置的WinForms代码?


Answers

我的经验与目前最高票数的答案相当不同。 通过介绍.NET框架代码并仔细阅读参考源代码,我得出结论认为,所有工作都可以自动扩展以适应工作,并且在某处将其搞乱。 事实证明这是事实。

如果您创建了适当的可自动回流/自动大小的布局,则几乎所有的工作都会自动使用Visual Studio使用的默认设置(即,AutoSizeMode =父窗体上的字体,以及其他所有内容上的继承)。

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

虽然修复很简单。 要么根本不在设计器中设置Font属性(将其设置在窗体构造函数中),要么手动重新排序这些赋值(但是每次在设计器中编辑窗体时都必须保持这样做)。 瞧,几乎完美和全自动缩放与最小的麻烦。 即使表格尺寸正确缩放。

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

  • 嵌套的TableLayoutPanel 错误地计算控件边距 。 没有已知的解决办法,因为没有完全避免边距和填充 - 或者避免嵌套的表格布局面板。
Question

简介:那里有很多评论说“WinForms不能自动扩展到DPI /字体设置;切换到WPF”。 不过,我认为这是基于.NET 1.1的; 看起来他们在.NET 2.0中实现了自动扩展的功能。 至少基于我们迄今为止的研究和测试。 但是,如果你们中有些人知道的更好,我们很乐意听到你的消息。 (请不要争论我们应该切换到WPF ...这不是一个选项。)

问题:

  • WinForms中的什么不能自动缩放,因此应该避免?

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

我们已经确定的设计指南迄今为止:

请参阅下面的社区wiki答案 。

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




将您的应用程序定位到.Net Framework 4.7并在Windows 10 v1703(Creators Update Build 15063)下运行。 在Windows 10(v1703)下的.Net 4.7中,MS进行了大量的DPI改进

从.NET Framework 4.7开始,Windows Forms包含对常见高DPI和动态DPI方案的增强。 这些包括:

  • 多个Windows窗体控件(如MonthCalendar控件和CheckedListBox控件)的缩放和布局方面的改进。

  • 单通缩放。 在.NET Framework 4.6及更早版本中,缩放是通过多遍执行的,这导致一些控件的缩放比必要的更多。

  • 支持在Windows Forms应用程序启动后用户更改DPI或缩放因子的动态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并声明每个监视器意识的应用程序。 这是现在在app.config中完成,而不是像以前一样在清单中!

<System.Windows.Forms.ApplicationConfigurationSection>
   <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection> 

这个PerMonitorV2是Windows 10创建者更新以来的新增功能:

DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2

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

  • 子窗口DPI更改通知 - 在每个监视器v2上下文中,将通知整个窗口树发生任何DPI更改。

  • 缩放非客户区域 - 所有窗口都会自动将他们的非客户区域以DPI敏感的方式绘制。 调用EnableNonClientDpiScaling是不必要的。

  • Win32菜单的调整 - 在Per Monitor v2上下文中创建的所有NTUSER菜单都将按照监视器的方式进行缩放。

  • 对话框缩放 - 在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中,我们可能会获得更多改进。




除了锚点不能很好地工作之外,我会更进一步说,精确定位(即使用Location属性)在字体缩放方面效果不佳。 我不得不在两个不同的项目中解决这个问题。 在他们中,我们都必须将所有WinForms控件的定位转换为使用TableLayoutPanel和FlowLayoutPanel。 使用TableLayoutPanel中的Dock(通常设置为Fill)属性效果很好,并且可以很好地与系统字体DPI一起缩放。