我为一家小型企业创建了一个应用程序。办公室中的一些员工无法正确查看表格。原因是他们将 DPI 设置设置为 96dpi 以上。有人知道控制这种情况的方法吗?
对于所有使用过 winforms 应用程序的人来说,如何控制表单布局以使 DPI 不会影响应用程序的外观?
假设您不尝试尊重用户的 UI 字体选择 (SystemFonts.IconTitleFont),并且仅将表单硬编码为一种字体大小(例如 Tahoma 8pt、Microsoft Sans Serif 8.25pt),您可以将表单设置AutoScaleMode
为ScaleMode.Dpi
.
这将通过调用来缩放窗体和大部分子控件的大小,这反过来又在自身和所有子控件上递归调用受保护的方法。将根据新缩放因子的需要增加控件的位置、大小、字体等。CurrentDpiSetting / 96
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); } } }
如果您只是使用控件,这一切都很好。但是,如果您曾经使用任何硬编码的像素大小,则需要通过表单的当前比例因子来缩放像素的宽度和长度。可能具有硬编码像素大小的一些情况示例:
如果是这种情况,您需要通过“当前缩放因子”来缩放这些硬编码值。遗憾的是没有提供“当前”比例因子,我们需要自己记录。解决方案是假设最初缩放因子为 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);
}
}
最初的比例因子是1.0
。如果 form 然后按 缩放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%
使用a SizeF
(而不是单个浮点值)的原因是缩放量在 x 和 y 方向上可能不同。如果表单设置为ScaleMode.Font
,则表单将缩放为新的字体大小。字体可以有不同的纵横比(例如 Segoe UI的字体比Tahoma高)。这意味着您必须独立缩放 x 和 y 值。
因此,如果您想在 location 放置一个控件(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;
如果您要选择字体大小,这同样适用:
Font f = new Font("Segoe UI", 8, GraphicsUnit.Point);
必须变成:
Font f = new Font("Segoe UI", 8.0*this.scaleFactor.Width, GraphicsUnit.Point);
将 32x32 图标提取到位图将变为:
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 显示是所有开发者都应该缴纳的税。但没人愿意这样做的事实就是为什么微软放弃并在 Vista 中增加了图形卡扩展任何不能正确处理高 dpi 的应用程序的能力。
通过全局搜索/替换将 AutoScaleMode 设置为随处继承(即所有用户控件),然后在主窗体上将 AutoScaleMode 设置为 Dpi。
我还发现在这种情况下布局容器比锚点效果更好。
我知道这有点激烈,但请考虑在 WPF 中重写您的应用程序。WPF 应用程序在每个 DPI 设置上都具有相同的外观。