简体   繁体   English

如何在.NET WinForms应用程序中控制字体DPI

[英]How to control the font DPI in .NET WinForms app

I created an app for a small business. 我为小型企业创建了一个应用程序。 Some of the employees in the office can not see the form correctly. 办公室的一些员工无法正确查看表格。 The reason is they have their DPI setting set to above 96dpi. 原因是他们的DPI设置设置为96dpi以上。 Does anybody know of a way to control this? 有人知道控制这个的方法吗?

For all of you who have experience with winforms apps, how do you control your form layout so that DPI does not affect the look of the application? 对于有winforms应用程序经验的所有人,如何控制表单布局以使DPI不影响应用程序的外观?

Assuming you do not try to honor the user's UI font choice (SystemFonts.IconTitleFont), and hard-code your forms for one font size only (eg Tahoma 8pt, Microsoft Sans Serif 8.25pt), you can set your form's AutoScaleMode to ScaleMode.Dpi . 假设您没有尝试遵守用户的UI字体选择(SystemFonts.IconTitleFont),并且只对一种字体大小的表单进行硬编码(例如Tahoma 8pt,Microsoft Sans Serif 8.25pt),您可以将表单的AutoScaleMode设置为ScaleMode.Dpi

This will scale the size of the form and most of it child controls by the factor CurrentDpiSetting / 96 by calling Form.Scale() , which in turns calls the protected ScaleControl() method recursivly on itself and all child controls. 这将通过调用Form.Scale()来扩展表单的大小和大多数子控件的因子CurrentDpiSetting / 96 ,而Form.Scale()对自身和所有子控件进行递归调用受保护的ScaleControl()方法。 ScaleControl will increase a control's position, size, font, etc as needed for the new scaling factor. ScaleControl将根据新缩放因子的需要增加控件的位置,大小,字体等。

Warning: Not all controls properly scale themselves. 警告:并非所有控件都能正确缩放。 The columns of a listview, for example, will not get wider as the font gets larger. 例如,列表视图的列不会随着字体变大而变宽。 In order to handle that you'll have to manually perform additional scaling as required. 为了处理这个问题,您必须根据需要手动执行其他扩展。 i do this by overriding the protected ScaleControl() method, and scaling the listview columns manually: 我这样做是通过重写受保护的ScaleControl()方法,并手动缩放listview列:

 public class MyForm : Form { protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { base.ScaleControl(factor, specified); Toolkit.ScaleListViewColumns(listView1, factor); } } public class Toolkit { /// <summary> /// Scale the columns of a listview by the Width scale factor specified in factor /// </summary> /// <param name="listview"></param> /// <param name="factor"></param> /// <example>/* /// protected override void ScaleControl(SizeF factor, BoundsSpecified specified) /// { /// base.ScaleControl(factor, specified); /// /// //ListView columns are not automatically scaled with the ListView, so we /// //must do it manually /// Toolkit.ScaleListViewColumns(lvPermissions, factor); /// } ///</example> public static void ScaleListViewColumns(ListView listview, SizeF factor) { foreach (ColumnHeader column in listview.Columns) { column.Width = (int)Math.Round(column.Width * factor.Width); } } } 

This is all well and good if you're just using controls. 如果您只是使用控件,这一切都很好。 But if you ever use any hard-coded pixel sizes, you'll need to scale your pixel widths and lengths by the current scale factor of the form. 但是,如果您使用任何硬编码像素大小,则需要根据表单的当前比例因子缩放像素宽度和长度。 Some examples of situations that could have hard-coded pixel sizes: 可能具有硬编码像素大小的情况的一些示例:

  • drawing a 25px high rectangle 绘制一个25像素高的矩形
  • drawing an image at location (11,56) on the form 在表单上的位置(11,56)绘制图像
  • stretch drawing an icon to 48x48 拉伸图标到48x48
  • drawing text using Microsoft Sans Serif 8.25pt 使用Microsoft Sans Serif 8.25pt绘制文本
  • getting the 32x32 format of an icon and stuffing it into a PictureBox 获取图标的32x32格式并将其填充到PictureBox中

If this is the case, you'll need to scale those hard-coded values by the " current scaling factor ". 如果是这种情况,您需要通过“ 当前比例因子 ”来缩放这些硬编码值。 Unfortunatly the "current" scale factor is not provided, we need to record it ourselves. 不幸的是,没有提供“当前”比例因子,我们需要自己记录。 The solution is to assume that initially the scaling factor is 1.0 and each time ScaleControl() is called, modify the running scale factor by the new factor. 解决方案是假设最初缩放因子为1.0并且每次调用ScaleControl() ,通过新因子修改运行比例因子。

public class MyForm : Form
{
   private SizeF currentScaleFactor = new SizeF(1f, 1f);

   protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
   {
      base.ScaleControl(factor, specified);

      //Record the running scale factor used
      this.currentScaleFactor = new SizeF(
         this.currentScaleFactor.Width * factor.Width,
         this.currentScaleFactor.Height * factor.Height);

      Toolkit.ScaleListViewColumns(listView1, factor);
   }
}

Initially the scaling factor is 1.0 . 最初缩放因子是1.0 If form is then scaled by 1.25 , the scaling factor then becomes: 如果然后将形式缩放1.25 ,则缩放因子变为:

1.00 * 1.25 = 1.25    //scaling current factor by 125%

If the form is then scaled by 0.95 , the new scaling factor becomes 如果表格随后缩放0.95 ,则新的缩放因子变为

1.25 * 0.95 = 1.1875  //scaling current factor by 95%

The reason a SizeF is used (rather than a single floating point value) is that scaling amounts can be different in the x and y directions. 使用SizeF (而不是单个浮点值)的原因是缩放量在x和y方向上可以不同。 If a form is set to ScaleMode.Font , the form is scaled to the new font size. 如果表单设置为ScaleMode.Font ,表单将缩放为新的字体大小。 Fonts can have different aspect ratios ( eg Segoe UI is taller font than Tahoma ). 字体可以具有不同的宽高比( 例如, Segoe UITahoma更高的字体)。 This means you have to scale x and y values independantly. 这意味着您必须独立缩放x和y值。

So if you wanted to place a control at location (11,56) , you would have to change your positioning code from: 因此,如果您想将控件放在位置(11,56) ,则必须更改定位代码:

Point pt = new Point(11, 56);
control1.Location = pt;

to

Point pt = new Point(
      (int)Math.Round(11.0*this.scaleFactor.Width),
      (int)Math.Round(56.0*this.scaleFactor.Height));
control1.Location = pt;

The same applies if you were going to pick a font size: 如果您要选择字体大小,则同样适用:

Font f = new Font("Segoe UI", 8, GraphicsUnit.Point);

would have to become: 必须成为:

Font f = new Font("Segoe UI", 8.0*this.scaleFactor.Width, GraphicsUnit.Point);

And extracting a 32x32 icon to a bitmap would change from: 并将32x32图标提取到位图将改为:

Image i = new Icon(someIcon, new Size(32, 32)).ToBitmap();

to

Image i = new Icon(someIcon, new Size(
     (int)Math.Round(32.0*this.scaleFactor.Width), 
     (int)Math.Round(32.0*this.scaleFactor.Height))).ToBitmap();

etc. 等等

Supporting non-standard DPI displays is a tax that all developers should pay . 支持非标准DPI显示是所有开发者应该支付 But the fact that nobody wants to is why Microsoft gave up and added to Vista the ability for the graphics card to stretch any applications that don't say they properly handle high-dpi . 但事实上,没有人愿意为什么微软放弃并增加了Vista的能力,使显卡能够扩展任何不能正确处理高dpi的应用程序

Set the AutoScaleMode to Inherit everywhere (ie all your UserControls) via a global search/replace, then set the AutoScaleMode to Dpi on your main form. 通过全局搜索/替换将AutoScaleMode设置为在任何地方继承(即所有UserControl),然后在主窗体上将AutoScaleMode设置为Dpi。

I also find that layout containers work better than anchors for this type of situation. 我还发现布局容器比这种情况下的锚更好。

I know it's somewhat drastic, but consider to rewrite your app in WPF. 我知道它有点激烈,但考虑在WPF中重写你的应用程序。 WPF applications have the same look on every DPI setting. WPF应用程序在每个DPI设置上具有相同的外观。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM