Creating Dynamic Controls in Windows Forms Using C#

Creating Dynamic Controls in Windows Forms Using C#

Visual Studio 2010 has a great WYSIWYG editor for laying out a variety of controls on a Windows Form, but this will only allow you to create static controls that are determined at compile time. What if you don’t know what controls you want until runtime? Thankfully, creating dynamic controls is quite simple. This tutorial will cover how to create a control at runtime, and then how to access it.

Auto Generated Code

When you drag a control onto a windows form, Visual Studio creates all the code needed and puts it in a designer file. This file will be called Windows_Form_Name.Designer.cs. Try dragging a control onto a blank form and see what it does in the designer file. By default, the auto generated portions of code will be collapsed under a region labeled Windows Form Designer generated code. By clicking on the + sign on the left margin, you can expand this region and view the code, which will belong to the InitializeComponent() method. This code is responsible for defining key attributes of a control and instantiating it.

This is the code generated by dragging a button control onto the form:

this.button1 = new System.Windows.Forms.Button();

this.button1.Location = new System.Drawing.Point(94, 120);

this.button1.Name = "button1";

this.button1.Size = new System.Drawing.Size(75, 23);

this.button1.TabIndex = 0;

this.button1.Text = "button1";

this.button1.UseVisualStyleBackColor = true;

this.Controls.Add(this.button1);

The first and last lines of this code are really crucial, as they instantiate a new button object named button1, and add it to the forms list of controls. Everything in between defines the buttons various characteristics, of which these are only a few. If you adjust the buttons properties from VS 2010’s Properties panel, you will notice that new lines of code are added or adjusted in the designer file. When we create a dynamic control, we can add as many characteristics as we want, but to be on the safe side, we will always add the ones listed above. Some of these are obviously required, but some seem unnecessary (TabIndex may be wholly unnecessary for a read-only label, for instance). However, since these are the bare minimum Visual Studio generates for new controls, it is probably a good practice to define them in our code as well.

At the bottom of the designer file, outside of the Windows Form Designer region, is a crucial line of code that is absolutely necessary for this code to run. It is the definition statement for button1:

private System.Windows.Forms.Button button1;

In our code, we will place this at the top and combine it with the object instantiation statement.

Creating Dynamic Controls at Runtime

For this example, we will be creating ten buttons at runtime using a for loop. The number ten will be hard-coded, but letting the user input it is also acceptable and more in the spirit of dynamic runtime controls, but there is enough extraneous code in doing this that it might muddle the tutorial. I’ll leave that exercise to the reader.

To begin entering code, select Windows_Form_Name.cs and go to View/Code to display the source code. I like to leave the designer file alone, relegating it to a storehouse of auto generated code, which cleans up the main source code file a lot. However, you may view things differently, and are welcome to put the code in the designer file. They both define the same partial class, so it doesn’t matter where you put the code. It’s really a matter of personal preference (or your company’s coding standards).

We are going to put all of our source code in a method called DynamicButton(). The first thing we are going to do is define an x and y coordinate for our buttons:

int x = 10, y = 10;

Each button will use these variables for their location, with the for loop incrementing the y coordinate during each iteration. You can also define x or y based on another controls location, by accessing its Location.X or Location.Y field. This is handy for aligning dynamic controls under a column label.

After defining the starting location for our buttons, we will create a for loop and add the incrementing logic so that the buttons will display in a long column:

for (int i = 0; i < 10; i++ )

{

y += 23;

}

This code will increment y by 23 each iteration, which happens to be the height of the button we are going to use. You can also access the button’s Height field to accomplish the same thing. This line should always remain at the bottom of the for loop, or there will be an empty space at the top of the column of buttons.

Now we need to define the button and instantiate it. Add the following line of code to declare a buttonRuntime object and instantiate it:

System.Windows.Forms.Button buttonRuntime = new System.Windows.Forms.Button();

Since these buttons are generated at runtime, we are going to act as though we have no idea how many are going to be created. The for loop has been hard coded for ten iterations, but this could easily be changed to fifty, a hundred, or some arbitrary number that is inputted by the user. The above line of code will represent each button as its being created. This effectively creates a reference to a new object which is then lost on the next iteration of the loop, which is definitely a bad thing. Don’t worry too much though, we can get the reference back pretty easily. If you are used to coding in C/C++, doing this may make you cringe, but think of it this way: We are adding a new object to a list of controls, which can be searched and accessed quite easily. We are only letting go of the reference because it is advantageous to do so at this point.

The rest of the code is fairly straightforward. It defines various properties for the buttons:

buttonRuntime.Location = new System.Drawing.Point(x, y);

buttonRuntime.Name = "Button_" + i;

buttonRuntime.Size = new System.Drawing.Size(200, 23);

buttonRuntime.TabIndex = i;

buttonRuntime.Text = buttonRuntime.Name;

buttonRuntime.UseVisualStyleBackColor = true;

Controls.Add(buttonRuntime);

Notice how I’ve altered a few things from the auto generated code. First of all, the Location is using the x and y variables we defined earlier. Second, the object’s Name is concatenated with the for loop’s iterator value. This will allow us to easily access a particular button later on. The tab index is also set to the for loop iterator value, so you can tab through the buttons to gain focus sequentially. If you had items above the buttons, you’d want to add the previous item’s tab index to this value. The text each button displays will also utilize the for loop iterator, creating buttons named “Button_0”, “Button_1”, and so on.

The completed method looks like this:

public void DynamicButton()

{

int x = 10, y = 10;

for (int i = 0; i < 10; i++ )

{

System.Windows.Forms.Button buttonRuntime = new System.Windows.Forms.Button();

buttonRuntime.Location = new System.Drawing.Point(x, y);

buttonRuntime.Name = " Button_" + i;

buttonRuntime.Size = new System.Drawing.Size(200, 23);

buttonRuntime.TabIndex = i;

buttonRuntime.Text = buttonRuntime.Name;

buttonRuntime.UseVisualStyleBackColor = true;

Controls.Add(buttonRuntime);

y += buttonRuntime.Size.Height;

}

}

You may have noticed that I changed the y variable assignment to buttonRuntime.Size.Height, which equates to the same value (23), but now if you ever change the button size, the column will still stack up neatly.

Alright, now add a call to this method to the Form1() constructor so that it is run when the form loads. Test out the program and see what it does. Notice that when you click on a button, nothing happens. We are going to change this using a .Net event handler, a special kind of delegate that predefines a number of things for you and generally makes life simpler.

Interacting with Dynamic Controls at Runtime

To kick things off, add this line to the DynamicButton() method at the bottom:

buttonRuntime.Click += new System.EventHandler(this.button_Click);

Not surprisingly, the Click event occurs when the button is clicked on. The event handler calls a method not defined yet, button_Click. This method will change the text on the button to “Clicked” when clicked on, and then back to “Button_i” when clicked a second time. The button_Click method signature takes an object named sender, which will help us access the button we created previously but lost the reference to. Here is the skeleton of this event method:

private void button_Click(object sender, EventArgs e)

{

}

In order to properly use sender, we need to explicitly cast it into a button, else we won’t have access to the button-specific methods. This is done simply like this:

System.Windows.Forms.Button buttonClicked = (Button)sender;

Now we can access the methods of whatever button we click on through buttonClicked. The logic to change the button text is fairly straightforward:

if ( buttonClicked.Text == "Clicked")

{

buttonClicked.Text = buttonClicked.Name;

}

else

{

buttonClicked.Text = "Clicked";

}

The completed method looks like this:

private void button_Click(object sender, EventArgs e)

{

System.Windows.Forms.Button buttonClicked = (Button)sender;

if ( buttonClicked.Text == "Clicked")

{

buttonClicked.Text = buttonClicked.Name;

}

else

{

buttonClicked.Text = "Clicked";

}

}

Now try running the program. By clicking on a button, you will change its text from its original name to “Clicked” and back again. While not entirely useful, this should provide you with everything you need to do more interesting things with dynamic controls.

Posted in Uncategorized | Leave a comment