WinForm响应式布局设计实践

引言

建立响应式WinForm应用程序并不那么简单。 响应式布局,在此我指的是应用程序在不一样屏幕分辨率下的可用性。 对于WinForm应用程序,咱们须要明确地根据分辨率来调整控件的大小和从新定位。 虽然在使用WPF时有相关的实践应用,经过使用控件的docking和anchoring,或使用panels等方法,但本文提供了一种将响应式应用于WinForm应用程序的不一样方法。c#

背景

我在一个本身设计的简单游戏中遇到了问题:我设计了一台分辨率为1920x1080的机器, 可是当我试图在笔记本电脑上播放时,发现应用程序边界跑到屏幕以外。由此颇有必要让程序来适应不一样分辨率的设备,而不是让用户来适应程序。 所以,我对代码进行了改进。函数

技术

其实没什么技术可言,只是用了一个小技巧。咱们用两个常量来保存设计时的屏幕分辨率,咱们称之为设计时分辨率。这样,不管什么时候运行应用程序,它都会得到一个乘法因子,这其实是一个比例因子,经过将当前分辨率除以设计时分辨率来得到该因子。 窗体的全部控件都被传递给这个类对象进行缩放和调整大小。布局

代码

The Responsive Class - 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()方法。 这种方法经过将当前分辨率除以设计时间分辨率来得到缩放因子。this

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

最后有两种重载方法,它们为应用程序控件提供响应式解决方案(最佳大小,位置和字体大小)的最终方法。code

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的控件,则该方法返回建议的大小,位置和字体大小为:orm

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 Class

咱们须要的是以任何须要响应的形式简单地建立这个类的对象。 当前的分辨率是在构造函数中提供的, 以后的工做就是创建所需的乘法因子。

Responsive ResponsiveObj;
ResponsiveObj = new Responsive(Screen.PrimaryScreen.Bounds);
ResponsiveObj.SetMultiplicationFactor();

在这以后,表单的全部控件都将逐个传递,以在表单的加载事件中调整大小和从新定位。 这个调用在下面的代码中完成。 它所作的是首先将窗体定位到屏幕的中心。 我在这里设置了一个校准常数(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");
    }
}

示例

如下是一个很是简单的表单,其中包含一个data gird,一个label,一个textbox和一个button。 下面的图片以三种不一样的分辨率截取。 下面的截图是在1920x1080分辨率下截取的:

下面的截图是在1360x768分辨率下截取的:

下面的截图是在1024x768分辨率下截取的:

实际上,经过缩小/扩大和从新定位控制到最佳水平,Form在不一样的分辨率下看起来是同样的。

代码调整

就像咱们对垂直中心定位所作的那样,咱们可能须要设置一些参数来调整整个布局。

另外,建议开发者尝试以不一样的分辨率查看表单的外观,以确认全部的控件都是可见的,并按照预期在屏幕上正肯定位。

除此以外,对于一个简单的表单,这是一个通用的方法,它假定表单的全部控件都具备这些属性---宽度,高度,左侧,顶部和字体大小。可是,真实状况并不是如此。有一些表单控件不具备全部这些属性。例如,图片框没有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,使用anchoring/docking等,这是一个更聪明的选择。 若是表单上有数千个控件,则可能会遇到加载延迟。 然而,这点延迟对如今运行飞快的处理器来讲不成问题。 这种方法只是在表单的加载时才执行一次调用操做,所以不会带来致命的性能降低的问题。

结尾

建立响应式WinForm应用程序,根据机器的运行时间分辨率自动调整大小,从新定位字体大小并从新校准字体大小,这是一种面向开发人员的方法。 只需将该类添加到项目中,在App.config文件中设置设计时分辨率,而后在窗体的加载事件中添加响应代码。 So easy!

相关文章
相关标签/搜索