Click here to Skip to main content
13,900,000 members
Click here to Skip to main content
Add your own
alternative version

Stats

26K views
2.3K downloads
23 bookmarked
Posted 17 Oct 2016
Licenced CPOL

A Responsive Design Technique for WinForms

, 17 Oct 2016
Rate this:
Please Sign up or sign in to vote.
Design to scale automatically to different screen resolutions

Introduction

It is not so straightforward to create a responsive WinForms application. By responsiveness, we do hereby mean usability in different screen resolutions. For WinForms, we need to explicitly take care of resizing and repositioning controls based on resolution. While there are suggestions on using WPF, or using docking and anchoring of controls, using panels, etc., this article provides a different way for applying responsiveness to a WinForm application.

Background

I designed a game which is available here. I designed it in a machine that had resolution of 1920x1080. But when I tried to play it in a laptop, I found that the board fell outside the screen. I felt the need to make it responsive to reach out to people who might have different resolutions. Hence, I made changes in the code to make it responsive. So, I thought it might benefit others just to have an alternate way of applying responsiveness to WinForm applications.

The Technique

The technique is simple. It has two hard-coded constants that preserve the design-time screen resolution. Now whenever the application is run, it obtains a multiplication factor which is actually a scaling factor. It obtains this factor by dividing the current resolution by the design-time resolution. Then all the controls of the form are passed to this class object for scaling and resizing.

Using the Code

The Responsive Class - Responsive.cs

There is a class Responsive.cs which has 5 member variables as below. The purposes of the members are self-explanatory by the names.

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;

The design-time screen resolutions are kept in the App.config file.

<add key ="DESIGN_TIME_SCREEN_WIDTH" value="1920"/>
<add key ="DESIGN_TIME_SCREEN_HEIGHT" value="1080"/>

When an instance of the class is created, the current resolution is supplied to the constructor. After that, a call is made to the SetMultiplicationFactor() method of the class. This method obtains the scaling factor by dividing the current resolution with the design-time resolution.

public Responsive(Rectangle ResolutionParam)
{
    Resolution = ResolutionParam;
}

public void SetMultiplicationFactor()
{
    WidthMultiplicationFactor = Resolution.Width / WIDTH_AT_DESIGN_TIME;
    HeightMultiplicationFactor = Resolution.Height / HEIGHT_AT_DESIGN_TIME;
}

For example, the application was designed in 1920x1080 resolution. If this application is run on a machine with resolution of 1024x768, then the WidthMultiplicationFactor, and HeightMultiplicationFactor change as follows:

WidthMultiplicationFactor = 1024/1920 = 0.533
HeightMultiplicationFactor = 768/1080 = 0.711

Finally, there are two overloaded methods which are the ultimate ones that provide responsive solution (optimal size, location and font-size) for the controls of the calling form.

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;
}
For example, if there is a control with width = 465, height = 72, Left = 366, Top = 41, and font-size = 40, the method returns the suggested size, position, and font-size for this control as:
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

In essence, the methods return the scaled solution for a control with optimum values for size, position and font-size.

Using the Responsive Class in a Form that Requires Responsiveness

What is needed is to simply create an object of this class in any form where responsiveness is intended. The current resolution is provided in the constructor. After that, the required multiplication factors are set up.

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

After that, all the controls of the form are passed one by one for resizing/repositioning in the form's load event. The call is done in the following code. Basically, what it is doing is first position the form to the centre of the screen. Please see there is a calibration constant (30) that is added for optimum vertical position (this would vary from developer to developer - it is a choice). After that, each and every control of the form is repositioned, resized, and re-calibrated for font-sizes.

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");
    }
}

Example

For demonstration purposes, following is a very simple form that contains a data grid, a label, a text box and a button. The snaps below are taken at three different resolutions. The following snap is taken at 1920x1080 resolution:

The following is a snap taken at 1360x768:

The following snap is taken at 1024x768:

In effect, the form would look the same at different resolutions by contracting/expanding and re-positioning controls to an optimum level.

Modification of Code

Some claibration factors might be required to adjust a little of what we obtain from the technique (like we did for vertical-centre positioning).

Also, it is suggested that the developer should see the outlook of the form at different resolutions to confirm that all the controls are visible and positioned correctly on the screen as intended. If not, some calibration or a little design-time change should be fine.

Also, this is a generic approach for a simple form which assumes that all the controls of the form have these properties - width, height, left, top and font-size. However that is not the case for real scenario. A practical form would contain other controls which might not have all these properties. E.g., a picturebox doesn't have the font-size property. Hence running the code as is would cause in runtime exceptions if such cases are not handled explicitly. This article is meant to introduce a technique, not to be the ultimate tool for everything; developer needs to calibrate according to the need. A suggested approach is as follows:

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");
        }
    }
}
Similar code changes might apply according to need and the nature of controls. Also, there might be need to introduce more overloaded methods for different control types.

Points of Interest

As mentioned earlier, there are other approaches like resorting to WPF, using anchoring/docking, etc. This is one more clever alternative. Loading delay might be experienced if the form has thousands of controls on it. However, with today's faster processors that would still be aired in the blink of an eye. Also to remember, this is one time operation only - at the loading of the form. Hence major performance degradation is not expected.

Conclusion

This is a developer-oriented approach for creating responsive WinForms application that automatically resizes, repositions, and re-calibrate font-sizes according to the runtime resolution of the machine. Just add the class to your project, set the design-time resolution at the App.config file, and add the code for responsiveness in the form's loading event. That would be all - the responsive class will take care of responsiveness.

History

  • 18th October, 2016: First release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Mehedi Shams
Software Developer
Bangladesh Bangladesh
A software developer mainly in .NET technologies and SQL Server. Love to code and learn.

You may also be interested in...

Comments and Discussions

 
QuestionIt works for controls in form but not in Groupbox Pin
perfectie15-Mar-18 2:22
memberperfectie15-Mar-18 2:22 
AnswerRe: It works for controls in form but not in Groupbox Pin
Mehedi Shams10-Feb-19 14:28
memberMehedi Shams10-Feb-19 14:28 
Questionresponsive design on winforms part 2, e.g. 1920 on 6in win tablet pc vs 24in desktop monitor? Pin
Member 82797064-Aug-17 15:40
memberMember 82797064-Aug-17 15:40 
AnswerRe: responsive design on winforms part 2, e.g. 1920 on 6in win tablet pc vs 24in desktop monitor? Pin
Mehedi Shams5-Nov-17 19:18
memberMehedi Shams5-Nov-17 19:18 
QuestionError Handling for Properties Pin
Andrew_assec3-May-17 1:50
memberAndrew_assec3-May-17 1:50 
AnswerRe: Error Handling for Properties Pin
Mehedi Shams3-May-17 13:57
memberMehedi Shams3-May-17 13:57 
Suggestionper monitor awareness Pin
André Ziegler17-Oct-16 23:29
memberAndré Ziegler17-Oct-16 23:29 
GeneralRe: per monitor awareness Pin
Nelek18-Oct-16 1:44
protectorNelek18-Oct-16 1:44 
GeneralRe: per monitor awareness Pin
Mehedi Shams18-Oct-16 12:15
memberMehedi Shams18-Oct-16 12:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190306.1 | Last Updated 18 Oct 2016
Article Copyright 2016 by Mehedi Shams
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid