One of the problems I've come across more than once in the recent past is with dynamically generated controls in ASP.NET pages and the fact that they seem to disappear in the form's post back.
The basic reason for this is that dynamically generated form controls, which are usually generated after the
OnInit event of the page don't exist in the
ViewState and therefore don't perpetuate between form posts. The fix therefore, is to build, or re-build, any dynamically generated form controls in the
OnInit event of the page. This can seem quite unintuitive, especially if you're posting back to another page - but it's still necessary. Let's just take a quick peak under the hood to see why...
The Page Lifecycle
As you are probably aware, the ASP.NET page passes through various stages in its lifecycle. Although you don't usually need to know much about all of these stages, it is important to understand the difference between when the
OnInit event fires and the
OnInit event fires, as you might expect, before the page loads. At this stage controls can be added to the
ViewState (the place where controls and their states are held between page views), but post back information can't be accessed.
OnLoad event fires after the page has loaded and control states have been retrieved from
ViewState. At this stage, controls can't be added into
PostBack data can be retrieved.
This understanding is fundamental to persisting the state of dynamically generated controls between page views.
OnInit event fires every time the page responds to an event, even if you're posting back to another page... So, if you have page1.aspx, which contains a submit button which posts back to page2.aspx, the
OnInit event code on page1.aspx will still run before diverting to page2.aspx, i.e.:
- page1.aspx loads for the first time,
- User clicks button which posts back to page2.aspx
OnInit event fires... page2.aspx page loads...
Let's Take An Example
Let's say, for the sake of simplicity, that you have a form which dynamically generates text boxes. You want to retrieve the value of these text boxes after a submit button is clicked.
So, first things first, let's look at the simple .aspx page:
<form id="form1" runat="server">
<asp:Panel ID="NumControlsPanel" runat="server">
<asp:Label ID="Label1" runat="server"
<asp:TextBox ID="NumControls" runat="server"></asp:TextBox>
<asp:Button ID="ShowControlsButton" runat="server" Text="Show Controls"
<asp:PlaceHolder ID="ControlsPlaceHolder" runat="server"></asp:PlaceHolder>
So, we have a
Panel control which holds a
TextBox that the user can use for setting the number of text boxes that should be gererated, and a button for then showing these controls. The code behind the button could look like this:
protected void ShowControlsButton_Click(object sender, EventArgs e)
if (NumControls.Text != "")
if (int.TryParse(NumControls.Text, out _numControls))
This calls a method,
BuildDynamicTextBoxes, which generates the text boxes by adding them to a
protected void BuildDynamicTextBoxes(int numTextBoxes)
for (int i = 0; i < numTextBoxes; i++)
TextBox _textBox = new TextBox();
_textBox.ID = "txt" + i.ToString();
Literal _lit = new Literal();
_lit.Text = "<br/>";
Button _submitButton = new Button();
_submitButton.Text = "Submit";
_submitButton.ID = "btnSubmit";
"ViewStateTest.aspx?ReloadControls=yes&NumControls=" + numTextBoxes;
As you can see, the method creates a dynamic number of text boxes and then appends a submit button to the
PlaceHolder. Notice that the
PostBackURL property of the submit button adds a couple of parameters to the
querystring. These are important, as we shall soon see...
The Crucial OnInit Event
Let's look at the crucial
protected override void OnInit(EventArgs e)
if (Request.QueryString["ReloadControls"] != null &&
Request.QueryString["ReloadControls"].ToString() == "yes")
if (Request.QueryString["NumControls"] != null)
int _numControls = int.Parse(Request.QueryString["NumControls"].ToString());
Here, we check the
querystring and, if the
querystring contains the
ReloadControls parameter, the code then queries the number of controls from the
NumControls parameter. It uses this information to call the
If this code doesn't exist in the
OnInit event, you'll find that your dynamically generated controls disappear in the postback.
However, now, when the
OnInit event fires, the controls that are added to the page dynamically enter into the
ViewState, along with their state. Do this any later in the page lifecycle, such as the
OnLoad event, and although you'll still see the controls on the rendered page, their state won't be stored between page views.
Why Pass Parameters in the Querystring?
Earlier, I mentioned that the passing of the info in the
querystring was important. Why? Well, remember that, although controls added to the page during the
OnInit event do go into the
ViewState, their state hasn't yet been loaded from the postback at this point. So, for example, querying the value of the
NumControls textbox in the
OnInit event will simply return an empty
string. Do the same thing in the
OnLoad event and you'll get the value that the user entered into the textbox no probs, but by then it's too late to build the controls dynamically... catch 22.
So, we need to use some other method for passing this information between page calls. I chose to use the
querystring, but you could just as easily, and more transparently, use a session variable, a cookie, write to a database, etc.