How do I maintain view state for dynamically added controls on a .Net Wizard Control?

bcardi
bcardi used Ask the Experts™
on
I have a .Net 3.5 web application. On my form I have a Wizard Control. On the fourth step of the wizard I have 6 data entry controls and an HTML table. The table is initially empty. On this step the user can enter 1 to n calendar events. The user fills the data entry controls, clicks the add button, and the data should be moved from the data entry controls to the table. On the server side OnClick event for the Add button I am able to dynamically create the table row and load it with the new calendar entry (I use read-only text boxes to hold the data). The form displays with the proper data. However, if I add another entry, the new entry is loaded OK but the existing entries turn blank. If I go to the previous or next page and then return to step 4, all of my table entries are blank. In both cases, the rows and text boxes still exist, but they're blank.

I implemented code in Page_Load to recreate the controls per examples I found online. If I make the table entries "editable" and then manually type data into the text boxes in the table, the Wizard does not lose this data when I move between steps. I only lose data when I load the value of the textbox programatically in server code.

I found some posts online that suggested dynamic controls should always be created during Page_Load in order to maintain state. So I added code to detect the Add button click in the Page_Load method rather than using the click event, but this did not fix the problem.
Some of the code:
 
protected void Page_Load(object sender, EventArgs e)
{
    if (Page.IsPostBack)
        CreateEventsGrid(SessionHandler.EventsCount);
    if (ButtonID.Value == "AddEvent")
    {
        DoAddEvent();
    }
    ButtonID.Value = "";
}
/*
* The idea for generating the text boxes is from the following web site
* See: http://aspnetresources.com/blog/dynamic_text_bBoxes_in_wizard_control.aspx
*/
protected void CreateEventsGrid(int max_events)
{
    //recreate table
    if (max_events < 1)
        return;
    EventsGrid.Controls.Clear();
    for (int i = 0; i < max_events; i++)
    {
        TableRow row = new TableRow();
        EventsGrid.Controls.Add(row);
 
        TableCell cell = addCell(this.EventDate.ID, row);
        cell = addCell(this.EventLocation.ID, row);
        cell = addCell(this.EventAddress.ID, row);
        cell = addCell(this.EventCity.ID, row);
        cell = addCell(this.EventState.ID, row);
        cell = addCell(this.EventZip.ID, row);
        cell = addCell(this.EventPhone.ID, row);
        cell = addCell(this.EventExtension.ID, row);
    }
}
 
protected void DoAddEvent()
{
    TableRow row = new TableRow();
    EventsGrid.Controls.Add(row);
 
    //Create cells and copy data from entry controls
    TableCell cell = addCell(this.EventDate.ID, row);
    cell.Text = this.EventDate.Text;
 
    cell = addCell(this.EventLocation.ID, row);
    cell.Text = this.EventLocation.Text;
 
    cell = addCell(this.EventAddress.ID, row);
    cell.Text = this.EventAddress.Text;
 
    cell = addCell(this.EventCity.ID, row);
    cell.Text = this.EventCity.Text;
 
    cell = addCell(this.EventState.ID, row);
    cell.Text = this.EventState.Text;
 
    cell = addCell(this.EventZip.ID, row);
    cell.Text = this.EventZip.Text;
 
    cell = addCell(this.EventPhone.ID, row);
    cell.Text = this.EventPhone.Text;
 
    cell = addCell(this.EventExtension.ID, row);
    cell.Text = this.EventExtension.Text;
 
    //Save the row count so we can recreate the table on the next Page_Load
    SessionHandler.EventsCount = EventsGrid.Controls.Count;
}
 
protected TableCell addCell(String controlID, TableRow row)
{
    TableCell cell = new TableCell();
    row.Cells.Add(cell);
 
    TextBox box = new TextBox();
    box.ID = controlID + EventsGrid.Controls.Count;
    box.ReadOnly = true;
    box.BorderStyle = BorderStyle.None;
    box.BorderWidth = 0;
 
    cell.Controls.Add(box);
    return cell;
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Richard LeeSoftware Enthusiast
Commented:
Notoriosly difficult to do what you want within a Wizard. I have two suggestions which may / may not work.
1. Try to use ControlState for the objects you dynamically create.
http://msdn.microsoft.com/en-us/library/1whwt1k7.aspx
2. Why isn't it possible to recreate the objects the second time the way you did the first time and re-populate then. Identify where in the code the data is lossed so you cannot repopulate the controls.
Commented:
Thanks DaTribe. Even though your answer did not directly solve my problem, your statement "Identify where in the code the data is lossed so you cannot repopulate the controls." made me start fresh.

I re-read "Understanding ASP.NET View State" on the Microsoft site (http://msdn.microsoft.com/en-us/library/ms972976.aspx) and discovered a little nugget I had missed previously: "Therefore, the initial property assignments in the instantiation stagewhile written to the ViewState in the properties' set accessorsare not persisted during the SaveViewState() method call in the save view state stage, because the TrackViewState() method has yet to be invoked."

So, the problem was that I was trying to load the Text property during Page_Load, which appears to occur before TrackViewState() has been invoked. So, I moved the code that loads the Text property into Page_PreRender and now everything works as planned. I still create the controls in Page_Load because .Net requires that.
Here is what the code looks like now:
 
protected void Page_Load(object sender, EventArgs e)
{
    if (Page.IsPostBack)
        CreateEventsGrid(SessionHandler.EventsCount);
 
    if (ButtonID.Value == "AddEvent")
    {
        DoAddEvent();
    }
}
 
protected void AddEvent_Click(object sender, EventArgs e)
{
    //do nothing
}
 
/*
* The idea for generating the text boxes is from the following web site
* See: http://aspnetresources.com/blog/dynamic_text_bBoxes_in_wizard_control.aspx
*/
protected void CreateEventsGrid(int max_events)
{
    //recreate table
    if (max_events < 1)
        return;
    EventsGrid.Controls.Clear();
    for (int i = 0; i < max_events; i++)
    {
        TableRow row = new TableRow();
        EventsGrid.Controls.Add(row);
 
        addCell(this.EventDate.ID, row);
        addCell(this.EventLocation.ID, row);
        addCell(this.EventAddress.ID, row);
        addCell(this.EventCity.ID, row);
        addCell(this.EventState.ID, row);
        addCell(this.EventZip.ID, row);
        addCell(this.EventPhone.ID, row);
        addCell(this.EventExtension.ID, row);
    }
}
 
protected void DoAddEvent()
{
    TableRow row = new TableRow();
    EventsGrid.Controls.Add(row);
 
    //Save references to text boxes so we don't have to find them later
    gridEventDate = addCell(this.EventDate.ID, row);
    gridEventLocation = addCell(this.EventLocation.ID, row);
    gridEventAddress = addCell(this.EventAddress.ID, row);
    gridEventCity = addCell(this.EventCity.ID, row);
    gridEventState = addCell(this.EventState.ID, row);
    gridEventZip = addCell(this.EventZip.ID, row);
    gridEventPhone = addCell(this.EventPhone.ID, row);
    gridEventExtension = addCell(this.EventExtension.ID, row);
 
    //Save the row count so we can recreate the table on the next Page_Load
    SessionHandler.EventsCount = EventsGrid.Controls.Count;
}
 
protected TextBox addCell(String controlID, TableRow row)
{
    TableCell cell = new TableCell();
    row.Cells.Add(cell);
 
    TextBox box = new TextBox();
    box.ID = controlID + EventsGrid.Controls.Count;
    box.ReadOnly = true;
    box.BorderStyle = BorderStyle.None;
    box.BorderWidth = 0;
 
    cell.Controls.Add(box);
    return box;
}
 
protected void Page_PreRender(object sender, EventArgs e)
{
    if (ButtonID.Value == "AddEvent")
    {
        gridEventDate.Text = this.EventDate.Text;
        gridEventLocation.Text = this.EventLocation.Text;
        gridEventAddress.Text = this.EventAddress.Text;
        gridEventCity.Text = this.EventCity.Text;
        gridEventState.Text = this.EventState.Text;
        gridEventZip.Text = this.EventZip.Text;
        gridEventPhone.Text = this.EventPhone.Text;
        gridEventExtension.Text = this.EventExtension.Text;
    }
    ButtonID.Value = "";
}

Open in new window

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial