Monday, October 16, 2006 9:00 PM InfinitiesLoop

TRULY Understanding Dynamic Controls (Part 3)

Part 1: Dynamic vs. Static
Part 2: Creating Dynamic Controls
Part 3: Adding Dynamic Controls to the Control Tree
Part 4: Because you don't know what to render at design time

PART 3


Adding Dynamic Controls to the Control Tree

Creating a control obviously isn't enough to get it involved in the page life cycle. A control is just an instance of a class like any other class. You have to put it somewhere so the framework knows it exists. You also probably want to initialize it into some state that suits your needs. You may even want to add even more dynamic controls to it.

When should dynamic controls be added? OnInit? OnLoad? DataBind? What if the controls I create depend on ViewState? Should I initialize the controls before or after I add them? How and when do I databind a dynamic dropdown list? These are just some of the questions you may have (or should have) asked yourself if you have used dynamic controls.

All these questions combine with your particular situation to create a mind boggling amount of permutations. I can't cover them all without writing whole book on it, so it will be up to you to try and adapt this knowledge to your own needs. But I will cover many scenarios that should be similar to your own.

Simply having the knowledge of how to use dynamic controls doesn't mean you should! In a future episode I will cover some of these scenarios and how you can simplify your life by avoiding them altogether. But for the purposes of this section, I will be using examples that you wouldn't do in the "real world". For example, there's no good reason why you should create a label dynamically when a static one would suffice. So don't take these examples literally.

 

The Control Tree

The Control Tree is a data structure. Strictly speaking, it is an implementation of an ordered tree where the type of each node is System.Web.UI.Control, and the root node's type is System.Web.UI.Page. The actual types may be derived from those types, of course. The root node isn't really a special case since Page derives from Control. It's an ordered tree because the order of the child nodes matters. That is to say, the fact that the "First Name" label control occurs before the TextBox, for example, is important (it is important but not necessarily for the reason you think... more on that later).

From a particular node in the tree (a control), you can get its child nodes (child controls) through the Controls property, which returns type System.Web.UI.ControlCollection. On the surface, this collection is not unlike any other collection. It has methods like Add, AddAt, Remove, RemoveAt, Clear, etc. You can also get to a parent node through the Parent property, which returns type Control. The root node (the page) has no parent just as described in the definition of an ordered tree, and thusly returns null.

Manipulating the Tree

Thinking about ASP.NET's control tree from the perspective of a strict ordered tree is a good thing. If you take a look at the Wikipedia definition again, it defines the following typical operations for manipulating this data structure. Adjacent to each operation I have included sample code that demonstrates it:

  • Enumerating all the items:
    private void DepthFirstEnumeration(Control root) {
        // deal with this control
     
        // visit each child
        foreach(Control control in root.Controls) {
            this.DepthFirstEnumeration(control);
        }
    }
  • Searching for an item:
    // read about the control ID syntax later
    this.FindControl("namingContainer$txtFirstName");
  • Adding a new item at a certain position on the tree:
    Control myControl = this.LoadControl("~/MyControl.ascx");
    someParent.Controls.Add(myControl);
  • Deleting an item:
    someParent.Controls.Remove(myControl);
  • Removing a whole section of a tree (called pruning):
    // build a mini-tree
    Control myControl = this.LoadControl("~/MyControl.ascx");
    myControl.Controls.Add(new LiteralControl("First name:"));
    myControl.Controls.Add(new TextBox());
    someParent.Controls.Add(myControl);
     
    // removes the entire mini-tree we just built
    someParent.Controls.Remove(myControl);
  • Adding a whole section to a tree (called grafting):
    // build a mini-tree
    Control myControl = this.LoadControl("~/MyControl.ascx");
    myControl.Controls.Add(new LiteralControl("First name:"));
    myControl.Controls.Add(new TextBox());
    // adds the entire mini-tree just built
    someParent.Controls.Add(myControl);
  • Finding the root for any node:
    // get a control's parent
    Control parent = someControl.Parent;
     
    // or... get the root to the tree given any node in it
    while(someControl.Parent != null)
        someControl = someControl.Parent;

Note that consistent with the definition of a tree, a node may only have one parent at a time. So lets say you tried to be sneaky by adding the same control to two places in the tree:

Control foo = this.LoadControl("~/foo.ascx");
this.Controls.Add(foo);
Control bar = this.LoadControl("~/bar.ascx");

this
.Controls.Add(bar);
foo.Controls.Add(bar);

No. This isn't going to throw an error or anything though. When you add a control to another control, and that control already had a parent, it is first removed from the old parent. In other words, this code is essentially the same thing as doing this:

Control foo = this.LoadControl("~/foo.ascx");
this.Controls.Add(foo);
Control bar = this.LoadControl("~/bar.ascx");
this.Controls.Add(bar);

// remove it

this
.Controls.Remove(bar);
// then add it to a different parent
foo.Controls.Add(bar);

There may be times where you would legitimately move a control from one parent to another, but it should be extremely rare.

Controls are notified when their child control collection is modified. As a control or page developer you can override these methods. This only applies to immediate children though.

protected override void AddedControl(Control control, int index) {
    // someone added a control to this control's collection
    base.AddedControl(control, index);
}
 
protected override void RemovedControl(Control control) {
    // someone removed a control from this control's collection
    base.RemovedControl(control);
}

 

Be Responsible for the Control Tree

Building the tree is your responsibility. Whether you do it dynamically or statically, it doesn't matter. One way or another, the controls of the tree must be completely reconstructed upon each and every request to the page. A common mistake made by developers is to add a dynamic control to the tree only in response to some event. For example, lets say you have a button on the form that reads, "Click here to enter your name", and when clicked you want to dynamically add a TextBox for them to enter it into:

private void cmdEnterName_Click(object sender, EventArgs e) {
    TextBox txtFirstName = new TextBox();
    this.Controls.Add(txtFirstName);
}

If you try it, you'll see the TextBox like you expect. But on the next postback, unless the user clicks this very same button a second time, the TextBox will cease to exist! Many developers who run into issues like this believe it to be due to broken ViewState. They scour the internet with queries like "how to enable ViewState for dynamic controls". It has nothing to do with ViewState. ViewState is responsible for maintaining the state of the controls in the control tree -- not the state of the control tree itself! That responsibility lies wholly on your shoulders. Dynamically created controls will not exist on the next postback unless you add them to the control tree once again. Once a control is a member of the tree, the framework takes over to maintain the control's ViewState. Of course, there are some gotchas with that, which will cover in more detail.

Okay. How then can we solve the problem stated in the example? As I've mentioned already -- and you will hear more about it later on -- when it comes to dynamic controls, just because you can doesn't mean you should! It's always better to statically declare controls whenever it is humanly possible. You will save yourself a lot of work. So the solution here is to avoid using dynamic controls in the first place.

Simply declare that TextBox on the form, but mark it as Visible="false". Then when you need it, just make it Visible. When you don't need it any more, make it not visible again. Simple as can be.

By the way, some of you may be aware of a little control out there called the DynamicPlaceHolder. I am reluctant to even mention it, because if you haven't heard of it you might go looking for it and then you will be tainted. Please, don't use it!!! I'm not saying there aren't times where a control like that could be useful -- but 99.999% of the time it is the wrong way go about solving a dynamic control problem. There are all sorts of disadvantages to using it, all of which can be avoided by solving the problem "correctly" to begin with. I believe this so firmly that if you find yourself using it, I will personally work with you via email to find a better way.

 

Revisiting the Repeater

This is somewhat off-topic but it's the perfect time to point this out, and it drives home the "responsibility" point. Remember in Part 2 we discussed how a Repeater uses it's ItemTemplate to repeatedly create controls dynamically? (If you skipped part 2 I highly recommend you go back and read it before continuing). If you stop and think about it for a moment, you might realize that the Repeater (or any other databound control) has a problem to deal with. When you call DataBind(), it creates the template one time for every data item. How is the Repeater going to follow the guideline that all controls must be recreated on every request? On a postback, you aren't databinding the repeater again (because you usually only do it when !IsPostback). So how can the repeater possibly rebuild the control tree, when it doesn't have any data to work with?

Simple. All it has to do is remember how many data items there were. When you call DataBind(), not only does it use the template for each data item, but it takes the number of data items there were and stores it in a ViewState field. Then on a postback, it waits for LoadViewState to occur, gets the count value back out of ViewState, and then uses the template count times.

Volia! As it creates each data item by using the template, the controls contained within it become "rooted" in the tree. Thanks to the framework, the instant they are rooted, their ViewState is loaded, and are restored to the state they were in when you first called DataBind() many requests ago. It's really a brilliant process. The repeater itself doesn't have to remember any state information about the controls it contains. All it has to remember is the number of items, and let the framework do the rest.

If you've ever dealt with the ItemCreated and ItemDataBound events on the Repeater (or again, any other databound control that has them), now you can appreciate the distinction, and why the DataItem is not always available.

 

The state of the Control Tree

We now have all the building blocks. We know how to create various types of dynamic controls. We know how they differ (or don't differ) from static controls. We know how to add these controls into the control tree. And we understand that it is up to us to rebuild that tree every request.

But the control tree isn't just a data structure. It's an evolving data structure. Throughout the page event sequence, the state of the tree and all the controls within it change. That has implications for dynamic controls, because your dynamic controls are going to be late-commers in the game. For example, if you add a control during the Load event, your control hasn't even had its Init event fired, but its older siblings will have already gone through Init, LoadViewState, and LoadPostData. If you are the youngest one in your family, you know how mean older siblings can be. Obviously, the framework has a way to regulate this situation (and it doesn't involve turning the car around).

Basically, dynamically added controls play "catch up" with the state of the tree. The instant a control becomes "rooted" in the control tree, the framework fast-forwards through the events in that control along with any children it contains until it has caught up with the page's current state. By "rooted" I mean that you can get to the root node of the tree by following the Parent properties. Here's an example:

PlaceHolder ph = new PlaceHolder();
ph.Controls.Add(new TextBox());
// at this point, neither the PlaceHolder or TextBox are "rooted"
 
this.Controls.Add(ph); // moment of rooting!
// now the PlaceHolder _and_ the TextBox are "rooted"

This example assumes that the control represented by "this" is already rooted in the tree. If the control that contains this code isn't rooted when this code runs, then those controls aren't rooted yet either.

So Controls.Add() isn't an innocent addition to a simple collection. It immediately has an impact on the control being added, as it "grows up" at an accelerated pace. But the steps through which the control is fast forwarded don't completely correspond to all of the events available on the page. The only events eligible for this process are (in order): Init, LoadViewState, Load, PreRender.

You must memorize that list...

Part 4, soon to come, will explore the different stages of the page event sequence in which you may be adding dynamic controls to the tree, and the scenarios they are meant for. Originally I planned for that to be part of this part, but it seems I'm so long winded I have reached Windows Live Writer's maximum length for a blog post!

Filed under: ,

Comments

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 17, 2006 2:51 AM by Plip

I'd love to read your posts, they look great, but the CSS makes it unbearable to my eyes. The code samples give me a headace (okay, usually it's because I have the IQ of a small badger, but this time it's because of the design of this page).

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 17, 2006 8:20 AM by RussianGeek

You wrote: ".Thanks to the framework, the instant they are rooted, their ViewState is loaded, and are restored to the state they were in when you first called DataBind() many requests ago."

But for load ViewState it must be saved? Who saves ViewState for Repeater's items? Repeater itself holds ViewState of all its items?

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 17, 2006 11:39 AM by InfinitiesLoop

RussianGeek -- each control in the control tree is responsible for its own viewstate. The repeater does not need to do anything special to maintain the state of its child controls, it happens automatically.

"Who saves ViewState for Repeater's items"? The framework does.

You could test this for yourself by dynamically creating a label in your OnInit. Put some code in a button handler that modifies the text of that label. Then do a regular postback. The label's text remains what it was changed to, and you didn't have to do anything special to enable that. The control just needs to be a member of the control tree to participate in state management.

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 17, 2006 11:43 AM by InfinitiesLoop

Plip -- I'm sorry it doesn't suit you. But its hard to decide if I should change my theme when I get just as many compliments about it as I do complaints. I wish I could provide a way for each user to choose their own theme, but I don't have time to write my own blogging engine.

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 17, 2006 3:40 PM by AndrewSeven

I don't much like reading in this kind color scheme.

It looks pretty, but to be able to read it, I do a CTRL+A to select the whole page. This puts most of the content as dark on white.

Pasting the whole thing into MS Word seems to produce a very readalbe output.

# TRULY Understanding Dynamic Controls (Part 3)

Wednesday, October 18, 2006 5:49 AM by DotNetKicks.com

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 24, 2006 2:01 AM by RussianGeek

Hi!

I tested a dynamic label and ViewState persisting. But is it working if I set the value of the Label on the client-side.

1) OnInit: loads dynamic label

2) Client: by JS changes the value of the label

3) PostBack

I think it must work also but it's not working in my project :-(

Can you help?

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, October 24, 2006 6:39 AM by InfinitiesLoop

RussianGeek -- in order for the server to know about changes on the client, the data has to be posted by the browser when there's a form submission. Labels are just spans on the client, so there's no way that data is going to be saved. The example I stated was for server-side updates to the label.

To accomplish that you'd have to create a new server control that mimics a label, except that it stores its value in a hidden form field just prior to form submission -- and then you'd need code to handle loading that value on the server side (by implementing IPostBackDataHandler). It's a bit outside the scope of this article, and too much to explain in a comment.

Unless you need this kind of functionality in lots of places, it might better if you just manually maintain the value in a static hidden form field, and then handle its value in the page itself. Or you can use Atlas (err, MS ASP.NET Ajax).

# re: TRULY Understanding Dynamic Controls (Part 3)

Saturday, October 28, 2006 3:06 AM by TweeZz

XML Parsing Error: no element found

Location: http://weblogs.asp.net/infinitiesloop/archive/2006/10/16/TRULY-Understanding-Dynamic-Controls-_2800_Part-4_2900_.aspx

:(

Just to show you how much I'm looking forward to your next post!

# re: TRULY Understanding Dynamic Controls (Part 3)

Saturday, October 28, 2006 4:38 AM by InfinitiesLoop

No URL hacking allowed, no sir. Good thing CS knows it's not published, because there's a draft in the works and that is indeed the url to it :) You wouldn't like it in its current form though...

The team has been pretty busy with the latest Microsoft AJAX library these past few weeks. Hopefully sometime soon I will get a chance to post some updates here. Thanks for reading :)

# re: TRULY Understanding Dynamic Controls (Part 3)

Saturday, October 28, 2006 11:54 AM by Ch

Hi - I found your articles on Viewstate and Dynamic controls very useful and it is really an eye-opener.. Kudos..

By the way - I got a question about the difference between User Controls(ascx) and Custom Server Controls(aspx).

for demo sake, let's say i have one text box control and a Gridview in User Control. But there is really no need to add them to the control tree explicitly by overriding  'CreateChildControls' method. Just it's a matter of declaring these controls at the design time.

But In case of 'Custom Server Control' , we need to really manipulate these content by overriding 'CreateChildControls' method.

So what's the real benefit of creating 'Custom Server Control' other than that it can be re-distributed as DLL.. I mean, User Control does make the job easier - isn't?

Please Correct me , If i am wrong ??

Cheers,

Ch.

P.S - there is a need in my current project, where I am tasked with 'Adding Atlas Ajax' feature to the existing 'Custom Server control that has the similar content(Gridview, Dropdownlist, Textbox,etc)'- which I find little inconvenient to add 'AutoComplete Extender', etc in dynamic fashion, though I could make it work.

# re: TRULY Understanding Dynamic Controls (Part 3)

Saturday, October 28, 2006 2:11 PM by InfinitiesLoop

Ch -- UserControls basically nothing more than Server Controls whose control tree is built automatically. So yes, they absolutely make things easier for you. Server Controls are more complex to write but have the advantage of being easily portable. Whatever works best for you, you should do that.

Server Controls are best suited for highly redistributable controls that may be useful in many different scenarios or may be used in many different applications.

By the way, there's a way to convert a UserControl into a Server Control (afterall that is basically what asp.net itself does when it generates the dynamic class based on your markup). You lose the ASCX of course, so maintaining the control still needs to be done in the original ASCX. Sorry I don't have a link for you but you should be able to find something about.

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, October 29, 2006 12:29 PM by Ch.

Dave - thanks for the guidance..

Just curious to know If atlas ajax framework does offer full support in creating any type of Atlas(&Toolkit) controls(e.g-july CTP) in 100% dynamic fashion in a Custom Server control? If so, do you have any recommendation to watch out for any pitfalls in this approach...

Your reply would really help me to decide If I should go down the path of creating Custom Server Control(DLL) or User Control(ascx) especially in context of Integrating Atlas Ajax feature..

Thank for your time & help!

Ch.

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, October 29, 2006 2:39 PM by InfinitiesLoop

Ch - dynamic scenarios are definitely feasible with the Ajax framework. For example, recently support was added for dynamic update panels.

Still, as with any controls, its generally easier to work with user controls. Personally I favor server controls because I usually make my controls useful on their own. But when developing a specific use application and you are just developing pages, user controls are often the way to go for the added agility.

# re: TRULY Understanding Dynamic Controls (Part 3)

Monday, October 30, 2006 9:00 AM by amit

i just cant wait for your next article .. Every day i check for your new articles .. You really hit the problem at the root..

Thanks for this great effort..  

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, November 21, 2006 7:09 PM by Joseph

How do you call a method of a dynamically loaded user control ?. I have a public method in the userconrol (ascx) which I wish to call from the parent ASPX page.

Thanks

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, November 22, 2006 1:41 PM by InfinitiesLoop

You need to cast the control to the type of your user control, then you can use whatever properties or methods on it you want.

The trick is figuring out how to do the cast -- it depends on how your project is setup. If you have a plain jane asp.net website project, then you will have to add a reference to the user control from the control or page you need it from (there's a <%@ Reference %> directive you can place).

If you are using a Web Application Project type (this was an out of band project template release that makes the compilation model of asp.net 2 more similar to the compiliation mode of asp.net 1), then the type of the user control's code behind should already be available to you with no special work.

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, November 23, 2006 10:29 PM by Kevmeister

Great article. Looking forward to part 4.

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, November 26, 2006 10:19 PM by Valimai

HI

How would you handle the situation where clicking a button adds a control to the page? Say I want to keep using this dynamically created control on subsequent postbacks, however, using other buttons to perform the postbac.. I now have to remember that I have created that control. Is this the best way to handle this?

(ASP 2.0)

--- ASPX ---

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Test.aspx.cs" Inherits="Test" %>

   <form id="form1" runat="server">

   <asp:button ID="Button1" runat="server" text="POST BACK" />

   <asp:button ID="butCreate" runat="server" text="CREATE" OnClick="butCreate_Click" />

   </form>

--- Code behind ---

public partial class Test : System.Web.UI.Page

{

   protected void Page_Load(object sender, EventArgs e)

   {

if (ViewState["CreateTXT"] != null && ViewState["CreateTXT"].ToString() == "yes")

CreateTXT();

   }

protected void butCreate_Click(object sender, EventArgs e)

{

CreateTXT();

ViewState.Add("CreateTXT", "yes");

}

private void CreateTXT()

{

TextBox txt = new TextBox();

txt.ID = "txt1";

txt.Text = "Default";

this.form1.Controls.Add(txt);

}

}

# re: TRULY Understanding Dynamic Controls (Part 3)

Monday, November 27, 2006 12:33 AM by InfinitiesLoop

Valimai -- that is basically what you have to do, except you should do the if(ViewState["CreateTXT"]) check in LoadViewState (after calling base.LoadViewState) instead of Page Load.

Or like I said in the article -- avoid doing it dynamically in the first place. If it must be dynamic, then if its possible to just load it every request do that -- then you just have to make it visible when you need it. And if that isn't possible... the solution you pasted is pretty much it.

# re: TRULY Understanding Dynamic Controls (Part 3)

Monday, December 04, 2006 10:46 AM by p

hi, great article. I've got a problem with dynamic controls and ajax update panel. I have Update panel on my page and PlaceHolder in it. Than i put dynamic created  User controls inside, after one of my buttons is clicked. I really need to do it this way as I have to create 'no-reload' page. The problem is: When I click one of the buttons inside user control, the content of this control disappears. I can't figure it out.  Could you help me? thx in advance

# re: TRULY Understanding Dynamic Controls (Part 3)

Monday, December 04, 2006 1:23 PM by InfinitiesLoop

p --

Controls must be re-created on every request. If you are only creating the dynamic control in response to a button click, then it isn't going to be recreated again on the next posback (unless it was via the same button).

It would be easier on you if you didn't do it dynamically -- just declare the control as invisible, then make it visible when you need it and vise-versa. If that doesn't suit your needs you will have to be sure to load the control dynamically on other postbacks. You can do that by setting a ViewState variable in your button click event, then override the LoadViewState method, and if the variable exists (after calling base.LoadViewState), load the control. In the button click you will have to Controls.Clear the placeholder, since the control may already be loaded.

I hope that makes sense... feel free to send me some code examples via the contact form and we can work on it privately.

# re: TRULY Understanding Dynamic Controls (Part 3)

Friday, December 08, 2006 1:05 PM by Jonathan B.

RE: OnInit vs OnLoad

Scenario:  I have a drop-down list of two "form" choices and a placeholder for the "form" to be loaded.  I want to display the selected form when the SelectedIndexChanged event is fired.  Therefore, the LoadControl( formX.ascx ) will occur after the OnInit (and even after OnLoad) event(s).  The form selected is saved in a ViewState entry to be reloaded during the postback.

Problem: To see what's wrong with this, I might as well quote the MSDN docs:

"When you load a control into a container control, the container raises all of the added control's events until it has caught up to the current event.  However, [... f]or an added control to participate in postback data processing, including validation, the control must be added in the Init event rather..."

So while the form loads properly when added in the SelectedIndexChanged event or in the OnLoad (i.e., when reloaded in a postback from the ViewState-saved form name), it is a non-functional form since postback processing does not occur for controls loaded after OnInit.  Since ViewState is not available in the OnInit, using the ViewState method only works if we load the control from the OnLoad event!

Solution?: OK, so maybe I can give up the nifty SelectedIndexChanged event and use of ViewState.  I could just use the drop-down's value from the Request[] variable from within the OnInit.  This does work.  However, this little example is a prototype for a more complicated real-world example where I have a menu (which changes based on who logs into the system) and potentially 100s of forms.  Plus, I think using ViewState like your examples make a lot of sense.

Is there a way out of this circular problem of OnInit form-loading vs OnLoad/ViewState form-loading?  If not in this version, in a future version of ASP.NET?

Thanks!

P.S. For bonus points, how do I clear the ViewState and postback values that share the same textbox IDs when switching from one form to another?

# re: TRULY Understanding Dynamic Controls (Part 3)

Friday, December 08, 2006 1:49 PM by InfinitiesLoop

Jonathan -- whew, okay. Here we go.

First of all, dynamically loaded controls that are loaded during OnLoad _do_ go through postback processing, but not until after OnLoad. This behavior usually allows these controls to act normally, but it can cause you trouble if you depend on the state of those controls from OnLoad, since they won't be updated yet.

But... what you should do is override the LoadViewState method. Call base, and then immediately after, reload the user control. That way you are loading the control before the first pass to process postback data. That is basically what databound controls do -- as soon as viewstate is loaded they repopulate their dynamically generated UI, and then all the controls that were created live happilly ever after.

Bonus points... I love bonus points... what can I use them for?

You should make sure each potential form gets an ID that is both unique to it and won't ever change while someone sits on the page. The easiest way to do that would be to create an ID based on the data element's ID, like form1, form2, form3, etc. Be sure to assign that ID after loading the control in the indexchanged event as well as in LoadViewState. It is crucial that you give the form an actual ID in this scenario -- because you will be loading it at different stages of the lifecycle. First it is loaded after OnLoad (in the event) and then later it is loaded before OnLoad (in LoadViewState). Depending on the form, if you allow the control to assume an automatically generated ID, the ID it is assigned could be different on a postback, and you will likely lose state.

That being said, even without this recommendation, I'm not sure I see how you could get "crossover" of viewstate or form values. Imagine you are on form1, and the user has just selected form2 and a postback is occurring. First, LoadViewState is called, and you load form1 in order to restore the form to its known state. form1 now loads its ViewState. Then SelectedIndexChanged fires. You remove form1 from the page (ala placeholder.controls.clear() or something), and then add form2. Finally, form2 saves its viewstate.

If you are seeing some kind of crossover I'm interested in hearing exactly what the setup is.

_Happy_ coding!

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, December 10, 2006 2:21 PM by InfinitiesLoop

Jonathan -- sorry for the delay, community server classified your comment as 'spam'. Hmmm... :)

j/k

When I said set the ID of the form I didn't literally mean the FORM tag. I mean the user control that you are loading. You seemed to refer to them as 'dynamic forms' that you are loading, hence the confusion.

As for the docs, notice it also says 'participate in validation'. I havent looked into that but there could be truth to that statement. Not sure. Either way... as with any system, the docs are usually just a starting point. Truly understanding the system takes experience.

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, December 31, 2006 3:59 PM by Dave

Hey There,

Great articles! I'm still kindof working towards my ahem moment, but thought I would ask you a question anyways.

You mentioned,

"If you have ever dealt with dynamic controls before, you know that they really are different. It's true that your experience with them may differ from that of static controls, but it's important to understand exactly why there's a difference. The difference has to do with when the control enters the control tree. When the framework adds controls, it does it extremely early in the event sequence. The only thing that happens first is the control or page constructor. That's why in OnInit or OnPreInit, the controls already exist and are ready for use (well, master pages can 'mess' with that process, but this is still a true statement)."

can you elaborate on what 'mess' maters pages can cause on this topic?

Thanks and happy New Year.

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, December 31, 2006 5:34 PM by InfinitiesLoop

Dave -- thanks :)

The controls on the page are moved after they are added, when there is a master page. So by 'mess with', I just mean they may be shuffled around. If you override OnPreInit you may need to call base.OnPreInit before the control instances are assigned to their respective declared variables. I'm not 100% sure about that, its been a while since it affected me.

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, January 02, 2007 11:22 PM by InfinitiesLoop

Keith -- Assuming you are only databinding it on the initial page load, you will have to enumerate over the items and update them. But you need to do it after they've been restored and loaded postback values. You also want to do it after ViewState has been saved so that your changes don't dirty viewstate. The change doesn't need to be persisted into viewstate because you're going to update it every request.

The only place you can do that is ... Render.

Override render on your page (or control, whichever you are adding it to), and do something like this:

foreach(RepeaterItem item in repeater.Items) {

   Control chk = item.FindControl("chk");

   Control ddl = item.FindControl("ddl");

   ddl.Visible = chk.Visible;

}

The ID passed to FindControl should match the declared ID of the checkbox and dropdownlist.

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, January 03, 2007 12:34 PM by Rick

GREAT article!

From one of the comments above, I got the impression that controls such as the asp.net repeater build their dynamic controls when the   LoadViewState is called i.e.

 protected override void LoadViewState(object savedState)

   {

       base.LoadViewState(object savedState);

       this.EnsureChildControls();

   }

   protected override void CreateChildControls()

   {

     //Construct the controls here

   }

I have built a control that has a dynamic number of updatepanels (each updatepanel represents a tab). I plan on storing the number of tabs and the selected tab in the control state.

So my question is can I do/should I do the following instead:

 protected override void LoadControlState(object savedState)

   {

      //load the controlstate data here

       this.EnsureChildControls();

   }

   protected override void CreateChildControls()

   {

     //Construct the controls here

   }

It strikes me that the num of tabs and the selected tab should be stored in the controlstate rather than the viewstate?? Thoughts??

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, January 03, 2007 12:41 PM by InfinitiesLoop

Rick -- I think I'd stick with ViewState in this case. You should be very careful when using ControlState, because users of your control will have no way to disable it. Perhaps the user of your control wants to disable ViewState, and then redatabind it every request? If you use ViewState you are giving them that option.

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 4:15 AM by Rick

In this case, I actually dont want them to be able to disable this functionality! Many thanks for your help!

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 4:45 AM by Rick

Hi again. LoadViewState doesnt always seem to be called. Is it only called when there is available viewstate?

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 5:43 AM by Rick

The repeater control allows the user to add a datasource and databind in a pages init event.

My tab control dynamically pre-builds updatepanels in the method createchildcontrols (I call ensureChildControls in the LoadViewState method).  Into these updatepanels I can add content via an Add method i.e.

myTabber.Add(control);

However, if the developer tries to add a control before the updatepanels have been built (as i said the updatePanels are built in the createChildControls method) an error is thrown.

(Note that '10' updatepanels are always prebuilt to reduce the amount of content posted back each time a new tab is created, when all 10 updatepanels are full, a further 10 are added - If I didnt do this, everytime I added a new tab, all the tabs would be updated).

The first time I create my tab control, because there will be no viewstate associated with it, the CreateChildControls wont fire until after PageLoad (LoadViewState wont have been called), which means that tabber.Add(control) can not be used until after pageLoad.

This suggests to me that I should queue the add method and then actually do the real adding of the controls later after createChildControls has been called.

Do you agree? Is this how the Repeater and DataGrid do this?

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 10:47 AM by Tom

I am also creating a tab control.

Should my tab control remember the controls added to it?

should the developer be forced to manually add each control to the tab control on each postback, or should the tab control rebuild them automatically?

Your opinion would be appreciated!

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 12:35 PM by InfinitiesLoop

Rick -- The way Repeater and DataGrid do it is by placing control over their structure in the hands of the user of the control.

You can't repeater.Add(item) to grow a repeater by one. To do it, you have to change the underlying datasource, reassign the datasource to the repeater, and re-databind it.

If you want to be consistent with DataBound controls, you should work on a similar model. Your datasource is the list of tabs (maybe just a number, for how many). The user of your control must assign that list (or number) then call DataBind. I understand you are trying to increase perf by growing 10 at a time... its an interesting idea, but it does really complicate things. I think you'll find that rebuilding the tabs doesn't really take too much time.

If you'd rather stick to your current model, you'll have to work around issues like this creatively and in your own way. Your  queueing idea is one way to go.

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 12:40 PM by InfinitiesLoop

Tom -- Well, if you design it correcty, you can have it both ways. A databound control can work with ViewState disabled if it is databound every request. Or, it can work with ViewState enabled if it is only databound once. The beauty is that it's up to the user of your control, giving them more flexibility. And it's consistent with the existing controls, so they should already know how to use it.

So... expose a DataSource property, which is appropriate for whatever source you need. Then override DataBind and build the tabs there. Remember how many there are in ViewState, and just after LoadViewState, rebuild that many tabs (thats the basic scenario -- you may need to use Templates if you plan on the user having content declared inside the tabs). In your DataBind method be sure to clear your control tree first, since you may be databound after already rebuilding the tabs.

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, January 04, 2007 12:41 PM by InfinitiesLoop

Rick -- as you figured out, LoadViewState doesn't occur on the first request. It should always be called subsequently.

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, January 17, 2007 6:04 AM by Rick

When ever I create a WebControl, I build its child controls in the CreateChildControls method.

I then use the EnsureChildControls throughout my control i.e on Controls collection accessor.

When building usercontrols that require controlstate to determine which controls need to be built, I also build my controls in  CreateChildControls.

As a rule of thumb should I try to always create dynamic child controls in the CreateChildControls method?

How do you approach this problem?

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, January 17, 2007 2:13 PM by InfinitiesLoop

Rick -- CreateChildControls is the right place to setup your control tree when you're creating a custom control. But not for UserControls, because their control tree is parsed and created long before that.

You need to keep in mind that you shouldn't depend on state information to determine what controls to create. The reason is simple: You don't know which will come first, the state or the controls. If you depend on a property, someone may change the value after you create the controls. If you depend on ViewState or ControlState, what if EnsureChildControls is called before it has been loaded?

Instead, you need to create every possible control that you might need, and make sure they are all added to your control collection. Then, you can control the visibility of those controls through state. For example if you're depending on a property value, and it gets set to ABC, which means control X should not be visible but control Y should be, then you can set their visible properties as such right away, within the property setter.

If this scheme doesn't work because you have a variable number of controls that could be created based on the data, then what you really need to create is a DataBound and/or Templated control.

# re: TRULY Understanding Dynamic Controls (Part 3)

Friday, January 26, 2007 8:41 AM by Ameya

Hi Dave,

Nice article. I am trying to add controls dynamically to updatepanel (the ajax one, of course) and somehow they disappear on postback.. be it ajax postback or a normal one.  

TextBox t = new TextBox();

UpdatePanel2.ContentTemplateContainer.Controls.Add(t);

This control loads the first time, but if any control, like say a button, outside the update panel, causes a postback, this text box does not load again. This could be expected to happen, but what is the solution to this?

# re: TRULY Understanding Dynamic Controls (Part 3)

Friday, January 26, 2007 1:31 PM by InfinitiesLoop

Ameya -- where is this code exactly? OnLoad, OnInit, etc? Is it occuring every request?

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, February 06, 2007 8:14 AM by Rick

In order to access view state and control state,  I build my controls in the CreateChildControls method of my bespoke control to ensure that LoadViewstate and LoadControlState will already have been called.

This is fine, I can access controlstate and viewstate.

However, when I build a textbox in the createchildcontrols method, it doesn't remember its typed in value.

i.e.

protected override CreateChildControls()

{

Textbox tbx = new TextBox();

tbx.id="test";

this.controls.add(tbx);

}

It does remember its values, however, if I build it during the oninit method.

Why doesn't it remember  the values when its built during the CreateChildControls phase? Is this a TrackViewState issue???

Everytime I think I understand this stuff, I realise I dont at all!

Very, very frustrating!

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, February 06, 2007 1:47 PM by InfinitiesLoop

Rick -- it may be ok in your specific scenario, but keep in mind that there is no contract anywhere that says CreateChildControls runs after LoadViewState. It can be called at ANY time. If someone calls FindControl on your control, that calls it. If someone derives from your control, they might call it earlier than you expect. You really should try hard not to depend on state information from CreateChildControls. Usually there's no need for it, because there are other ways to handle it, such as by creating all the controls you might need, then showing/hiding the appropriate one once LoadViewState occurs. Or if that isn't enough, you can create the controls directly from LoadViewState (this is basically what databound controls do).

But anyway, moving on :)

The latest CreateChildControls can be called in during the PreRender phase. At that point, its too late for controls to load their posted values. You might think thats the problem... however, when a control has a posted value in the post data, the framework tries to 'find' the control by calling FindControl. FindControl ends up calling EnsureChildControls, which calls CreateChildControls, so your textbox should be created just in time. If that is not happening it could be because the framework can't find that control, or that the textbox is not posting anything (it could be outside of a form tag, or it could be 'disabled' in html). Try one easy thing for me... actually two. First, see what happens if you remove setting the ID of that TextBox. So remove the id ="test" line. Whether that works or not, put it back, and make your control implement INamingContainer.

# re: TRULY Understanding Dynamic Controls (Part 3)

Sunday, February 11, 2007 9:27 PM by darren

I've got the same problem when trying to add a control dynamically in an update panel. Basically it dosen't seem to invoke anything when you try a postback!!! In fact it actually disappears. If I put the control into the update panel normally (i.e register it at top of page and insert the server tags) then it works fine. Something that looks like it needs to be ironed out for another ajax release.

# re: TRULY Understanding Dynamic Controls (Part 3)

Monday, February 12, 2007 2:07 AM by InfinitiesLoop

darren -- can you post some code? It definitely shouldn't disappear, so there may be a something else going on.

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, February 20, 2007 4:56 PM by David

I think I have a similiar issue with my controls as above. I have a CheckBoxList that I am dynamically creating and adding to the custom control (which inherits WebControls.Panel) when the custom Control is created.

The user selects their check boxes, then hits the Submit button, but then CreateChildControls is called again and the CheckBoxList has lost all of its databound controls, not to mention having no clue what the user checked (obviously because those checkboxes are gone.

Imports System.Web.UI.WebControls

Imports System.Web.UI

Imports System.Web

Imports DjjCommonComments.DJJ_Functions

Public Class CommentPanel

   Inherits System.Web.UI.WebControls.Panel

   Implements INamingContainer

   Private pnl As New System.Web.UI.WebControls.Panel

   Private txtComments As New System.Web.UI.WebControls.TextBox

   Private WithEvents btnEnterComments As New System.Web.UI.WebControls.Button

   Private f_strBizType As String

   Private f_strApplicationType As String

   Private f_strDocumentTypeCode As String

   Public cblCommentTypes As New CheckBoxList

   Public btnCancel As New WebControls.Button

   'Dim tbFirst As TextBox

   'Dim tbSecond As TextBox

   'Dim tbResult As TextBox

   Sub New()

       MyBase.New()

       Me.Width = Unit.Pixel(650)

       Me.Height = Unit.Pixel(550)

       Me.Wrap = True

       Me.BackColor = Color.Yellow

   End Sub

   Protected Overrides Sub CreateChildControls()

       Dim litLineBreak As New Literal

       Dim litSpaces As New Literal

       litLineBreak.Text = "<br /><br />"

       litSpaces.Text = "     "

       txtComments.Width = 580

       txtComments.Height = 100

       txtComments.TextMode = TextBoxMode.MultiLine

       Me.cblCommentTypes.RepeatColumns = 2

       Me.cblCommentTypes.Width = 580

       Me.cblCommentTypes.Height = 400

       Controls.Add(txtComments)

       Controls.Add(litLineBreak)

       Controls.Add(cblCommentTypes)

       btnEnterComments.Text = "Enter Comments"

       btnCancel.Text = "Cancel"

       Controls.Add(btnEnterComments)

       Controls.Add(litSpaces)

       Controls.Add(btnCancel)

       txtComments.Text = "test"

       ChildControlsCreated = True

   End Sub

   Protected Overrides Sub render(ByVal output As HtmlTextWriter)

       EnsureChildControls()

       RenderChildren(output)

   End Sub

   Private Sub btnEnterComments_click(ByVal sender As Object, ByVal e As EventArgs) Handles btnEnterComments.Click

Try

           'Loop through Comment Types and get array of comment types

       Catch ex As Exception

       End Try

   End Sub

   Public Sub Initialize(ByVal strBizType As String, ByVal strApplicationType As String, ByVal strDocumentTypeCode As String)

       Me.f_strApplicationType = strApplicationType

       Me.f_strBizType = strBizType

       Me.f_strDocumentTypeCode = strDocumentTypeCode

       Me.ViewState.Add("strBizType", strBizType)

       Me.ViewState.Add("strApplicationType", strApplicationType)

       Me.ViewState.Add("strDocumentTypeCode", strDocumentTypeCode)

       'Fill CheckBoxLIst with requested Comment Types based on parameters passed in from calling application

       DJJCommonComments.DJJ_Functions.DJJ_Comments.FillCommentCheckBoxList(Me.cblCommentTypes, Me.ViewState.Item("strBizType"), Me.ViewState.Item("strApplicationType"), Me.ViewState.Item("strDocumentTypeCode"))

       EnsureChildControls()

   End Sub

   Protected Overrides Sub LoadControlState(ByVal savedState As Object)

       MyBase.LoadControlState(savedState)

   End Sub

End Class

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, February 20, 2007 6:09 PM by InfinitiesLoop

David -- what is this Initialize() function? Whomever is calling it, do they call it every request or only on non-postbacks? During which page event do they do it?

The fact you aren't calling EnsureChildControls until after building the checkbox list's data means the checkbox list will not be "rooted" in the control tree when this is done -- unless someone else is calling EnsureChildControls from somewhere else -- which means it will not store its data in viewstate, because it wasn't tracking yet.

If you intend for the call to Initialize to 'stick' in viewstate so that it doesn't need to be called on postbacks, you should call EnsureChildControls at the TOP of Initialize not the bottom.

Essentially you've created a DataBound control. You should follow the pattern that other databound controls use. They should call DataBind, not Initialize. The data they pass in through Initialize should be properties instead. This makes your control not only look and behave like other databound controls, but it makes it possible to use completely declaratively. You cant call a method declaratively, but you can set properties. Know what I mean?

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, February 21, 2007 9:20 AM by David

Just moving the EnsureChildControls() call helped a LOT. I just started creating custom controls this week, so I guess the Initialize sub was my way of allowing the user to set some values, save those in a ViewState, and then load the databound CheckBoxList.

They would be calling Initialize in the Page_load on non-post-backs. Moving EnsureChildControls() allowed that to work, but now that you pointed out that this is really just a Databound control, I'm intrigued on how I can make it more like other databound controls.

Can you give me an example of how they would do this with Databind? Do I override the Databind function, get the properties they set when the control was created, and then load my checkbox list within that? When does the user call Databind()?

Thanks for your help!

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, February 21, 2007 1:30 PM by InfinitiesLoop

David -- you've got it. You override DataBind and just refer to the properties they have set. It is at that point when you commit things "to memory", viewstate.

When does the user call it? Whenever they want to! They may want to disable viewstate can databind you every request. Or they may do the usual only on non-postbacks. It shouldn't really matter to you. Just be sure and 'clear out' any existing controls or data, as they could possibly call databind on you more than once in a request.

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, March 07, 2007 8:40 AM by pr.arun

Realy useful articlefor the Developers as well as learners, if possible please post an example for dynamically addition of controls to a aspx page and the sequential methods we have to follow for that. Those controls values should be editable and should be saved in database during postback. Thanx for this and the all the best for the next...

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, March 20, 2007 3:48 PM by zabolots

Where's part 4 of the series??

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, March 20, 2007 4:03 PM by zabolots

I create a bunch of dynamic controls (including AjaxControlToolkit controls) in OnInit() using ParseControl() from a string that I build up by parsing a custom XML file.  I then add the resulting control tree into a placeholder control that I have on the form. This was all working fine.

Then, in an attempt to reduce the overhead of parsing the XML and calling ParseControl() every time, I store the resulting control tree in the Session object. So OnInit() first looks for the control tree in the Session object. If it doesn't find it, it build it from XML & ParseControl() and adds it to the Session object. Subsequent requests (e.g. post backs) now find the control tree in the Session object and add it directly to the placeholder control without reparsing.

For some reason, there are problems with some postbacks (specifically those where I need to modify properties of some of the dynamic controls). When I attempt to do this using the control tree retrieved from the Session object the AtlasControlToolkit throws the following exception: "Extender controls may not be registered after PreRender."

Is there some reason that using the control tree from the Session object would screw up the page lifecycle in some way that it would cause this problem? If I skip the Session usage and reparse the XML and recreate the control tree for each request, the problem goes away.

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, March 20, 2007 4:24 PM by InfinitiesLoop

Yes that is a problem. You cannot "cache" controls. They go through the page lifecycle and data internal to them changes. Controls are meant to be thrown away after they are used, they cannot be used to serve multiple requests. There are all sorts of reasons why it won't work.

What you should do instead -- don't use parse control in the first place. I don't know what your xml file looks like, but you should be able to design this such that you parse the xml file into meta information about the controls (control type, properties, etc). You cache THAT. And then you use that meta information tree to create the controls fresh every request. You could also potentially go straight from the xml file into control instances, then there's really nothing to cache except the xml itself. Basically -- cut out the middle man -- result of parsing xml is controls, not a string which is parsed into controls.

Again I don't know what your xml file looks like, but have you considered using templates in ASCX files? You can use LoadTemplate to load an ASCX file, and then call InstantiateIn(placeHolder) to instantiate the tree. This would have all sorts of advantages -- the format of the file isn't some custom xml format, its actual asp.net markup, the control is parsed and compiled just like regular user controls.

You could also dynamically write the ASCX file as a result of parsing your xml file, then call LoadTemplate on that.

Lots of options.... none of which involve ParseControl.

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, April 17, 2007 11:24 PM by jackie trillo

Hi, i have have a need to create a grid with nested grids for each data item. I am using one grid (the parent) and a template column with a placeholder (you said to avoid). If the record has details i add a subgrid in the position of the place holder, the result is an n level nested grid.  I am creating this structure on the oninit stage.

As you can imagine it has become tricky. Do you have a better approach that will achive the same result?

Jackietrillo@bellsouth.net

# re: TRULY Understanding Dynamic Controls (Part 3)

Wednesday, April 18, 2007 1:40 AM by InfinitiesLoop

You can put the nested grid directly within the template column. You don't need a placeholder. You can assign the inner grid's DataSource property the same way you can assign other properties via databinding syntax...

<asp:DataGrid ... DataSource='<%# Eval("Orders") %>'>

# re: TRULY Understanding Dynamic Controls (Part 3)

Thursday, April 26, 2007 5:44 PM by D King

Hi - I agree with everyone here that your articles are exceptional. It does very much help to have something beyond the stock documentation to draw from.

With that said, I was hoping you could help me with a problem with ASP.NET Ajax and dynamically added ASCX controls. I agree that you should statically add controls whenever possible but unfortunatley my project calls for adding and removing ASCX controls from a table structure on demand. I actually have this working very well except for an issue when I add a new ASCX control, the Ajax functionality fails to work the first time I click a button or change a text box etc. to fire off the partial page update. Once I do it once and it fails (essentially does nothing except kind of flicker and the value I entered disappears) it then works fine and I am in business until I add another control. I am removing all controls each time I add a new ASCX control and rebuilding the entrie thing each time I reload the page.

Any idea on what I might be doing wrong? The ASCX controls have their own UpdatePanel set to Conditional update and then they are placed in a new row, in a new cell in a Table that also is wrapped up in an UpdatePanel.

I can provide a code sample if needed. This one really has me stumped and I can't seem to find anything on the ASP.NET Ajax forums that has been answered. Actually I was lucky enough to find the link to your article there ;-)

Thanks for any help or guidance.

Don

# re: TRULY Understanding Dynamic Controls (Part 3)

Tuesday, May 08, 2007 12:08 PM by Karissa

First of all, your article is extremely helpful.  I've read and re-read it over and over (the concepts can be quite confusing!).  However, it seems like I'm following all the "rules" but still I'm losing viewstate in my dynamically added user controls upon postback.  I have a button on my aspx page that when clicked, adds to an array stored in session state, clears the page of any user controls and then calls Page_Init, which then loops through the session state array and loads the controls back to the page.  Everything is working perfectly, except that the controls lose their viewstate values.  For example, when the aspx page first loads, it contains one user control, which has a number of checkboxes.  I select a few checkboxes and then click 'Add another user', which adds another user control, but none of the checkboxes in the first user control retain their values.  Here's some code for clarification:

'Init Method

Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init

       InitializeComponent()

       'Clear the controls from the place holder

       If IsPostBack Then

           For i As Integer = 0 To Me.UserControlIDList.Count - 1

               'LoadViewState(ViewState)

               'Load the controls again

               LoadUserControl(i)

           Next

       Else

           Dim sControlID As String

           sControlID = "User" & Me.UserControlIDList.Count + 1

           Me.UserControlIDList.Add(sControlID)

           LoadUserControl(0)

       End If

   End Sub

'Load Control Method

Private Sub LoadUserControl(ByVal index As Integer)

       Dim newUserControl As New User

       newUserControl = CType(Page.LoadControl("User.ascx"), User)

       newUserControl.ID = Me.UserControlIDList(index)

       newUserControl.EnableViewState = True

       Me.FindControl("ContentPlaceHolder").Controls.Add(newUserControl)

End Sub

'Add another user control

Private Sub lnkAnotherUser_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAnotherUser.Click

       Dim sControlID As String