title: WebForms Nested User Controls Data Binding
body: <p>I'm implementing something using nested user controls and am having issues related to data binding, so I created a small application to attempt to debug the behavior.</p>
  
  <p>I have a default page (aspx) that contains a single user control, 'ChildControl'. In the code behind, I am setting a couple of properties for ChildControl and calling DataBind() on it.</p>
  
  <p>In the ChildControl user control (ascx), I have a ListView of 'GrandchildControl' ListViewItems, which has a datasource bound to a property that was set within the LoadPage() method of Default.aspx. I also am binding a property of GrandchildControl that will be used for a DropDownList within that user control. In the code behind for this user control, I have a click event handler that simply adds a new User object to a session variable and calls the LoadPage() method of Default.aspx.</p>
  
  <p>In the GrandchildControl user control (ascx), I am displaying the information for each bound User object.</p>
  
  <p>I have added logs to see the order in which the events are being called. When I navigate to the page (not postback),  the events fire in the following order as expected:</p>
  
  <pre><code>DEFAULT PAGE LOAD
  GRANDCHILD CONTROL ITEM DATA BOUND
  GRANDCHILD CONTROL ITEM DATA BOUND
  CHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL PAGE LOAD
  </code></pre>
  
  <p>However, when I click the LinkButton within ChildControl to add a new User object, the events fire in this order:</p>
  
  <pre><code>DEFAULT PAGE LOAD
  CHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL PAGE LOAD
  ADD INCOME CLICKED
  GRANDCHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL ITEM DATA BOUND
  GRANDCHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL ITEM DATA BOUND
  GRANDCHILD CONTROL PAGE LOAD
  GRANDCHILD CONTROL ITEM DATA BOUND
  </code></pre>
  
  <p>As you can see, the User objects are bound to the ListView after the call to the their respective page load's firing. Because of this, the dropdown selections are not set correctly (they all default to the 0 index).</p>
  
  <p><strong>So my question is, what causes ucChildControl.DataBind() to behave differently when the Page is initially loaded, versus when it is called from the context of the ChildControl's click handler?</strong></p>
  
  <p>I apologize for the long post but wanted to show as much detail as possible seeing as I have searched and have been unable to find a similar issue and resolution.</p>
  
  <p>All code is below:</p>
  
  <p>Model:</p>
  
  <pre><code>public class DropdownContainer
  {
      public List&lt;DropdownValue&gt; DropdownValues { get; set; }
  }
  
  public class DropdownValue
  {
      public string DisplayText { get; set; }
      public string ID { get; set; }
  }
  
  public class User
  {
      public string FirstName { get; set; }
      public string LastName { get; set; }
      public string AgeGroup { get; set; }
  }
  </code></pre>
  
  <p>aspx front end:</p>
  
  <pre><code>&lt;asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent"&gt;
      &lt;uc1:ChildControl runat="server" ID="ucChildControl"&gt;&lt;/uc1:ChildControl&gt;
  &lt;/asp:Content&gt;
  </code></pre>
  
  <p>aspx code behind:</p>
  
  <pre><code>public partial class Default : Page
  {
      private DropdownContainer _ageGroupContainer = new DropdownContainer()
      {
          DropdownValues = new List&lt;DropdownValue&gt;()
          {
              new DropdownValue()
              {
                  DisplayText = "18-25",
                  ID = "1"
              },
              new DropdownValue()
              {
                  DisplayText = "26-35",
                  ID = "2"
              },
              new DropdownValue()
              {
                  DisplayText = "36-45",
                  ID = "3"
              }
          }
      };
  
      private List&lt;User&gt; _userDataSource = new List&lt;User&gt;()
      {
          new User()
          {
              FirstName = "Mickey",
              LastName = "Mouse",
              AgeGroup = "1"
          },
          new User()
          {
              FirstName = "Daffy",
              LastName = "Duck",
              AgeGroup = "3"
          }
      };
  
      public string Log { get; set; }
  
      protected void Page_Load(object sender, EventArgs e)
      {
          Log += "DEFAULT PAGE LOAD\n";
  
          if (!Page.IsPostBack)
          {
              LoadPage();
          }
      }
  
      public void LoadPage()
      {
          List&lt;User&gt; lstUserTemp = _userDataSource;
  
          if (Session["usersAdded"] != null)
          {
              lstUserTemp.AddRange((List&lt;User&gt;)Session["usersAdded"]);
          }
  
          ucChildControl.UserDataSource = lstUserTemp;
          ucChildControl.AgeGroupDataSource = _ageGroupContainer;
          ucChildControl.DataBind();
      }
  }
  </code></pre>
  
  <p>Child ascx front end:</p>
  
  <pre><code>&lt;div&gt;
      &lt;asp:ListView runat="server" ID="lvChild" DataSource='&lt;%# UserDataSource %&gt;' OnItemDataBound="lvChild_ItemDataBound"&gt;
          &lt;ItemTemplate&gt;
              &lt;uc2:GrandchildControl runat="server" ID="ucGrandchildControl" User='&lt;%# Container.DataItem %&gt;' AgeGroupDataSource='&lt;%# AgeGroupDataSource %&gt;'&gt;&lt;/uc2:GrandchildControl&gt;
              &lt;br /&gt;
          &lt;/ItemTemplate&gt;
      &lt;/asp:ListView&gt;
  &lt;/div&gt;
  &lt;asp:LinkButton runat="server" ID="lbtnAddUser" OnClick="lbtnAddUser_Click" Text="Add User"&gt;&lt;/asp:LinkButton&gt;
  </code></pre>
  
  <p>Child ascx code behind:</p>
  
  <pre><code>public partial class ChildControl : System.Web.UI.UserControl
  {
      public List&lt;User&gt; UserDataSource { get; set; }
  
      public DropdownContainer AgeGroupDataSource { get; set; }
  
      protected void Page_Load(object sender, EventArgs e)
      {
          ((Default)Page).Log += "CHILD CONTROL PAGE LOAD\n";
      }
  
      protected void lvChild_ItemDataBound(object sender, ListViewItemEventArgs e)
      {
          ((Default)Page).Log += "GRANDCHILD CONTROL ITEM DATA BOUND\n";
      }
  
      protected void lbtnAddUser_Click(object sender, EventArgs e)
      {
          ((Default)Page).Log += "ADD USER CLICKED\n";
  
          List&lt;User&gt; usersAdded = (List&lt;User&gt;)Session["usersAdded"];
  
          if (usersAdded == null)
          {
              usersAdded = new List&lt;User&gt;();
          }
  
          usersAdded.Add(new User());
  
          Session["usersAdded"] = usersAdded;
  
          ((Default)Page).LoadPage();
      }
  }
  </code></pre>
  
  <p>Grandchild ascx front end:</p>
  
  <pre><code>&lt;div&gt;
      &lt;asp:Label runat="server" Text="User Name: "&gt;&lt;/asp:Label&gt;
      &lt;asp:TextBox runat="server" ID="txtUserName" Text='&lt;%# string.Format(@"{0} {1}", User.FirstName, User.LastName) %&gt;'&gt;&lt;/asp:TextBox&gt;
  &lt;/div&gt;
  &lt;div&gt;
      &lt;asp:Label runat="server" Text="Age Group: "&gt;&lt;/asp:Label&gt;
      &lt;asp:DropDownList runat="server" ID="ddlAgeGroup" DataSource='&lt;%# AgeGroupDataSource.DropdownValues.OrderBy(x =&gt; Convert.ToInt32(x.ID)) %&gt;' DataTextField="DisplayText" DataValueField="ID" OnSelectedIndexChanged="ddlAgeGroup_SelectedIndexChanged" AutoPostBack="true"&gt;&lt;/asp:DropDownList&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>Grandchild ascx code behind:</p>
  
  <pre><code>public partial class GrandchildControl : System.Web.UI.UserControl
  {
      public User User { get; set; }
  
      public DropdownContainer AgeGroupDataSource { get; set; }
  
      protected void Page_Load(object sender, EventArgs e)
      {
          ((Default)Page).Log += "GRANDCHILD CONTROL PAGE LOAD\n";
  
          if (User != null)
          {
              ddlAgeGroup.SelectedValue = User.AgeGroup;
          }
  
          ddlAgeGroup.Items.Insert(0, new ListItem() { Text = "SELECT AN AGE GROUP", Value = string.Empty });
      }
  
      protected void ddlAgeGroup_SelectedIndexChanged(object sender, EventArgs e)
      {
          ((Default)Page).Log += "GRANDCHILD CONTROL SELECTED INDEX CHANGED\n";
      }
  }
  </code></pre>