Dynamically added controls in Asp.Net

Solution 1:

I agree with the other points made here "If you can get out of creating controls dynamically, then do so..." (by @Jesper Blad Jenson aka) but here is a trick I worked out with dynamically created controls in the past.

The problem becomes chicken and the egg. You need your ViewState to create the control tree and you need your control tree created to get at your ViewState. Well, that's almost correct. There is a way to get at your ViewState values just before the rest of the tree is populated. That is by overriding LoadViewState(...) and SaveViewState(...).

In SaveViewState store the control you wish to create:

protected override object SaveViewState()
{
    object[] myState = new object[2];
    myState[0] = base.SaveViewState();
    myState[1] = controlPickerDropDown.SelectedValue;

    return myState
}

When the framework calls your "LoadViewState" override you'll get back the exact object you returned from "SaveViewState":

protected override void LoadViewState(object savedState) 
{
    object[] myState = (object[])savedState;

    // Here is the trick, use the value you saved here to create your control tree.
    CreateControlBasedOnDropDownValue(myState[1]);

    // Call the base method to ensure everything works correctly.
    base.LoadViewState(myState[0]);
}

I've used this successfully to create ASP.Net pages where a DataSet was serialised to the ViewState to store changes to an entire grid of data allowing the user to make multiple edits with PostBacks and finally commit all their changes in a single "Save" operation.

Solution 2:

You must add your control inside OnInit event and viewstate will be preserved. Don't use if(ispostback), because controls must be added every time, event in postback!
(De)Serialization of viewstate happens after OnInit and before OnLoad, so your viewstate persistence provider will see dynamically added controls if they are added in OnInit.

But in scenario you're describing, probably multiview or simple hide/show (visible property) will be better solution.
It's because in OnInit event, when you must read dropdown and add new controls, viewstate isn't read (deserialized) yet and you don't know what did user choose! (you can do request.form(), but that feels kinda wrong)

Solution 3:

After having wrestled with this problem for at while I have come up with these groundrules which seems to work, but YMMV.

  • Use declarative controls whenever possible
  • Use databinding where possible
  • Understand how ViewState works
  • The Visibilty property can go a long way
  • If you must use add controls in an event handler use Aydsman's tip and recreate the controls in an overridden LoadViewState.

TRULY Understanding ViewState is a must-read.

Understanding Dynamic Controls By Example shows some techniques on how to use databinding instead of dynamic controls.

TRULY Understanding Dynamic Controls also clarifies techniques which can be used to avoid dynamic controls.

Hope this helps others with same problems.

Solution 4:

If you truly need to use dynamic controls, the following should work:

  • In OnInit, recreate the exact same control hierarchy that was on the page when the previous request was fulfilled. (If this isn't the initial request, of course)
  • After OnInit, the framework will load the viewstate from the previous request and all your controls should be in a stable state now.
  • In OnLoad, remove the controls that are not required and add the necessary ones. You will also have to somehow save the current control tree at this point, to be used in the first step during the following request. You could use a session variable that dictates how the dynamic control tree was created. I even stored the whole Controls collection in the session once (put aside your pitchforks, it was just for a demo).

Re-adding the "stale" controls that you will not need and will be removed at OnLoad anyway seems a bit quirky, but Asp.Net was not really designed with dynamic control creation in mind. If the exact same control hierarchy is not preserved during viewstate loading, all kinds of hard-to find bugs begin lurking in the page, because states of older controls are loaded into newly added ones.

Read up on Asp.Net page life cycle and especially on how the viewstate works and it will become clear.

Edit: This is a very good article about how viewstate behaves and what you should consider while dealing with dynamic controls: http://geekswithblogs.net/FrostRed/archive/2007/02/17/106547.aspx