65.9K
CodeProject 正在变化。 阅读更多。
Home

WinForms 的响应式设计技术

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.79/5 (12投票s)

2016 年 10 月 18 日

CPOL

5分钟阅读

viewsIcon

68080

downloadIcon

4449

设计自动适应不同屏幕分辨率

引言

创建响应式的 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 的机器上运行,则 WidthMultiplicationFactorHeightMultiplicationFactor 更改如下

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,height = 72,Left = 366,Top = 41,font-size = 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");           // 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 日:首次发布
© . All rights reserved.