At first glance it appears that adding a control to a form dynamically is quite simple. In fact it is. It’s when you need to be able to persist that control on multiple post-backs where you start running into problems. This article describes how the ASP.NET engine handles VIEWSTATE and POSTBACKS and how that affects dynamically added controls.
First we need to create a page that has a placeholder for the dynamically added controls…
<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=”addControlButton”
runat=”server”
onclick=”addControlButton_Click”
Text=”Button” />
<br />
<br />
<asp:PlaceHolder ID=”controlHolder” runat=”server”></asp:PlaceHolder>
</div>
</form>
</body>
</html>
Next we create a button click event for the button. In it we will add a simple textbox. We have a global counter variable that we use to dynamically assign a textbox id. After creating the textbox we add it to the controlHolder placeholder control.
public partial class _Default : System.Web.UI.Page{ int controlCounter = 0;
protected void addControlButton_Click(object sender, EventArgs e)
{
controlCounter++; TextBox box = new TextBox();
box.Text = “a new text Box”;
box.ID = “textBox” + controlCounter.ToString();
LiteralControl lineBreak = new LiteralControl(“
“);
controlHolder.Controls.Add(box);
controlHolder.Controls.Add(lineBreak);
myControlList.Add(box.ID); }
}
Because our controlHolder placeholder control is loaded into viewstate, when we add our textbox to the placeholder it is also added to the pages viewstate. Because of this one might assume that with each postback the form will now display the dynamically added controls. This is not the case. In the above example, each button click will only result in displaying one textbox and a linebreak. This is because it will always only display the textbox that was created on the most recent button click event.
In order for the ASP.NET engine to process and properly display each control, the control not only needs to exist in the viewstate, but the ASP.NET engine needs to be notified that the control exists, so that it knows to look for it in the viewstate.
When a page is created with static controls (all controls are added at design time), the .aspx page that resides on the server provides the necessary notification for each of the controls on the page.
When we add controls dynamically, we not only need to add them to the forms control collection, but we also need to build the notification mechanism to let the ASP.NET engine know that our newly added controls exist on each subsequent postback.
I’ve modified the code to include this notification mechanism as follows.
1. added a generic list of strings to store the id’s of the dnamically added textboxes (if you need to add multiple different types of controls you may want to use a hashtable, store the control id as the key and the control type as the value)
2. stored the list in the pages ViewState
3. added the overriden LoadViewState function. This function serves the purpose of reading the forms viewstate into memory. Since we want to load our controls from viewstate before any other events occur this is the appropriate function (as it happens before the Page_Load event) I use this function to load and loop through the generic list that I stored in the viewstate. On each loop I load an instance of the control into memory and give it the appropriate id. The ASP.NET engine then maps it to the object found in the viewstate.
using System.Collections.Generic;
public partial class _Default : System.Web.UI.Page
{
int controlCounter = 0; List<string> myControlList;
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
myControlList = (List<string>)ViewState[“myControlList”];
foreach (string ctlID in myControlList)
{
controlCounter++; TextBox box = new TextBox();
box.ID = ctlID; LiteralControl lineBreak = new LiteralControl(“
“);
controlHolder.Controls.Add(box);
controlHolder.Controls.Add(lineBreak);
}
}
protected void Page_Load(object sender, EventArgs e)
{ if (!IsPostBack)
{
myControlList = new List<string>();
ViewState[“myControlList”] = myControlList;
}
}
protected void addControlButton_Click(object sender, EventArgs e)
{
controlCounter++; TextBox box = new TextBox();
box.Text = “a new text Box”;
box.ID = “textBox” + controlCounter.ToString();
LiteralControl lineBreak = new LiteralControl(“
“);
controlHolder.Controls.Add(box);
controlHolder.Controls.Add(lineBreak);
myControlList.Add(box.ID);
ViewState[“myControlList”] = myControlList;
}
}
The form now operates as desired, each button click will dynamically add a textbox that is persisted on each post back.
Categories: Web
Leave a Reply