Glad to see the title hasn’t scared you off as talking about just one of these topics can get pretty hairy. The reason for this post comes from an issue I was looking into last week. I don’t want to go into all the details but the problem was that a grid that had view state was not repopulating it values during postbacks. I also don’t want to go into too many details about view state and dynamic controls so I suggest reading the following posts by Dave Reed on view state and dynamic controls. The one is pretty lengthy and the other is a four part post so make sure you set aside some time before diving into these. The thing to take away from those is that view state does not store the controls that are on the page or ones that are added to the page.

Back to the issue. To demonstrate I made a created a new page with a few controls so it will be easier to follow. Below is the markup I will use in this post.

<%@ Page Title="Home Page" Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ViewStateAndDynamicControls._Default" %>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Viewstate test
    </title>
</head>
<body>
    <div>
        <form runat="server">
            <asp:Label Text="before panel, set in markup" runat="server" ID="lblBeforePanel" />
            <asp:Panel runat="server" ID="pnlHolder" BorderWidth="1">
                <asp:Label Text="first in panel, set in markup" runat="server" ID="lblFirstInPanel" />
                <asp:GridView runat="server" ID="gvInPanel" AutoGenerateColumns="false">
                    <Columns>
                        <asp:TemplateField>
                            <ItemTemplate>
                                <asp:Label runat="server" ID="lblName" Text='<%# Eval("Name") %>' />
                            </ItemTemplate>
                        </asp:TemplateField>
                    </Columns>
                </asp:GridView>
                <asp:Label runat="server" ID="lblAfterGridInPanel" Text="after grid in panel, set in markup" />
            </asp:Panel>
 
            <asp:GridView runat="server" ID="gvOutside" AutoGenerateColumns="false">
                <Columns>
                    <asp:TemplateField>
                        <ItemTemplate>
                            <asp:Label runat="server" ID="lblName" Text='<%# Eval("Name") %>' />
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </asp:GridView>
            <asp:Button runat="server" ID="btnPostback" Text="post page" OnClick="btnPostback_Click" />
        </form>
    </div>
</body>
</html>
As you can see I have a label, a panel with some labels before and after a grid view, and then I have the pretty much the same grid view outside of the panel. It should be noted that view state is on all of the controls by default. Now here is the code in the code behind.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
 
namespace ViewStateAndDynamicControls
{
    public partial class _Default : Page
    {
 
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                gvInPanel.DataSource = GetPeople();
                gvInPanel.DataBind();
 
                gvOutside.DataSource = GetPeople();
                gvOutside.DataBind();
 
                var dynLabel = new Label();
                dynLabel.Text = "dynamic label in page load<br />";
                pnlHolder.Controls.AddAt(2, dynLabel);
 
                lblFirstInPanel.Text = "first in panel, set in code";
                lblAfterGridInPanel.Text = "after grid in panel, set in code";
            }
        }
 
        private List<Person> GetPeople()
        {
            return new List<Person> { new Person { Name = "foo bar"},
                                        new Person { Name = "john doe"},
                                        new Person { Name = "jane smith"},
                                        new Person { Name = "bill gates"}
            };
        }
 
        protected void btnPostback_Click(object sender, EventArgs e)
        {
        }
 
    }
 
    class Person
    {
        public string Name { get; set; }
    }
 
}

Nothing too crazy here. The thing to point out is the control we are creating and then inserting into the control tree of the panel in the page load. Notice we are inserting it between the first label and the grid and only creating it if the page is not posted back. This will create problems for the grid the follows it inside of the panel after the postback. The following is a picture of the page when it is first loaded and the second picture is what the page looks like after a postback has occurred.

Notice that after the postback the grid inside of the panel does not get repopulated. In fact if you look close you notice the label after the grid does not get set from view state either. But why is this? If you have read the previous links you will know that view state does not store the controls added to the page. So the control we added in the page load is not there when the page is posted back. But the first time we request the page the control is in the control tree and its value is saved into view state. When the page is posted back, load view state is called but the control tree from when view state was saved is now different from when it is loaded. It’s hard to explain and I don’t know the exact technical reason but here is why I think this is. There is a value where the label was in the tree but since it is not there it tries to set the value to the control now in that spot. This happens to be the grid. The grid cannot be set with the value that was in the label so it just ignores it. This then has a chain effect on the rest of the controls inside the current container, which happens to be the panel. The grid is no longer in the same place in the control tree and neither are any of the controls that come after it in the panel. This is why the grid that is outside of the panel is able to retain its values after the postback.

I didn’t show it but if I added the label in the page init then the page would behave as you would expect since view state is loaded after the init. As the linked posts mention, avoid dynamic controls whenever you can but if you have to use them, make sure you use them properly. Hope this was useful and that you never run into this in your coding.