如何在.NET WinForms应用程序中控制字体DPI
我创build了一个小企业的应用程序。 办公室的一些员工无法正确地看到表格。 原因是他们的DPI设置为96dpi以上。 有谁知道一个方法来控制这个?
对于所有对winforms应用程序有经验的人,你如何控制你的表单布局,使DPI不影响应用程序的外观?
假设您不尝试遵守用户的UI字体select(SystemFonts.IconTitleFont),并且仅对一种字体大小(例如Tahoma 8pt,Microsoft Sans Serif 8.25pt)对表单进行硬编码,则可以将表单的AutoScaleMode
设置为ScaleMode.Dpi
。
这将通过调用Form.Scale()
来缩放表单的大小和大部分子控件的大小,通过调用Form.Scale()
来调用本身和所有子控件的受保护的ScaleControl()
方法。 ScaleControl
将根据新缩放因子的需要增加控件的位置,大小,字体等。
警告:并非所有的控件都能正确缩放。 例如,列表视图的列不会越来越宽,因为字体变大。 为了处理这个问题,您必须根据需要手动执行额外的缩放。 我通过覆盖受保护的
ScaleControl()
方法,并手动缩放列表视图列来做到这一点: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); } } }
如果你只是使用控件,这一切都很好。 但是,如果您使用任何硬编码的像素大小,则需要使用表单的当前比例因子缩放像素的宽度和长度。 可能有硬编码像素大小的情况的一些示例:
- 画一个25px高的矩形
- 在表单上的位置(11,56)处绘制图像
- 拉伸一个图标到48×48
- 使用Microsoft Sans Serif 8.25pt绘制文本
- 获取图标的32×32格式并将其填充到PictureBox中
如果是这种情况,则需要通过“ 当前比例因子 ”来缩放这些硬编码值。 不幸的是,“当前”的比例因子没有提供,我们需要自己logging。 解决的办法是假定最初的缩放因子是1.0,每次调用ScaleControl()
时, 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); } }
最初的缩放因子是1.0
。 如果表单被缩放1.25
,那么缩放因子变成:
1.00 * 1.25 = 1.25 //scaling current factor by 125%
如果表单被缩放0.95
,则新的缩放因子变成
1.25 * 0.95 = 1.1875 //scaling current factor by 95%
使用SizeF
(而不是单个浮点值)的原因是缩放量在x和y方向上可能不同。 如果窗体设置为ScaleMode.Font
,窗体将缩放到新的字体大小。 字体可以有不同的高宽比( 例如, Segoe UI比Tahoma字体高)。 这意味着您必须独立地缩放x和y值。
所以如果你想在位置(11,56)
放置一个控件,你将不得不从下面的位置改变你的位置代码:
Point pt = new Point(11, 56); control1.Location = pt;
至
Point pt = new Point( (int)Math.Round(11.0*this.scaleFactor.Width), (int)Math.Round(56.0*this.scaleFactor.Height)); control1.Location = pt;
如果您要select字体大小,也是如此:
Font f = new Font("Segoe UI", 8, GraphicsUnit.Point);
将不得不成为:
Font f = new Font("Segoe UI", 8.0*this.scaleFactor.Width, GraphicsUnit.Point);
并提取32×32图标到位图将从以下更改:
Image i = new Icon(someIcon, new Size(32, 32)).ToBitmap();
至
Image i = new Icon(someIcon, new Size( (int)Math.Round(32.0*this.scaleFactor.Width), (int)Math.Round(32.0*this.scaleFactor.Height))).ToBitmap();
等等
支持非标DPI显示是所有开发者应该支付的税 。 但事实上,没有人愿意为什么微软放弃,并join到Vista的能力,显卡扩展任何应用程序,不要说,他们正确处理高DPI 。
将AutoScaleMode设置为通过全局search/replace随处(即所有用户控件)inheritance,然后将AutoScaleMode设置为主窗体上的Dpi。
我也发现在这种情况下,布局容器比锚更好。
我知道这是有点激烈,但考虑重写WPF中的应用程序。 WPF应用程序在每个DPI设置上都有相同的外观。