Adding Controls to an ASP.NET form Dynamically

Downloads
Lessons
18
Filofax, it's a way of life
  Buy used DVDs $3.99 or less
Free Book Offer - Orange - 300x250
  Buy used DVDs $3.99 or less

What Icon Dynamicallly 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 Icon 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">

 

<html xmlns="http://www.w3.org/1999/xhtml">

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

    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.

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 <br> 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("<br>"));

 

        }

 

    }

 

    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("<br>"));

 

        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("<br>");

                }

            }

        }

 

        //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.


MAGIX Music Maker

So What Icon 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 I will explain how to dynamically create an entire form from a database. Part 3 will discuss adding event handlers to dynamically created controls.


blog comments powered by Disqus