Changing Layout Dynamically in Windows Forms

Introduction

If you are a long-time Windows developer, at some point in time you must have faced the difficulty in adjusting your forms and dialogs for multiple screen resolutions. In the past, developers used various techniques to avoid such problems, including resizing controls at runtime by handling certain event handlers. However, the task was certainly not an easy one. Fortunately, the .NET Framework 2.0 provides some handy techniques that allow you to provide dynamic layout to your forms. In this article, I am going to illustrate some of these techniques with examples.

Why Dynamic Layout?

There are many scenarios wherein you need dynamic layouts. Some of the common ones are as follows:

  • You developed your application under one screen resolution and want to run it on machines having different (and possibly unknown) screen resolutions.
  • You are setting the text of controls such as Label and Button via code and don't know the length of the text at design time.
  • You are developing a multilingual application and don't know the width of controls under different language settings.
  • Your application adds controls dynamically on the forms.

In all such cases, being able to change the layout of your form dynamically becomes essential.

The Solution

Luckily, the .NET Framework 2.0 comes with several options to solve the problem of dynamic layout. In this article, I am going to cover the following options:

  • Auto sizing forms and controls
  • Docking
  • Anchoring
  • Flow layout panel
  • Table layout panel

Auto sizing forms and controls

In this example, you will learn to resize Windows Forms controls and forms automatically so that they fit their content. Before going any further, let me illustrate why such auto sizing is necessary.

Create a new Windows Application using Visual Studio and C# as the programming language. Design the default form as shown below:

The form consists of a textbox and three buttons. Clicking on the "Change Text" button changes Text property of the remaining two buttons to a value entered in the textbox. Write the following code in the Click event handler of "Change Text" button.

private void button3_Click(object sender, EventArgs e)
{
   button1.Text = textBox1.Text;
   button2.Text = textBox1.Text;
}

The code simply sets the Text property of button1 and button2 to a string entered in textBox1. After writing the code, run the form. Enter some big string value in the textbox and click the "Change Text" button. The following figure shows a sample run of the form.

Can you see the problem? The button controls do not display the complete text. Now, set the AutoSize property of both buttons to true. The AutoSize property governs whether a control should automatically adjust its width to accommodate its contents. Also, set the AutoSizeMode property of the first button to GrowOnly and that of the second button to GrowAndShrink. The AutoSizeMode property governs how a control should behave to adjust its contents. The value of GrowOnly indicates that the control can only expand to accommodate its content. The value of GrowAndShrink indicates that the control can expand or shrink to fit its content. Run the form again. This time, you will observe that the buttons adjust their width as per the text entered. Notice the behavior of the second button. It expands as well as shrinks according to the length of the text value.

Now, enter a very long string and check the behavior. You will find that the buttons adjust themselves as per the text but the form remains the same. As a result, buttons expand beyond the form boundaries.

This can be easily corrected by setting Form's AutoSize property to true.

Docking

Suppose that you want to develop a form as shown below:

The form has a Label control at the bottom that displays some status message to the user. Try resizing the form. You will find that, as the form is resized, the Label doesn't change its position and size. This is certainly undesirable (see below).

The expected behavior would be that the Label always remains attached with the bottom edge on the form. This is where the docking feature comes into picture. Windows Forms controls have a property called Dock that can dock that control to left, right, top, or bottom edge of the form. You can also make the control fill the available area of the form. To see this property in action, set the Dock property of the Label control to Bottom and run the form again. This time, as the form is resized, the Label is also resized and always remains attached to the bottom of the form.

Anchoring

Assume that you are developing a resizable dialog box that has two buttons, OK and Cancel (see below).

If you resize the form at runtime, you will find that the buttons assume a fixed location.

You would expect them to maintain the same distance from the right and bottom edges of the form. Thanks to the Anchor property, that makes this possible. The Anchor property anchors a control at some fixed place with respect to form borders. By default, controls are anchored at a fixed point from the top and left edges. To see the Anchor property in action, set the Anchor property of the OK and Cancel buttons to Bottom-Right and run the form. This time, you will observe that, even if you resize the form, the controls maintain a fixed distance from the bottom and right border of the form.

Flow layout panel control

In some applications, you need to add controls dynamically to the form depending on some condition. In such cases, you need to place the newly added control at an appropriate location via code. However, this means that you need to carefully calculate their left and top coordinates depending on surrounding controls. This can get tricky and tedious at times. To make the problem clear, develop a form that adds a few buttons dynamically to a GroupBox control. Add a new form to the project and drag and drop a GroupBox control to it. The GroupBox control is a container control and can house other controls inside it. Now, add the following code in the Load event of the form.

private void FlowLayoutDemo_Load(object sender, EventArgs e)
{
   for (int i = 0; i < 5; i++)
   {
      Button b = new Button();
      b.Text = "Button " + i.ToString();
      groupBox1.Controls.Add(b);
   }
}

The code above runs a for loop. With each iteration, a new Button is created and added to the Controls collection of the GroupBox. If you run this form, you will see something like this:

As you can see, all the dynamically added buttons assume the same left and top coordinates and as a result overlap each other. To arrange all of them, you need to write code. The FlowLayoutPanel control makes your job easy. See how.

Drag and drop a FlowLayoutPanel control on the same form and add the following code to the Load event of the form.

private void FlowLayoutDemo_Load(object sender, EventArgs e)
{
   for (int i = 0; i < 5; i++)
   {
      Button b = new Button();
      b.Text = "Button " + i.ToString();
      groupBox1.Controls.Add(b);
   }

   for (int i = 0; i < 5; i++)
   {
      Button b = new Button();
      b.Text = "Button " + i.ToString();
      flowLayoutPanel1.Controls.Add(b);
   }

}

The code adds another for loop. This time, the new for loop adds buttons to FlowLayoutPanel control. If you run the form, you will observe that buttons added to FlowLayoutPanel automatically get arranged one after the other (see below).

The FlowLayoutPanel control has a property called FlowDirection that governs the direction in which the child controls are arranged. The FlowDirection property has four possible values: LeftToRight, TopDown, RightToLeft, and BottomUp. The WrapContents boolean property governs whether the contents of FlowLayoutPanel will be wrapped or clipped depending on the FlowLayoutPanel width.

Changing Layout Dynamically in Windows Forms

TableLayoutPanel control

The FlowLayoutPanel control arranges child controls in a flow; in other words, one beside other. At times, however, you need the ability to place controls in a tabular fashion. That is where the TableLayoutPanel control comes into the picture. The TableLayoutPanel control consists of rows and columns. You can place a control in a specific row and column. To see how TableLayoutPanel works, add a new form to the project. Drag and drop two TableLayoutPanel controls on the form. Set the AutoSize property of the form and both the TableLayoutPanel controls to true. Set the GrowStyle property of the first TableLayoutPanel control to AddRows and that of the second TableLayoutPanel control to AddColumns. The GrowStyle property should happen when new controls are added to the TableLayoutPanel and no cells are available to accommodate them. The value of AddRows indicates that the TableLayoutPanel control will add a new row to accommodate additional controls whereas the value of AddColumns indicates that the TableLayoutPanel control will add extra columns to accommodate new controls. Now, add the following code in the Load event of the form.

private void TableLayoutPanelDemo_Load(object sender, EventArgs e)
{
   for (int i = 0; i < 5; i++)
   {
      Button b = new Button();
      b.Text = "Button " + i.ToString();
      TableLayoutPanelCellPosition pos =
         new TableLayoutPanelCellPosition(0, i);
      tableLayoutPanel1.SetCellPosition(b, pos);
      tableLayoutPanel1.Controls.Add(b);
   }
   for (int i = 0; i < 5; i++)
   {
      Button b = new Button();
      b.Text = "Button " + i.ToString();
      tableLayoutPanel2.Controls.Add(b);
   }
}

The code consists of two for loops. The first for loop creates some Button controls on the fly and adds them to the first TableLayoutPanel. The buttons are added to a specific row and column. This is done using TableLayoutPanelCellPosition class and SetCellPosition() method. The TableLayoutPanelCellPosition class represents the position inside a TableLayoutPanel in terms of row and column. The code sets column as 0 and row as i. The SetCellPosition() method of TableLayoutPanel control sets the cell position for buttons. Finally, dynamically created buttons are added to the Controls collection of TableLayoutPanel control.

The second for loop does a similar job as the first one except that it doesn't set any cell positions as such. The following figure shows a sample run of the form.

[DynamicLayout11.jpg]

You also can set the row span and column span of individual child controls by using the SetRowSpan() and SetColumnSpan() methods of the TableLayoutPanel control.

Summary

Changing your form layout at runtime is required in several cases. The .NET Framework 2.0 provides several approaches to tackling the problem of dynamic form layout. In this article, you were introduced to approaches such as Auto sizing, anchoring, and docking. You also learned about two handy controls—namely FlowLayoutPanel and TableLayoutPanel—that allow you to easily add controls to your form on the fly. The FlowLayoutPanel arranges the controls in a flow; in other words, one beside other whereas the TableLayoutPanel control arranges them in a tabular fashion. Together, these approaches allow you to develop forms that are resolution independent and dynamic.



About the Author

Bipin Joshi

Bipin Joshi is a blogger and writes about apparently unrelated topics - Yoga & technology! A former Software Consultant by profession, Bipin has been programming since 1995 and has been working with the .NET framework ever since its inception. He has authored or co-authored half a dozen books and numerous articles on .NET technologies. He has also penned a few books on Yoga. He was a well known technology author, trainer and an active member of Microsoft developer community before he decided to take a backseat from the mainstream IT circle and dedicate himself completely to spiritual path. Having embraced Yoga way of life he now codes for fun and writes on his blogs. He can also be reached there.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hurricane Sandy was one of the most destructive natural disasters that the United States has ever experienced. Read this success story to learn how Datto protected its partners and their customers with proactive business continuity planning, heroic employee efforts, and the right mix of technology and support. With storm surges over 12 feet, winds that exceeded 90 mph, and a diameter spanning more than 900 miles, Sandy resulted in power outages to approximately 7.5 million people, and caused an estimated $50 …

  • By providing complete access control with granular permissions, deployment flexibility, mapped drive support, and ability to transfer large files, Egnyte provides a more robust, secure and an affordable file sharing solution for the business than Box

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds