Oct 172004
 

DropDownLists within a Repeater appear to lose or forget which item is selected before the page loads. This is not the case. Dynamically created DropDownLists inside of a Repeater create a rare obstacle. The SelectedItem is not being lost or forgotten. It only appears that way because the OnDataBinding event for the DropDownList is being called twice. Only when the RepeaterItem containing the DropDownList calls its own DataBind method is the data actually bound to the DropDownList. Consider the following code:

// UsingDropDownLists.aspx
115 <asp:Repeater id=”rptOne” runat=”server” OnItemCreated=”rptOne_ItemCreated”/>
116  <ItemTemplate>
117    <asp:Literal id=”litQuestion” runat=”server”/>
118    <asp:DropDownList id=”ddlResponse” runat=”server”/>
119    <br/>
120  </ItemTemplate>
121 </asp:Repeater>

// UsingDropDownLists.aspx.cs
211 protected void rptOne_ItemCreated(object sender, RepeaterItemEventArgs e)
212 {
213   if(e.Item.DataItem != null)
214   {
215     MyClass myClass = sender as MyClass;
216     (e.Item.FindControl(“litQuestion”) as Literal).Text = myClass.Question;
217     DropDownList ddl = e.Item.FindControl(“ddlResponse”) as DropDownList;
218     ddl.DataSource = GetResponseList(myClass.AvailableResponses);
219     ddl.Items.FindByValue(myClass.Response).Selected = true;
220     ddl.DataBind();
221     ddl.SelectedIndex = e.Item.ItemIndex%(ddl.Items.Count-1); //Random
222   }
223 }

Here is the sequence of events:

  1. Page_Load
  2. Data Retrieval/Bind Data to Repeater
  3. Repeater’s ItemCreated event fires for each RepeaterItem
  4. Data Retrieval/Bind Data to DropDownList
  5. OnDataBinding is fired from the DropDownList’s DataBind() (line 220). However, if you watch in debug mode, you will see that the DropDownList has no ListItems yet.
  6. DropDownList’s SelectedIndex is chosen (line 221). Although, at this point, the DropDownList still has no ListItems. The SelectedIndex call falls on deaf ears.
  7. RepeaterItem’s DataBind method performs an actual DataBind on the DropDownList. There are no selected items at this point.

RepeaterItem inherits from Control. If you look at Control’s DataBind() method in Lutz Roeder’s .NET Reflector, you will see that it performs a DataBind on each of its child controls. 


public virtual void DataBind()
{
   this.OnDataBinding(EventArgs.Empty);
   if (this._controls != null)
   {
      string text1 = this._controls.SetCollectionReadOnly(“Parent_collections_readonly”);
      int num1 = this._controls.Count;
      for (int num2 = 0; num2 < num1; num2++)
      {
         this._controls[num2].DataBind();
      }

      this._controls.SetCollectionReadOnly(text1);
   }
}


From this, we can see three things.



  1. The only safe time to choose the SelectedItem is after the binding of all objects within the Repeater. A perfect place to do this is in the RepeaterItem’s PreRender or in the Repeater’s PreRender. You have access to the same bound object from RepeaterItem.DataItem, and can find the DropDownList and choose the SelectedIndex, SelectedValue, or ddl.Items.FindByValue(response).Selected = true;
  2. The DataBind() call (line 220) is completely unnecessary and leads the programmer to believe that it has actually bound the data. This is untrue. Again, in debug mode, you will see that by line 221, there are still zero ListItems in ddl.Items.
  3. The same obstacle will arise for all Controls derived from ListControl. This includes CheckBoxList, DropDownList, ListBox, and RadioButtonList. The same solution will apply to these controls within a Repeater.