Adding Controls to an ASP.NET form Dynamically

Dynamically added controls disappear on postback

At first glance it appears that adding a control to a form dynamically is quite simple. In fact, adding a control to a form is simple. The problem is when you need to perform a postback the control disappears. This module discusses why a dynamically added control disappears on postback and how to stop it from disappearing.
I also discuss how to get the values out of a dynamically created control.

How to add and persist controls dynamically

First I need to create a page that has a placeholder for the dynamically added controls. The page below has the following controls:
 AddControlButton
 This is the button used to create new textboxes dynamically.
 DynamicControlsHolder
 This is s placeholder control that is used to hold all newly added textboxes.
 Submit
 Another button used to submit the form. This button click event will read the values of all the dynamically added textboxes.
 ContentMessage
 A label control that will display the values of the textboxes that were added dynamically.

 

<%@ Page Language=”C#” AutoEventWireup=”true”  CodeFile=”Default.aspx.cs” Inherits=”_Default” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<head runat=”server”>
    <title></title>
</head>
<body>
    <form id=”form1″ runat=”server”>
    <div>
        <asp:Button ID=”AddControlButton” runat=”server” Text=”Add Control”
            onclick=”AddControlButton_Click” />
        <br />
        <asp:PlaceHolder ID=”DynamicControlsHolder” runat=”server”></asp:PlaceHolder>
        <br />
        <br />
        <asp:Button ID=”Submit” runat=”server” Text=”Submit Form”
            onclick=”Submit_Click” />
        <br />
        Form Contents:
        <br />
        <asp:Label ID=”ContentsMessage” runat=”server”></asp:Label>
    </div>
    </form>
</body>
</html>

 

Next I create a button click event for the AddControlButton. In the click event I will add a simple textbox. After creating the textbox I add it to the controlHolder placeholder control. Notice that I also add a
tag after the textbox. This is just to make sure that only one textbox is added on each line.
using System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Text;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}

protected void AddControlButton_Click(object sender, EventArgs e)
{
TextBox txt = new TextBox();
DynamicControlsHolder.Controls.Add(txt);
DynamicControlsHolder.Controls.Add(new LiteralControl(
));
}

    protected void Submit_Click(object sender, EventArgs e)
{
}
}

 

Since the DynamicControlsHolder is stored in the pages ViewState it is easy to assume that all controls that are added to the placeholder are also automatically added to the ViewState. This is not the case however. The contents of the placeholder control are discarded once the page has been sent to the requesting browser. When a page is posted back it has to be loaded back into memory from the .aspx file. Since my dynamically added files are not listed on the .aspx page they are not re-created at post back. Each time I click the “add control” button, a new textbox is created and added to an empty placeholder control. Therefore, with each click I only see one empty textbox.

If I want to persist a textbox from one postback to the next I need to manage how it will be persisted. This can be done in a number of ways, for example it can be stored in a database, in a Session variable or in ViewState. This example uses ViewState, but I will discuss [using a database as storage mechanism in another lesson|Adding Controls to an ASP.NET Form Dynamically from a Database].

A page that has all controls provided at design time can remember what all of the controls are with each post back because they are loaded by the runtime at each post back. As noted above, controls that are added dynamically are not automatically added back to the runtime with each post back. In the following code example notice that I’ve modified the code to override the LoadViewState event. This event fires before the forms Load event but after the OnPreInit event. Since I am storing my dynamic controls in ViewState I cannot use the OnPreInit event to access them. They are inaccessible because the ViewState has not yet been initialized in the OnPreInit event.

Now when I add a control to the placeholder, I also need to add it to the ViewState. I’ve modified the code to both store and retrieve the dynamic controls to and from ViewState as follows.

  1. I added a generic list of strings to store the id’s of the dnamically added textboxes (if you need to add multiple different types of controls you may want to use a hashtable, store the control id as the key and the control type as the value)
  2. I stored the list of controls in the pages ViewState
  3.  I added the overriden LoadViewState function. This function serves the purpose of reading the forms viewstate into memory. Since I want to load my controls from viewstate before any other events occur this is the appropriate function (as it happens before the Page_Load event) I use this function to load and loop through the generic list that I stored in the viewstate. On each loop I load an instance of the control into memory and give it the appropriate id. The ASP.NET engine then maps it to the object found in the viewstate.
  4.  Finally I added code to the submit buttons click event to loop through and read the values of each of the dynamically added textboxes. Since I loaded both textboxes and
    tags into the placeholder, I have to check to make sure that the control I’m trying to read from is in fact a textbox.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
public partial class _Default : System.Web.UI.Page
{
    //private property to access the ControlsList in ViewState
    private List<string> ControlsList
    {
        get
        {
            if (ViewState[“controls”] == null)
            {
                ViewState[“controls”] = new List<string>();
            }
            return (List<string>)ViewState[“controls”];
        }
    }
    //Determines what the ID shoulld be for the next control
    private int NextID
    {
        get
        {
            return ControlsList.Count + 1;
        }
    }
    //LoadViewState fires before Page_Load but after OnPreInit
    protected override void LoadViewState(object savedState)
    {
        //MAKE SURE TO LEAVE THIS CALL TO THE BASE CLASS
        // this is what loads the ViewState into memory
        base.LoadViewState(savedState);
        foreach (string txtID in ControlsList)
        {
            TextBox txt = new TextBox();
            txt.ID = txtID;
            DynamicControlsHolder.Controls.Add(txt);
            DynamicControlsHolder.Controls.Add(new LiteralControl(
));
        }
    }
    protected void Page_Load(object sender, EventArgs e)
    {
    }
    protected void AddControlButton_Click(object sender, EventArgs e)
    {
        TextBox txt = new TextBox();
        //assign a value to the ID of each control
        // so that each control can be uniquely identified
        txt.ID = “TextBox” + NextID.ToString();
        DynamicControlsHolder.Controls.Add(txt);
        DynamicControlsHolder.Controls.Add(new LiteralControl(
));
        ControlsList.Add(txt.ID);
    }
    protected void Submit_Click(object sender, EventArgs e)
    {
        StringBuilder sb = new StringBuilder();
        foreach (Control ctl in DynamicControlsHolder.Controls)
        {
            if (ctl is TextBox)
            {
                TextBox txt = ctl as TextBox;
                if (ctl != null)
                {
                    sb.Append(txt.ID);
                    sb.Append(“: “);
                    sb.Append(txt.Text);
                    sb.Append(
);
                }
            }
        }
        //display contents of all texboxes in label
        ContentsMessage.Text = sb.ToString();
    }
}

 

The form now operates as desired, each button click will dynamically add a textbox that is persisted on each postback.

 

So What?

Often it is necessary to dynamically add controls to a form. This may be because you want the user to have the ability to add more fields to a form. Other times you may want to store an entire form in a database and dynamically create it from there.

This lesson showed you how to dynamically add a textbox to a form using the forms ViewState to persist the control on post backs. In [Part 2|Adding Controls to an ASP.NET Form Dynamically from a Database] I will explain how to dynamically create an entire form from a database. [Part 3|Adding Event Handlers to Dynamically Created Web Controls] will discuss adding event handlers to dynamically created controls.



Categories: Misc

Tags: , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: