在现代软件开发中,应用程序需要能够在各种不同的屏幕分辨率上运行,并且保持良好的用户体验。对于WinForms应用程序来说,这意味着需要明确地处理控件的调整大小和重新定位,以适应不同的分辨率。虽然有建议使用WPF,或者使用控件的停靠和锚定,使用面板等方法,但本文提供了一种不同的方法来实现WinForms应用程序的响应性。
设计了一个游戏,可以在找到。在一个分辨率为1920x1080的机器上设计了它。但是当尝试在笔记本电脑上玩时,发现棋盘超出了屏幕。感到有必要使其响应,以便能够接触到可能具有不同分辨率的人。因此,更改了代码以使其响应。所以,认为这可能会对其他人有所帮助,只是提供一个替代方法来实现WinForms应用程序的响应性。
这种技术很简单。它有两个硬编码的常量,用于保留设计时的屏幕分辨率。现在,每当应用程序运行时,它都会获得一个乘法因子,实际上是一个缩放因子。它通过将当前分辨率除以设计时分辨率来获得这个因子。然后,将表单的所有控件传递给这个类对象进行缩放和调整大小。
响应类 - 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;
}
例如,如果有一个控件,宽度=465,高度=72,左边距=366,顶部=41,字体大小=40,该方法将返回此控件的建议大小、位置和字体大小:
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");
// 设置表单的宽度和高度。
Height = ResponsiveObj.GetMetrics(Height, "Height");
Left = Screen.GetBounds(this).Width / 2 - Width / 2;
// 表单居中。
Top = Screen.GetBounds(this).Height / 2 - Height / 2 - 30;
// 30是一个校准因子。
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分辨率下拍摄的:
实际上,通过收缩/扩展和重新定位控件到最佳水平,表单在不同分辨率下看起来会相同。
如前所述,可能需要一些校准因子来调整从技术中获得的一些内容(就像为垂直中心定位所做的那样)。
还建议开发人员在不同的分辨率下查看表单的外观,以确认所有控件都正确地显示在屏幕上,并且位置正确。如果不是这样,一些校准或一些小的设计时更改应该可以。
这也是一个通用方法,用于一个简单的表单,它假设表单的所有控件都有这些属性 - 宽度、高度、左边距、顶部和字体大小。然而,实际情况并非如此。一个实际的表单将包含其他控件,这些控件可能没有所有这些属性。例如,一个PictureBox没有字体大小属性。因此,如果不显式处理这些情况,运行代码将导致运行时异常。本文的目的是介绍一种技术,而不是成为一切的终极工具;开发人员需要根据需要进行校准。建议的方法如下:
private void ResponsiveForm_Load(object sender, EventArgs e)
{
Width = ResponsiveObj.GetMetrics(Width, "Width");
// 设置表单的宽度和高度。
Height = ResponsiveObj.GetMetrics(Height, "Height");
Left = Screen.GetBounds(this).Width / 2 - Width / 2;
// 表单居中。
Top = Screen.GetBounds(this).Height / 2 - Height / 2 - 30;
// 30是一个校准因子。
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,使用锚定/停靠等。这是另一种聪明的替代方法。如果表单上有成千上万的控件,可能会遇到加载延迟。然而,随着今天更快的处理器,这仍然会在眨眼之间完成。另外要记住,这只是一个一次性操作 - 在表单加载时。因此,预计不会有太大的性能下降。