WinForms 的响应式设计技术






4.79/5 (12投票s)
设计自动适应不同屏幕分辨率
引言
创建响应式的 WinForms 应用程序并非易事。响应式意味着在不同屏幕分辨率下的可用性。对于 WinForms,我们需要根据分辨率显式处理控件的重排和重新定位。虽然有关于使用 WPF、控件的停靠和锚定、使用面板等的建议,但本文提供了一种为 WinForm 应用程序应用响应式的不同方法。
背景
我设计了一个游戏,可以在 这里 找到。我是在一台分辨率为 1920x1080 的机器上设计的。但当我尝试在笔记本电脑上玩时,发现面板超出了屏幕。我感到有必要使其响应式,以便能够触达可能具有不同分辨率的用户。因此,我修改了代码使其响应式。所以,我想也许提供一种应用 WinForm 应用程序响应式的替代方法可能会让其他人受益。
技巧
该技术很简单。它有两个硬编码的常量,用于保留设计时屏幕分辨率。现在,每当应用程序运行时,它都会获得一个乘数因子,该因子实际上是一个缩放因子。它通过将当前分辨率除以设计时分辨率来获得此因子。然后,将窗体的所有控件传递给此类对象以进行缩放和调整大小。
Using the Code
响应式类 - Responsive.cs
有一个名为 Responsive.cs 的类,它有 5 个成员变量,如下所示。成员的用途根据名称不言而喻。
float WIDTH_AT_DESIGN_TIME = (float)Convert.ToDouble
(ConfigurationManager.AppSettings["DESIGN_TIME_SCREEN_WIDTH"]);
float HEIGHT_AT_DESIGN_TIME = (float)Convert.ToDouble
(ConfigurationManager.AppSettings["DESIGN_TIME_SCREEN_HEIGHT"]);
Rectangle Resolution;
float WidthMultiplicationFactor;
float HeightMultiplicationFactor;
设计时屏幕分辨率保存在 App.config 文件中。
<add key ="DESIGN_TIME_SCREEN_WIDTH" value="1920"/>
<add key ="DESIGN_TIME_SCREEN_HEIGHT" value="1080"/>
创建类的实例时,会将当前分辨率传递给构造函数。之后,会调用该类的 SetMultiplicationFactor()
方法。此方法通过将当前分辨率除以设计时分辨率来获取缩放因子。
public Responsive(Rectangle ResolutionParam)
{
Resolution = ResolutionParam;
}
public void SetMultiplicationFactor()
{
WidthMultiplicationFactor = Resolution.Width / WIDTH_AT_DESIGN_TIME;
HeightMultiplicationFactor = Resolution.Height / HEIGHT_AT_DESIGN_TIME;
}
例如,该应用程序在 1920x1080 分辨率下设计。如果此应用程序在分辨率为 1024x768 的机器上运行,则 WidthMultiplicationFactor
和 HeightMultiplicationFactor
更改如下
WidthMultiplicationFactor = 1024/1920 = 0.533
HeightMultiplicationFactor = 768/1080 = 0.711
最后,有两个重载方法,它们是最终提供响应式解决方案(最佳尺寸、位置和字体大小)的方法,用于调用窗体的控件。
public int GetMetrics(int ComponentValue)
{
return (int)(Math.Floor(ComponentValue * WidthMultiplicationFactor));
}
public int GetMetrics(int ComponentValue, string Direction)
{
if (Direction.Equals("Width") || Direction.Equals("Left"))
return (int)(Math.Floor(ComponentValue * WidthMultiplicationFactor));
else if (Direction.Equals("Height") || Direction.Equals("Top"))
return (int)(Math.Floor(ComponentValue * HeightMultiplicationFactor));
return 1;
}
Width = 465 * 0.533 = 248
Height = 72 * 0.711= 51
Left = 366 * 0.533= 195
Top = 41 * 0.711= 29
Font-size = 40 * 0.533 = 21
本质上,这些方法返回缩放后的解决方案,其中控件的尺寸、位置和字体大小都具有最佳值。
在需要响应式的窗体中使用响应式类
只需在任何需要响应式的窗体中创建该类的对象即可。在构造函数中提供当前分辨率。之后,设置所需的乘数因子。
Responsive ResponsiveObj;
ResponsiveObj = new Responsive(Screen.PrimaryScreen.Bounds);
ResponsiveObj.SetMultiplicationFactor();
之后,在窗体的 load
事件中,逐个将窗体的所有控件传递用于调整大小/重新定位。调用如下代码。基本上,它首先将窗体定位到屏幕中央。请注意,为了获得最佳垂直位置,添加了一个校准常量 (30)(这会因开发人员而异——这是一个选择)。之后,窗体的每一个控件都会被重新定位、调整大小,并重新校准字体大小。
private void ResponsiveForm_Load(object sender, EventArgs e)
{
Width = ResponsiveObj.GetMetrics(Width, "Width"); // Form width and height set up.
Height = ResponsiveObj.GetMetrics(Height, "Height");
Left = Screen.GetBounds(this).Width / 2 - Width / 2; // Form centering.
Top = Screen.GetBounds(this).Height / 2 - Height / 2 - 30; // 30 is a calibration factor.
foreach (Control Ctl in this.Controls)
{
Ctl.Font = new Font(FontFamily.GenericSansSerif,
ResponsiveObj.GetMetrics((int)Ctl.Font.Size), FontStyle.Regular);
Ctl.Width = ResponsiveObj.GetMetrics(Ctl.Width, "Width");
Ctl.Height = ResponsiveObj.GetMetrics(Ctl.Height, "Height");
Ctl.Top = ResponsiveObj.GetMetrics(Ctl.Top, "Top");
Ctl.Left = ResponsiveObj.GetMetrics(Ctl.Left, "Left");
}
}
示例
为了演示目的,下面是一个包含数据网格、标签、文本框和按钮的非常简单的窗体。下面的快照是在三种不同分辨率下拍摄的。下面的快照是在 1920x1080 分辨率下拍摄的
下面的快照是在 1360x768 下拍摄的
下面的快照是在 1024x768 下拍摄的
实际上,通过以最佳水平收缩/扩展和重新定位控件,窗体在不同分辨率下的外观将保持一致。
代码修改
可能需要一些校准因子来微调从技术中获得的内容(就像我们为垂直居中定位所做的那样)。
此外,建议开发人员在不同分辨率下查看窗体的外观,以确认所有控件都可见并按预期正确地放置在屏幕上。如果不是,一些校准或少量设计时更改应该就可以了。
此外,这是一个通用的方法,适用于简单的窗体,该窗体假定窗体的所有控件都具有这些属性 - width、height、left、top 和 font-size。然而,实际情况并非如此。实际的窗体将包含其他控件,这些控件可能没有所有这些属性。例如,picturebox
没有 font-size 属性。因此,如果未显式处理此类情况,则直接运行代码会导致运行时异常。本文旨在介绍一种技术,而不是成为万能的工具;开发人员需要根据需求进行校准。建议的方法如下
private void ResponsiveForm_Load(object sender, EventArgs e)
{
Width = ResponsiveObj.GetMetrics(Width, "Width"); // Form width and height set up.
Height = ResponsiveObj.GetMetrics(Height, "Height");
Left = Screen.GetBounds(this).Width / 2 - Width / 2; // Form centering.
Top = Screen.GetBounds(this).Height / 2 - Height / 2 - 30; // 30 is a calibration factor.
foreach (Control Ctl in this.Controls)
{
if (Ctl is PictureBox)
{
Ctl.Width = ResponsiveObj.GetMetrics(Ctl.Width, "Width");
Ctl.Height = ResponsiveObj.GetMetrics(Ctl.Height, "Height");
Ctl.Top = ResponsiveObj.GetMetrics(Ctl.Top, "Top");
Ctl.Left = ResponsiveObj.GetMetrics(Ctl.Left, "Left");
}
else
{
Ctl.Font = new Font(FontFamily.GenericSansSerif,
ResponsiveObj.GetMetrics((int)Ctl.Font.Size), FontStyle.Regular);
Ctl.Width = ResponsiveObj.GetMetrics(Ctl.Width, "Width");
Ctl.Height = ResponsiveObj.GetMetrics(Ctl.Height, "Height");
Ctl.Top = ResponsiveObj.GetMetrics(Ctl.Top, "Top");
Ctl.Left = ResponsiveObj.GetMetrics(Ctl.Left, "Left");
}
}
}
关注点
如前所述,还有其他方法,例如诉诸 WPF、使用锚定/停靠等。这是一种更巧妙的替代方法。如果窗体上有数千个控件,可能会出现加载延迟。然而,随着当今更快的处理器,这仍然会瞬间完成。另外需要记住的是,这只是一次性操作 - 在窗体加载时。因此,预计不会出现主要的性能下降。
结论
这是一种面向开发人员的方法,用于创建响应式的 WinForms 应用程序,该程序可以根据计算机的运行时分辨率自动调整大小、重新定位和重新校准字体大小。只需将该类添加到您的项目中,在 App.config 文件中设置设计时分辨率,并在窗体的加载事件中添加响应式代码。这样就完成了——响应式类将负责响应式。
历史
- 2016 年 10 月 18 日:首次发布