I recently posted a short article on avoiding the use of dynamic controls in ASP.NET; for part 1, please click here.

Part 1 demonstrated the basics of using a listview control to dynamically generate a table containing a number of textboxes, allowing the user to select how many textboxes are required at runtime. This is the type of problem that is best solved without the use of dynamic controls, and part 1 explained why this is so.

This article will extend the code from part 1, to show how to preserve the values of the textboxes between postbacks.

When you run the code from the original article, you will notice that any existing values entered into the textboxes are lost each time a new textbox is added. This would make for a pretty user-unfriendly UI experience in a real-world application. Luckily, the solution is straightforward.

The first change I have made is to alter the databinding methodology slightly, so that the listview is now bound to a collection of strings, rather than a collection of integers. The integer collection served no purpose in the original article, other than to provide an IEnumerable implementation to databind to. Now the datasource will actually be a data source: it will be used to track the textbox text values.

First off, a reminder of the original page markup, which will remain unchanged:

<asp:ListView ID="lvDynamicTextboxes" runat="server" 
    ItemPlaceholderID="itemPlaceholder" 
    onitemdatabound="lvDynamicTextboxes_ItemDataBound">
    <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>
<br />
<asp:Button ID="btnAddTextBox" runat="server" 
    Text="Add" onclick="btnAddTextBox_Click" />

The initial alteration I made was to add two methods for saving and retrieving the string collection which will be the new data source for the listview control:

private List<string> GetDataSource()
{
    List<string> dataSource = null;
    if (ViewState["DataSource"] != null)
    {
        dataSource = (List<string>)ViewState["DataSource"];
    }
    else
    {
        dataSource = new List<string>();
        dataSource.Add(string.Empty);
        ViewState["DataSource"] = dataSource;
    }
    return dataSource;
}
 
private void SetDataSource(List<string> dataSource)
{
    ViewState["DataSource"] = dataSource;
}

I chose to use ViewState for persisting the collection, given that we are unlikely to be dealing with a large number of textboxes, so the collection size will remain fairly small.

Next up, I re-jigged the method which originally incremented the textbox count in ViewState, so that it increases the data source collection size by one instead:

private void IncrementTextboxCount()
{
    List<string> dataSource = this.GetDataSource();
    dataSource.Add(string.Empty);
    this.SetDataSource(dataSource);
}

The next step was to create a method which persists the existing textbox values. This is going to be called before rebinding the listview, and is the key method which will ensure the values are preserved:

private void UpdateDataSource()
{
    List<string> dataSource = new List<string>();
    foreach (ListViewItem item in this.lvDynamicTextboxes.Items)
    {
        if (item is ListViewDataItem)
        {
            TextBox txt = (TextBox)item.FindControl("txtText");
            dataSource.Add(txt.Text);
        }
    }
    this.SetDataSource(dataSource);
}

The method above iterates through the ListView rows, identifying the data rows which contain the textboxes. It then gets a reference to the textbox using FindControl, and stores the current text in the collection.

Finally, an event handler is added for the listview's ItemDataBound event, which allows the textbox text to be set for each row in the data source, as it is being databound:

protected void lvDynamicTextboxes_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    if (e.Item is ListViewDataItem)
    {
        TextBox txt = (TextBox)e.Item.FindControl("txtText");
        txt.Text = ((ListViewDataItem)e.Item).DataItem.ToString();
    }
}

The DataItem property of the ListViewDataItem contains a reference to the item in the datasource collection which is currently being databound. As we are binding to a collection of strings, I simply call ToString() on the dataitem, as we know this will be of string type,

So, finally putting it all together we get:

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        this.BindListView();
    }
}
 
private void BindListView()
{
    //create an enumerable range based on the current count
    List<string> dataSource = this.GetDataSource();
 
    //bind the listview
    this.lvDynamicTextboxes.DataSource = dataSource;
    this.lvDynamicTextboxes.DataBind();
}
 
private void UpdateDataSource()
{
    List<string> dataSource = new List<string>();
    foreach (ListViewItem item in this.lvDynamicTextboxes.Items)
    {
        if (item is ListViewDataItem)
        {
            TextBox txt = (TextBox)item.FindControl("txtText");
            dataSource.Add(txt.Text);
        }
    }
    this.SetDataSource(dataSource);
}
 
private void IncrementTextboxCount()
{
    List<string> dataSource = this.GetDataSource();
    dataSource.Add(string.Empty);
    this.SetDataSource(dataSource);
}
 
private List<string> GetDataSource()
{
    List<string> dataSource = null;
    if (ViewState["DataSource"] != null)
    {
        dataSource = (List<string>)ViewState["DataSource"];
    }
    else
    {
        dataSource = new List<string>();
        dataSource.Add(string.Empty);
        ViewState["DataSource"] = dataSource;
    }
    return dataSource;
}
 
private void SetDataSource(List<string> dataSource)
{
    ViewState["DataSource"] = dataSource;
}
 
protected void btnAddTextBox_Click(object sender, EventArgs e)
{
    this.UpdateDataSource();
    this.IncrementTextboxCount();
    this.BindListView();
}
 
protected void lvDynamicTextboxes_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    if (e.Item is ListViewDataItem)
    {
        TextBox txt = (TextBox)e.Item.FindControl("txtText");
        txt.Text = ((ListViewDataItem)e.Item).DataItem.ToString();
    }
}

And the page now not only allows the user to dynamically increase the number of textboxes displayed, but also preserves all existing values each time a new textbox is added.

You can download the full source code using the link at the top of the article.