Add Controls Dynamically - Part 5

In parts 1 to 4 I have talked about creating dynamic controls and adding them to the page. I haven’t talked about a important aspect of Asp.Net that is very important for any developers and specially if you are dealing with dynamic controls. So in this article I’m going to talk about the Asp.Net Life cycle.

Related posts:

Asp.Net Life Cycle

As I said in previous articles the state of the page or any of it’s components is not stored on the sever. Everything is stored on the page. This is due to the characteristics of HTTP protocol which is stateless. Having this in mind, every time there is a postback the structure of the page is recreated so that you can have access to the controls through code. I’m not sure if I made myself clear so here another way to put it:

HTML is HTML and that is it. When you look at the page in the browser all there is is HTML, there is no C# or VB.Net. Only when the page is submitted to the server is that the HTML is going to be read transformed in server side controls. The programmer then uses these server side controls to manipulate the controls. At the end this controls will render HTML again which will be sent to the page.

Ok, so why is this important? Well, the process of creating the server side code from the HTML happens in several stages which fire events that let you interact with the components in the right moment.

The life cycle has a lot of stages but the ones we are interested in are the Initialization and Load. For more details on all stages check out MSDN

Initialization

In this stage the controls available on the page (not the ones created dynamically) become available. The ViewState has not been retrieved however and therefore the control values cannot be loaded in the controls. The postback data is also not available at this stage.

The Load stage has three events to help the programmer:

  1. PreInit
  2. Init
  3. InitComplete

In the PreInit event the controls don’t even exist yet. They have not been created.

If you set a property like the Text of a TextBox in these events this value will be lost in future stages when the control is loaded. If you want to use these events you need to declare the following methods in your code:

void Page_PreInit(object sender, EventArgs e)
    {

    }

    void Page_Init(object sender, EventArgs e)
    {

    }

    void Page_InitComplete(object sender, EventArgs e)
    {

    }

Load

This is an important stage for us because it is here that the values of the controls are loaded. Before this stage you might have the controls but it’s values have are not there yet.

The Load stage has three events to help the programmer:

  1. PreLoad
  2. Load
  3. LoadComplete

We have used the Page_Load event in our previous examples to create our dynamically created controls. The thing to watch is that when these controls are created they don’t have it’s values from the page loaded yet. If you want to get the values from the controls you can only do it in the Page_LoadComplete event. Only then all the values of the controls will be loaded.

You can test this using creating the method for each of these events and using the debugger to check the controls we the event is hit.

void Page_PreLoad(object sender, EventArgs e)
    {

    }

    void Page_Load(object sender, EventArgs e)
    {
        //when the user first enters the page set the count as zero
        if (!IsPostBack)
        {
            ViewState["count"] = 0;
        }
        //on every subsequent postback, check the control count
        //and recreated all the controls
        else
        {
            int controlCount = (int)ViewState["count"];
            for (int i = 0; i < controlCount; i++)
            {
                //notice that now we pass the i as parameter
                createDynamicControls(i);
            }
        }
    }

    void Page_LoadComplete(object sender, EventArgs e)
    {

    }

Notice that I have used the Load event defined in the previous articles but the methods for the other events are new. If you set a breakpoint and use the Watch to inspect the controls you can see when the values have been loaded.

Ok, so if you need to create the controls you do it where we’ve been doing up to now: In the Page_Load

If you need to get the values from these controls right after you loaded them you need to wait for the Page_LoadComplete event.

Well, I hope this info was useful.

Add Controls Dynamically - Part 4

The part 3 of this series was getting a bit long for my taste so I decided to break it in 2. Here we’ll continue dealing with the controls added dynamically to our page only now we’re going to access them by ID.

Related posts:

Naming the controls

By now you have noticed that we haven’t explicitly set the ID of any of our controls. Of course the controls have ID’s but they are defined automatically by the Asp.Net framework when the controls are instantiated. The reason this works is because the framework will create the names consistently the same, again and again as long as you create the controls always in the same order (we are doing that).

It is this automatically naming feature that allows our components to keep their state between requests. When the page is submitted all of the controls states are sent back from the browser to the server, but the server doesn’t know how to create the controls that’s why we had to do it every time the page is submitted. After the control has been created the Asp.Net framework is able to get it’s state from the page based on it’s name.

Ok, enough theory. Let’s give our controls and ID manually. Since the controls are added dynamically the ID’s have to be something that can be predicted. Let’s say: TextBox + a number. For this will change the createDynamicControls method to add a parameter which will be the number of the control added.

//this method takes care of creating the controls
    private void createDynamicControls(int count)
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        //now we set the control ID manually
        tb.ID = "TextBox" + count;
        PlaceHolder1.Controls.Add(tb);
        controlsList.Add(tb);
    }

When the add control button is clicked we need to pass the number to the method. We can use the control count that we’re storing in the ViewState.

protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls((int)ViewState["count"]);
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;
    }

Another place we need to change is the Page_Load event when the controls are re-created. Here for loop when we pass in the new parameter:

for (int i = 0; i < controlCount; i++)
            {
                //notice that now we pass the i as parameter
                createDynamicControls(i);
            }

To test our new functionality we will add TextBox, a Button and a Label. We are going to inform the ID of the TextBox we want to get the value for. When the button is clicked the event will get the control and display it’s value in the label.

<asp:TextBox ID="TextBoxControlId" runat="server"></asp:TextBox>
        <asp:button ID="ButtonGetValue" runat="server" text="Get value" 
            onclick="ButtonGetValue_Click" />
        Valor: <asp:Label ID="LabelValue" runat="server" Text=""></asp:Label>

And here is the event where we get the TextBox we want:

protected void ButtonGetValue_Click(object sender, EventArgs e)
    {
        //find the control we want using the name informed by the user in the 
        //TextBoxControlId control
        TextBox tb = (TextBox)PlaceHolder1.FindControl(TextBoxControlId.Text);
        LabelValue.Text = tb.Text;
    }

Now if you add 3 TextBoxes (which will have id’s TextBox0, TextBox1 and TextBox2) and you input TextBox0 in the interface and hit the button the LabelValue control will show the value in the first TextBox.

This one was a bit longer than I would like to so I’ll try to make the next article shorter.

Here is the final version of our page after all of our modifications:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    System.Collections.Generic.List<TextBox> controlsList = new System.Collections.Generic.List<TextBox>();

    void Page_Load(object sender, EventArgs e)
    {
        //when the user first enters the page set the count as zero
        if (!IsPostBack)
        {
            ViewState["count"] = 0;
        }
        //on every subsequent postback, check the control count
        //and recreated all the controls
        else
        {
            int controlCount = (int)ViewState["count"];
            for (int i = 0; i < controlCount; i++)
            {
                //notice that now we pass the i as parameter
                createDynamicControls(i);
            }
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls((int)ViewState["count"]);
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;
    }

    //this method takes care of creating the controls
    private void createDynamicControls(int count)
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        //now we set the control ID manually
        tb.ID = "TextBox" + count;
        PlaceHolder1.Controls.Add(tb);
        controlsList.Add(tb);
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    {
        //the sender is the control that fired the event
        //that is the control we want to change the color
        TextBox tb = (TextBox)sender;
        tb.BackColor = System.Drawing.Color.Yellow ;
    }

    protected void ButtonAdd_Click(object sender, EventArgs e)
    {
        int value = 0;
        foreach (TextBox tb in controlsList)
        {
            value += int.Parse(tb.Text);
        }
        LabelTotal.Text = value.ToString();
    }

    protected void ButtonGetValue_Click(object sender, EventArgs e)
    {
        //find the control we want using the name informed by the user in the 
        //TextBoxControlId control
        TextBox tb = (TextBox)PlaceHolder1.FindControl(TextBoxControlId.Text);
        LabelValue.Text = tb.Text;
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
        <br />
        <asp:Button ID="Button1" runat="server" Text="Add TextBox" 
            onclick="Button1_Click" />
        <br />
        <asp:Button ID="ButtonAdd" runat="server" Text="Add all values" 
            onclick="ButtonAdd_Click" />
        <br />
        Total:<asp:Label ID="LabelTotal" runat="server" Text=""></asp:Label>
        <br />
        <br />
        <asp:TextBox ID="TextBoxControlId" runat="server"></asp:TextBox>
        <asp:button ID="ButtonGetValue" runat="server" text="Get value" 
            onclick="ButtonGetValue_Click" />
        Value: <asp:Label ID="LabelValue" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>

See you next time.

Add Controls Dynamically - Part 4

The part 3 of this series was getting a bit long for my taste so I decided to break it in 2. Here we’ll continue dealing with the controls added dynamically to our page only now we’re going to access them by ID.

Related posts:

Naming the controls

By now you have noticed that we haven’t explicitly set the ID of any of our controls. Of course the controls have ID’s but they are defined automatically by the Asp.Net framework when the controls are instantiated. The reason this works is because the framework will create the names consistently the same, again and again as long as you create the controls always in the same order (we are doing that).

It is this automatically naming feature that allows our components to keep their state between requests. When the page is submitted all of the controls states are sent back from the browser to the server, but the server doesn’t know how to create the controls that’s why we had to do it every time the page is submitted. After the control has been created the Asp.Net framework is able to get it’s state from the page based on it’s name.

Ok, enough theory. Let’s give our controls and ID manually. Since the controls are added dynamically the ID’s have to be something that can be predicted. Let’s say: TextBox + a number. For this will change the createDynamicControls method to add a parameter which will be the number of the control added.

//this method takes care of creating the controls
    private void createDynamicControls(int count)
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        //now we set the control ID manually
        tb.ID = "TextBox" + count;
        PlaceHolder1.Controls.Add(tb);
        controlsList.Add(tb);
    }

When the add control button is clicked we need to pass the number to the method. We can use the control count that we’re storing in the ViewState.

protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls((int)ViewState["count"]);
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;
    }

Another place we need to change is the Page_Load event when the controls are re-created. Here for loop when we pass in the new parameter:

 for (int i = 0; i < controlCount; i++)
            {
                //notice that now we pass the i as parameter
                createDynamicControls(i);
            }

To test our new functionality we will add TextBox, a Button and a Label. We are going to inform the ID of the TextBox we want to get the value for. When the button is clicked the event will get the control and display it’s value in the label.

<asp:TextBox ID="TextBoxControlId" runat="server"></asp:TextBox>
        <asp:button ID="ButtonGetValue" runat="server" text="Get value" 
            onclick="ButtonGetValue_Click" />
        Valor: <asp:Label ID="LabelValue" runat="server" Text=""></asp:Label>

And here is the event where we get the TextBox we want:

protected void ButtonGetValue_Click(object sender, EventArgs e)
    {
        //find the control we want using the name informed by the user in the 
        //TextBoxControlId control
        TextBox tb = (TextBox)PlaceHolder1.FindControl(TextBoxControlId.Text);
        LabelValue.Text = tb.Text;
    }

Now if you add 3 TextBoxes (which will have id’s TextBox0, TextBox1 and TextBox2) and you input TextBox0 in the interface and hit the button the LabelValue control will show the value in the first TextBox.

This one was a bit longer than I would like to so I’ll try to make the next article shorter.

Here is the final version of our page after all of our modifications:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    System.Collections.Generic.List<TextBox> controlsList = new System.Collections.Generic.List<TextBox>();

    void Page_Load(object sender, EventArgs e)
    {
        //when the user first enters the page set the count as zero
        if (!IsPostBack)
        {
            ViewState["count"] = 0;
        }
        //on every subsequent postback, check the control count
        //and recreated all the controls
        else
        {
            int controlCount = (int)ViewState["count"];
            for (int i = 0; i < controlCount; i++)
            {
                //notice that now we pass the i as parameter
                createDynamicControls(i);
            }
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls((int)ViewState["count"]);
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;
    }

    //this method takes care of creating the controls
    private void createDynamicControls(int count)
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        //now we set the control ID manually
        tb.ID = "TextBox" + count;
        PlaceHolder1.Controls.Add(tb);
        controlsList.Add(tb);
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    {
        //the sender is the control that fired the event
        //that is the control we want to change the color
        TextBox tb = (TextBox)sender;
        tb.BackColor = System.Drawing.Color.Yellow ;
    }

    protected void ButtonAdd_Click(object sender, EventArgs e)
    {
        int value = 0;
        foreach (TextBox tb in controlsList)
        {
            value += int.Parse(tb.Text);
        }
        LabelTotal.Text = value.ToString();
    }

    protected void ButtonGetValue_Click(object sender, EventArgs e)
    {
        //find the control we want using the name informed by the user in the 
        //TextBoxControlId control
        TextBox tb = (TextBox)PlaceHolder1.FindControl(TextBoxControlId.Text);
        LabelValue.Text = tb.Text;
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
        <br />
        <asp:Button ID="Button1" runat="server" Text="Add TextBox" 
            onclick="Button1_Click" />
        <br />
        <asp:Button ID="ButtonAdd" runat="server" Text="Add all values" 
            onclick="ButtonAdd_Click" />
        <br />
        Total:<asp:Label ID="LabelTotal" runat="server" Text=""></asp:Label>
        <br />
        <br />
        <asp:TextBox ID="TextBoxControlId" runat="server"></asp:TextBox>
        <asp:button ID="ButtonGetValue" runat="server" text="Get value" 
            onclick="ButtonGetValue_Click" />
        Value: <asp:Label ID="LabelValue" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>

See you next time.

Add Controls Dynamically - Part 3

On the last article of this series we saw how we could assign event handlers to dynamically created controls and how the PlaceHolder control could help us add the controls in a more organized way.

Related posts:

In this article I want to show how you can keep a reference of a dynamically created control so that you can use it later somewhere else in your code. For this I’ll continue using the code created add the end of the last article of this series.

Now that you can add as many TextBoxes to the form as you want let’s say that you want to use them to input numeric values and then add up all the values for display. The first thing then is to decide where do we want to keep the reference to the controls. I chose to use a generic list of TextBox (List) but you could have chosen another structure that you feel more comfortable with.

System.Collections.Generic.List<TextBox> controlsList = new System.Collections.Generic.List<TextBox>();

Now that the list is declared and created the next step is deciding when we are going to add the controls to the list. To me using the method that adds the controls dynamically feels like the best choice, maybe someone has another idea (please share if you do). So the method would look like this:

 private void createDynamicControls()
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        PlaceHolder1.Controls.Add(tb);
        controlsList.Add(tb);
    }

Easy, right? Ok, the last step now is adding a button to the form that will call the logic to add the values within all the controls and add a label that will display the result of the addition. The logic for adding the values is very simple, once you have a list with all the controls you only need to iterate through the list, get the value of the control and add it to a variable. Here is the code:

 protected void ButtonAdd_Click(object sender, EventArgs e)
    {
        int value = 0;
        foreach (TextBox tb in controlsList)
        {
            value += int.Parse(tb.Text);
        }
        LabelTotal.Text = value.ToString();
    }

Please notice that I have assumed that all the TextBoxes are provided with valid numeric values. We could improve the code to check for empty values and conversion errors however this is not the main goal of this article. Here is a complete version of the page after all our changes were made:

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

    System.Collections.Generic.List<TextBox> controlsList = new System.Collections.Generic.List<TextBox>();

    void Page_Load(object sender, EventArgs e)
    {
        //when the user first enters the page set the count as zero
        if (!IsPostBack)
        {
            ViewState["count"] = 0;
        }
        //on every subsequent postback, check the control count
        //and recreated all the controls
        else
        {
            int controlCount = (int)ViewState["count"];
            for (int i = 0; i < controlCount; i++)
            {
                createDynamicControls();
            }
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls();
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;        
    }

    //this method takes care of creating the controls

    private void createDynamicControls()
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        PlaceHolder1.Controls.Add(tb);
        controlsList.Add(tb);
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    {
        //the sender is the control that fired the event
        //that is the control we want to change the color
        TextBox tb = (TextBox)sender;
        tb.BackColor = System.Drawing.Color.Yellow ;
    }

    protected void ButtonAdd_Click(object sender, EventArgs e)
    {
        int value = 0;
        foreach (TextBox tb in controlsList)
        {
            value += int.Parse(tb.Text);
        }
        LabelTotal.Text = value.ToString();
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
        <br />
        <asp:Button ID="Button1" runat="server" Text="Add TextBox" 
            onclick="Button1_Click" />
        <br />
        <asp:Button ID="ButtonAdd" runat="server" Text="Add all values" 
            onclick="ButtonAdd_Click" />
        <br />
        Total:<asp:Label ID="LabelTotal" runat="server" Text=""></asp:Label>
    </div>
    </form>
</body>
</html>

 

Add Controls Dynamically - Part 2

This post continues what’s been done in the Part 1

On my last post I talked about creating controls dynamically in asp.net. In this post I’m going to continue with common issues or needs that users have based on what I noticed on the Asp.Net Forums

Related posts:

First I want to talk about assigning events to the controls that we are creating, this is a simple task but still and important topic.

Adding Events

The first thing we need to do is create a method that will be the handler for the event. When the event is fired on the control this is the method we want to be called to handle the event. In our example we are using TextBoxes so we want to handle the TextChanged event and make the background of the control yellow. Here is the method:

private void TextBox_TextChanged(object sender, EventArgs e)
    {
        //the sender is the control that fired the event
        //that is the control we want to change the color
        TextBox tb = (TextBox)sender;
        tb.BackColor = System.Drawing.Color.Yellow ;
    }

The name of the method is irrelevant but it’s return type and parameters are not. If you create an event through VisualStudio you’ll notice that the signature of the method is the same.

Now that we have the method we need to set this method as the handler for the event on the controls that we are creating. Adding the method to the controls handler is as simples as this:

tb.TextChanged += TextBox_TextChanged;

If you add your controls now all of them will have this handler and it’s background will turn yellow (after submit) if you change the content of the textbox.

PlaceHolder

Up until now we’ve been adding the controls directly to the Form. This will cause the controls to be added to the end of the page after all other controls. This is fine as an example but might not be you desired result. We will you the PlaceHolder control as a container to our controls, this way we can put the PlaceHolder wherever we want in the page and the controls will be added within it.

Another advantage of the PlaceHolder control is that it doesn’t generate any html, it’s just the to serve (as it’s own name says) as a place holder. So you don’t need to worry about adding extra html just to have a place to put your controls in.

In our example I will put the PlaceHolder before the button that we are using just to show that the controls will no longer be added to the end of the page.

The new page with the events and the PlaceHolder control will be like this:

<script runat="server">

    void Page_Load(object sender, EventArgs e)
    {
        //when the user first enters the page set the count as zero
        if (!IsPostBack)
        {
            ViewState["count"] = 0;
        }
        //on every subsequent postback, check the control count
        //and recreated all the controls
        else
        {
            int controlCount = (int)ViewState["count"];
            for (int i = 0; i < controlCount; i++)
            {
                createDynamicControls();
            }
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls();
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;        
    }

    //this method takes care of creating the controls
    private void createDynamicControls()
    {
        TextBox tb = new TextBox();
        tb.TextChanged += TextBox_TextChanged;
        PlaceHolder1.Controls.Add(tb);
    }

    private void TextBox_TextChanged(object sender, EventArgs e)
    {
        //the sender is the control that fired the event
        //that is the control we want to change the color
        TextBox tb = (TextBox)sender;
        tb.BackColor = System.Drawing.Color.Yellow ;
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
        <asp:Button ID="Button1" runat="server" Text="Add TextBox" 
            onclick="Button1_Click" />
    </div>
    </form>
</body>
</html>

Stay tuned for more about dynamically created controls.

Add Controls Dynamically

Dynamic controls are an interesting topic that keep showing up in the asp.net forums. I’ll try to explain the main points about creating and working with dynamically created controls in a few articles (this one would be too long otherwise).

First of all there are two points you need to understand:
  1. Adding the controls dynamically is easy, the tricky part is getting them to stick around.
  2. The controls are not kept between postbacks therefore you have to keep recreating them every time there is a postback.
  3. The best place to recreate the controls in the Page_Load or Page_Init events.

Creating a new control is easy, it’s just a matter of instantiate the class. Adding the control to the page is also easy you only need to add the new control to the page controls.

//create a new textbox
TextBox tb = new TextBox();
//add the new textbox to the page
Page.Form.Controls.Add(tb);

You’ve seen how easy it is to add the controls but if you don’t keep recreating them when you submit the page the dynamic controls will be gone.

First understand that this need to be recreated is not exclusive for dynamic created controls. Every time there is a postback the whole page is recreated. The difference is that all the controls that are declared in the aspx page are created automatically what gives the user the impression that the controls are always there (they are not!).

Since dynamically created controls are not hard coded anywhere (obviously) you are the one responsible for recreating them.

If you create a control in the Page_Load event you’ll notice that it works just like any control already in the aspx page:

<script runat="server">

    void Page_Load(object sender, EventArgs e)
    {
        TextBox tb = new TextBox();
        Page.Form.Controls.Add(tb);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>

    </div>
    </form>
</body>
</html>

Granted that if you could create the control like this you might declare it in the aspx page as well. Ok, so let’s improve our example and create a button that adds one and only one edit to the page. If the edit already exists the button won’t do any thing.

<script runat="server">

    void Page_Load(object sender, EventArgs e)
    {
        //means that the control has been created already so you 
        //need to recreate it
        if (ViewState["tb"] != null)
        {
            createDynamicControls();
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        //means the control has not been created yet
        //create the control and store the viewstate
        if (ViewState["tb"] == null)
        {
            createDynamicControls();
            ViewState["tb"] = true;
        }        
    }

    //this method takes care of creating the controls
    private void createDynamicControls()
    {
        TextBox tb = new TextBox();
        Page.Form.Controls.Add(tb);
    }

</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="Button1" runat="server" Text="Add TextBox" 
            onclick="Button1_Click" />
    </div>
    </form>
</body>
</html>

Notice that in this version of the code I created a method responsible for creating the dynamic control. This will help to encapsulate the method creation and avoid duplicating code. Also notice that I’m using the ViewState to help maintain a variable that indicates if the control has been added yet in order to avoid duplication.

When the user clicks the button I check my ViewState variable to see if the TextBox has been created, if it hasn’t I create the TextBox, add it to the page and set the ViewState variable. Now, as long as you don’t leave the page the control and it’s state will be maintained.

We could also let the user add an undefined number of TextBoxes to the page making some small changes.

<script runat="server">

    void Page_Load(object sender, EventArgs e)
    {
        //when the user first enters the page set the count as zero
        if (!IsPostBack)
        {
            ViewState["count"] = 0;
        }
        //on every subsequent postback, check the control count
        //and recreated all the controls
        else
        {
            int controlCount = (int)ViewState["count"];
            for (int i = 0; i < controlCount; i++)
            {
                createDynamicControls();
            }
        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        createDynamicControls();
        //increment the number of controls
        ViewState["count"] = (int)ViewState["count"] + 1;        
    }

    //this method takes care of creating the controls
    private void createDynamicControls()
    {
        TextBox tb = new TextBox();
        Page.Form.Controls.Add(tb);
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:Button ID="Button1" runat="server" Text="Add TextBox" 
            onclick="Button1_Click" />
    </div>
    </form>
</body>
</html>

Now you are able to add any number of controls keeping the state of them all. On my next post I’ll keep working with dynamic created controls so stay tuned.

Related posts:

Exposing events in Web User Controls

Web User Controls are a way encapsulating code that would have to be repeated in various pages, they are definitely a huge help.

VisualStudio makes so simple to create these user controls that even novice users can do it. When the page doesn’t need to interact with the user control it is really simple, and there’s nothing to it but putting the controls you want in the user control. However the controls that are inside the user control are isolated from the Page and you can’t use it’s events. This post will show you how easy it is to overcome this issue.

Let’s say we want to create a simple control with a DropDownList, a Button and a Label. When the button is clicked the selected value in the DropDownList is copied to the label. Here’s the code for it:

<%@ Control Language="C#" ClassName="MyUserControl" %>

<script runat="server">

    protected void ButtonCopy_Click(object sender, EventArgs e)
    {
        LabelText.Text = DropDownListNames.SelectedValue;
    }
</script>
<asp:DropDownList ID="DropDownListNames" runat="server">
    <asp:ListItem>Elisa</asp:ListItem>
    <asp:ListItem>Gabriel</asp:ListItem>
    <asp:ListItem>Rafaela</asp:ListItem>
</asp:DropDownList>
<br />
<br />
<asp:Button ID="ButtonCopy" runat="server" Text="Copy SelectedValue to Label" 
    onclick="ButtonCopy_Click" />
<br />
<br />
<asp:Label ID="LabelText" runat="server" Text="Label" Font-Size="X-Large"></asp:Label>

This is simple enough, right? All you have to do is place this control in any page and this behavior is going to be replicated. What I notice that most novice programmers miss is if you need the control to send some kind of information to the page.

Say you want to have a label in your page showing the time that the Copy button of the user control is clicked. It would be easy if you could access the button click event of the button inside the user control but unfortunately you can’t. What you want is to make the event of the inner button accessible to outside the control, this is done creating a new event exposing the event you want.


<%@ Control Language="C#" ClassName="MyUserControl" %>

<script runat="server">

    //this is the event that will be exposed
    public event EventHandler ButtonClick;

    protected void ButtonCopy_Click(object sender, EventArgs e)
    {
        LabelText.Text = DropDownListNames.SelectedValue;

        //this tests if the event has been subscribed by any method
        if (ButtonClick != null)
        {
            //fires the event passing the same arguments of the button
            //click event
            ButtonClick(sender, e);
        }
    }
</script>
<asp:DropDownList ID="DropDownListNames" runat="server">
    <asp:ListItem>Elisa</asp:ListItem>
    <asp:ListItem>Gabriel</asp:ListItem>
    <asp:ListItem>Rafaela</asp:ListItem>
</asp:DropDownList>
<br />
<br />
<asp:Button ID="ButtonCopy" runat="server" Text="Copy SelectedValue to Label" 
    onclick="ButtonCopy_Click" />
<br />
<br />
<asp:Label ID="LabelText" runat="server" Text="Label" Font-Size="X-Large"></asp:Label>

Now you are able to put the control in a page and use it’s event, like in the following example:

<%@ Page Language="C#" %>

<%@ Register Src="MyUserControl.ascx" TagName="MyUserControl" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    protected void MyUserControl1_ButtonClick(object sender, EventArgs e)
    {
        LabelTime.Text = DateTime.Now.ToString();
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <!-- Notice here that I use ButtonClick event I just created in the user control -->
        <!-- Also notice that I didn't misspell the event, the On is added by a VisualStudio convention   -->
        <uc1:MyUserControl ID="MyUserControl1" runat="server" OnButtonClick="MyUserControl1_ButtonClick" />
        <br />
        <asp:Label ID="LabelTime" runat="server" Text="Label" Font-Bold="True" 
            Font-Italic="True" ForeColor="#FF3300"></asp:Label>
    </div>
    </form>
</body>
</html>

You could also subscribe to the event in the code behind just like you do with asp.net controls. Here is the code for it:

<%@ Page Language="C#" %>

<%@ Register Src="MyUserControl.ascx" TagName="MyUserControl" TagPrefix="uc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    public void Page_Load(object sender, EventArgs e)
    {
        MyUserControl1.ButtonClick += MyUserControl1_ButtonClick;
    }

    protected void MyUserControl1_ButtonClick(object sender, EventArgs e)
    {
        LabelTime.Text = DateTime.Now.ToString();
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <!-- Notice here that I use ButtonClick event I just created in the user control -->
        <!-- Also notice that I didn't misspell the event, the On is added by a VisualStudio convention   -->
        <uc1:MyUserControl ID="MyUserControl1" runat="server" />
        <br />
        <asp:Label ID="LabelTime" runat="server" Text="Label" Font-Bold="True" 
            Font-Italic="True" ForeColor="#FF3300"></asp:Label>
    </div>
    </form>
</body>
</html>

I wrote this post because I helped a few people in the asp.net forums and I hope it can help other people that are beginning to write web user controls. Happy coding guys.