Dynamically Add Controls to a Web Page

Introduction

Ever try to dynamically add a control to a web page only to find it's events don't work correctly, property settings don't persist across postbacks or even worse the control disappears after a postback? Dynamic controls can be a bit frustrating when you first learn how to use them. Rest assured once you learn a few key points you'll be able to get them to function just as good as declared controls.

Add on each postback

The first point you need to remember is that dynamic controls need to be re-created on each postback. If your controls are disappearing after a postback then you've most likely have the code that creates it wrapped in an if block that tests to see if it isn't a postback. The nice thing about dynamic controls is that viewstate will automatically be saved for them but the creating the controls is up to you. When viewstate is applied to the controls, which is after the Load event, if the control isn't found then nothing happens and the viewstate will be lost.

Add in the Load Event or Earlier

The second point to remember is that dynamic controls need to be created in the page's Load event or any event before that. The Init event is also a good place to add them. Now there's a few things that are preferable to do in the same block of code as when you create it. Take a look at the example below and then I'll review after that.

protected void Page_Load(object sender, EventArgs e)

{

// First Name

Panel firstNamePanel = new Panel();

firstNamePanel.ID = "FirstNamePanel";

DynamicControls.Controls.Add(firstNamePanel);

 

Label firstNameLabel = new Label();

firstNameLabel.ID = "FirstNameLabel";

firstNameLabel.AssociatedControlID = "FirstName";

firstNameLabel.Text = "First Name:";

firstNamePanel.Controls.Add(firstNameLabel);

 

TextBox firstName = new TextBox();

firstName.ID = "FirstName";

firstNamePanel.Controls.Add(firstName);

 

 

// Last Name

Panel lastNamePanel = new Panel();

lastNamePanel.ID = "LastNamePanel";

DynamicControls.Controls.Add(lastNamePanel);

 

Label lastNameLabel = new Label();

lastNameLabel.ID = "LastNameLabel";

lastNameLabel.AssociatedControlID = "LastName";

lastNameLabel.Text = "Last Name:";

lastNamePanel.Controls.Add(lastNameLabel);

 

TextBox lastName = new TextBox();

lastName.ID = "LastName";

lastNamePanel.Controls.Add(lastName);

 

 

// Submit

Panel submitPanel = new Panel();

submitPanel.ID = "SubmitPanel";

DynamicControls.Controls.Add(submitPanel);

 

Button submit = new Button();

submit.ID = "Submit";

submit.Text = "Submit";

submit.Click += new EventHandler(Submit_Click);

submitPanel.Controls.Add(submit);

}

So what are we doing here? Well first of all we're building a form for a user to enter their first and last name and then submit them to the server. We're adding all the controls dynamically. You might have noticed that I'm adding all the labels and textboxes to panels and that I'm adding those panels to a control named DynamicControls. That control is actually a PlaceHolder control which is already declared on the page. You see it's a good idea when you're adding controls dynamically to add them to some kind of container. Otherwise placing the controls in the correct position becomes very difficult.

The two best controls to use as containers are the PlaceHolder and the Panel. The difference between the two is that a Panel actually renders as a div element in the markup and also allows you to add style properties and other attributes. The PlaceHolder doesn't render anything for itself. It only renders the controls contained in it. Choosing one over the other won't impact the performance in anyway so just playing around with both of them and decide for yourself which one you favor. You most likely will come to the conclusion that it depends on the particular case you're using.

The next thing to notice is that there are two identical lines of code for each control. The first is that I'm giving each one an ID. This is so the control can be uniquely identified allowing it to be found in other parts of your code. The second thing is that I'm adding the control to a container. The first part is not required for a dynamic control to be added however the second step is. Without adding the control to a container it will never be rendered.

For some of the controls there were other properties that needed to be set. For example, on both labels I set the value of the AssociatedControlID so that it would be coupled to its associated textbox. One line of code I'd like you to pay special attention to is where I add an event handler for the button's click event. Just as you need to recreate the control on each postback you also need to add the event handlers as well.

Locating a Control

You may have picked up on the fact that the way I created the control variables that their scope was limited to that method. So what do you do if you need to work with one of these controls in say the button's click event handler? Well there's a couple ways to accomplish this. I mentioned earlier about how I added an ID for each control. That is one way to access the control and here is an example of how to do that.

protected void Submit_Click(object sender, EventArgs e)

{

string firstNameText = ((TextBox)((Panel)DynamicControls.FindControl("FirstNamePanel") ).FindControl("FirstName")).Text;

string lastNameText = ((TextBox)((Panel)DynamicControls.FindControl("LastNamePanel") ).FindControl("LastName")).Text;

 

DynamicControls.Controls.Add(new LiteralControl(string.Concat("Hello ", firstNameText, " ", lastNameText)));

}

If that looks confusing that's because it is. First let me explain what I was doing here. I wanted to retrieve the value of each textbox to use in created some text to render on the page. But look what had to be done to find the control. First we had to call the PlaceHolder's FindControl method to locate the panel we needed and then call the FindControl method on the panel to find the textbox. Nothing really wrong with doing it this way but I find there's a simpler method. Take a look at the next block of code.

Panel FirstNamePanel;

Panel LastNamePanel;

Panel SubmitPanel;

 

Label FirstNameLabel;

Label LastNameLabel;

 

TextBox FirstName;

TextBox LastName;

 

Button Submit;

 

protected void Page_Load(object sender, EventArgs e)

{

// First Name

FirstNamePanel = new Panel();

FirstNamePanel.ID = "FirstNamePanel";

DynamicControls.Controls.Add(FirstNamePanel);

 

FirstNameLabel = new Label();

FirstNameLabel.ID = "FirstNameLabel";

FirstNameLabel.AssociatedControlID = "FirstName";

FirstNameLabel.Text = "First Name:";

FirstNamePanel.Controls.Add(FirstNameLabel);

 

FirstName = new TextBox();

FirstName.ID = "FirstName";

FirstNamePanel.Controls.Add(FirstName);

 

 

// Last Name

LastNamePanel = new Panel();

LastNamePanel.ID = "LastNamePanel";

DynamicControls.Controls.Add(LastNamePanel);

 

LastNameLabel = new Label();

LastNameLabel.ID = "LastNameLabel";

LastNameLabel.AssociatedControlID = "LastName";

LastNameLabel.Text = "Last Name:";

LastNamePanel.Controls.Add(LastNameLabel);

 

LastName = new TextBox();

LastName.ID = "LastName";

LastNamePanel.Controls.Add(LastName);

 

 

// Submit

SubmitPanel = new Panel();

SubmitPanel.ID = "SubmitPanel";

DynamicControls.Controls.Add(SubmitPanel);

 

Submit = new Button();

Submit.ID = "Submit";

Submit.Text = "Submit";

Submit.Click += new EventHandler(Submit_Click);

SubmitPanel.Controls.Add(Submit);

}

This time I declared the controls as module-level variables. They can now be accessed from anywhere inside the page's code. Now watch how much simpler this makes our Submit_Click method.

protected void Submit_Click(object sender, EventArgs e)

{

string firstNameText = FirstName.Text;

string lastNameText = LastName.Text;

 

DynamicControls.Controls.Add(new LiteralControl(string.Concat("Hello ", firstNameText, " ", lastNameText)));

}

Conclusion

Well there you have it. Adding controls dynamically really isn't that difficult once you learn the steps. Hopefully this article has got you headed in the right direction.

Suprodeep    5/31/2008 10:38 PM

Hi,

 

As usual amazing article. Good procedural manner to explain things.

New Comment

  
  
  
  
Email Print