This is part 1 of what I intend to be something of a recurring theme: how to avoid the use of dynamic controls in ASP.NET.
Whilst dynamic controls have their uses in a small number of cases, they come with a significant amount of extra baggage. For example:
* Dynamic controls need to be created early in the page lifecycle to participate in viewstate
* Dynamic controls need to be re-created on each postback, and assigned the same control IDs to maintain viewstate
* Extracting values from dynamic controls on postback is not straightforward.
There are of course workarounds to all of the above obstacles- but the fact remains, if a built-in ASP.NET control does, or can be coerced into doing, what you need, then the simpler approach should prevail.
I’m often surprised by the number of times I see some code like this:
private
void CreateTextBoxes(int count) {
TextBox tb = null;
for (int i = 0; i < count; i++) {
tb = new TextBox();
tb.ID = "txt" + i.ToString();
this.Panel1.Controls.Add(tb);
}
}
To dynamically create textboxes. When asked why they are using dynamic controls, the coder will often reply ‘because I don’t know how many textboxes I need until runtime’. This is, of course, a valid concern, but in this case, dynamic controls are usually not a good solution when a Repeater, DataList, or ListView control can be employed to the same effect, with much less effort.
For example, let’s take the case of a page which initially loads with a single textbox, but contains a button which allows the user to add additional textboxes at runtime. To show how much easier the solution is using built-in .NET controls, I started by adding a ListView control to the page, and edited its ItemTemplate and LayoutTemplate like so:
<asp:ListView ID="lvDynamicTextboxes" runat="server"
ItemPlaceholderID="itemPlaceholder"> <LayoutTemplate> <table> <asp:PlaceHolder ID="itemPlaceholder"
runat="server"></asp:PlaceHolder> </table> </LayoutTemplate> <ItemTemplate> <tr> <asp:TextBox ID="txtText" runat="server"> </asp:TextBox> </tr> </ItemTemplate>
</asp:ListView>
Next up, I added a button to the page, which will be used to add new textboxes:
<asp:Button ID="btnAddTextBox" runat="server"
Text="Add" onclick="btnAddTextBox_Click" />
I then created two methods- one for binding the ListView, and one for incrementing the current textbox count, which I chose to store using ViewState:
private void BindListView()
{
//get the current textbox count int count = 1;
if (ViewState["textboxCount"] != null)
count = (int)ViewState["textboxCount"];
//create an enumerable range based on the current count IEnumerable<int> enumerable = Enumerable.Range(1, count);
//bind the listview this.lvDynamicTextboxes.DataSource = enumerable;
this.lvDynamicTextboxes.DataBind();
}
private void IncrementTextboxCount()
{
int count = 1;
if (ViewState["textboxCount"] != null)
count = (int)ViewState["textboxCount"];
count++;
ViewState["textboxCount"] = count;
}
You will notice in the above that I’m using the Enumerable.Range() method which is located within the System.Linq namespace in the .NET framework 3.0 and greater. This is used to automatically create an integer collection with a specified range: a quick and easy way of generating a datasource for these purposes.
The only thing left to do now, is bind the ListView in the page load, and increment the TextBox count and rebind when the button is clicked:
protected
void Page_Load(object sender, EventArgs e) {
if (!Page.IsPostBack) {
this.BindListView();
}
}
protected
void btnAddTextBox_Click(object sender, EventArgs e) {
this.IncrementTextboxCount();
this.BindListView();
}
“But… hang on a minute”, you might well say, “that’s actually more code than I needed before, using dynamic controls”.
Well, yes, it is. But the real gains come when you need to extract the values from the textboxes you have dynamically created. Using dynamic controls, this gets very ugly very quickly. Using a ListView, you need only to iterate through the ListView’s data items, and extract the textbox values from the data rows, like so:
private IList<string> GetValues()
{
List<string> values = new List<string>();
TextBox txt = null;
foreach (ListViewItem item in this.lvDynamicTextboxes.Items)
{
if (item is ListViewDataItem)
{
txt = (TextBox)item.FindControl("txtText");
values.Add(txt.Text);
}
}
return values;
}
And that’s all there is to it.
For preserving the values of the textboxes between postbacks, see part 2.