Cascading Picklist inside a ListView – A few more findings on ASP.NET DataBinding
Nicolas Galler | May 28, 2008Cascading picklists (or dependent dropdowns, however you want to call them) are a pretty common occurrence and there is a common pattern in ASP.NET to address it, it goes like this:
<asp:DropDownList runat="server" ID="cboType" AppendDataBoundItems="true" AutoPostBack="true" DataSourceID="dsTypes"> <asp:ListItem Text="" Value="" /> </asp:DropDownList> <asp:DropDownList runat="server" ID="cboSubType" AppendDataBoundItems="true" AutoPostBack="true" DataSourceID="dsSubTypes"> <asp:ListItem Text="" Value="" /> </asp:DropDownList> <asp:ObjectDataSource runat="server" ID="dsSubTypes" TypeName="CustomerDao" SelectMethod="GetSubTypes"> <SelectParameters> <asp:ControlParameter ControlID="cboType" Name="type" Type="String" PropertyName="SelectedValue" /> </SelectParameters> </asp:ObjectDataSource> <asp:ObjectDataSource runat="server" ID="dsTypes" TypeName="CustomerDao" SelectMethod="GetTypes"/>
What happens if you want to have the dropdown displayed in an editable list of data though, like so:
You wish you could write something like this:
<asp:DropDownList OnDataBound="cboSubType_DataBound" runat="server" ID="cboSubType" SelectedValue='<%# Bind("SubTypes") %>' AppendDataBoundItems="false" DataSourceID="dsSubTypes"> <asp:ListItem Text="" Value="" /> </asp:DropDownList>
But in reality this will give 2 kinds of error:
- ‘cboSubType’ has a SelectedValue which is invalid because it does not exist in the list of items
- Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control
An additional problem is that if you disable the ViewState for the page (as I tend to routinely do) the "SelectedValue" property of the DropDownList is not correctly updated anymore.
In order to tackle these problems I took inspiration in the code listed at http://webswapp.com/codesamples/viewsource.aspx?file=~/codesamples/aspnet20/dependentlists/datalist.aspx (there are a few other neat tricks on the page so I recommend the read!).
The solution I came up with boils down to 2 elements:
- In the DataBound event of the DropDownList, retrieve the value (either from the postback data, or from the current data) and select it.
DropDownList cboSubType = (DropDownList)sender; ListViewDataItem parentItem = (ListViewDataItem)cboSubType.NamingContainer; cboSubType.ClearSelection(); String prevValue = null; if (IsPostBack) { prevValue = Request.Form[cboSubType.UniqueID]; } else { if (parentItem.DataItem != null) { prevValue = ((Customer)parentItem.DataItem).SubType; } } ListItem li = cboSubType.Items.FindByValue(prevValue); if (li != null) li.Selected = true;
- In the ItemUpdating event of the listview I explicitely retrieve the value (from the postback because the SelectedValue may not be valid at that point) and set it in the updated values:
DropDownList cboSubType = (DropDownList)lstCustomers.Items[e.ItemIndex].FindControl("cboSubType"); e.NewValues["SubType"] = Request.Form[cboSubType.UniqueID];
This is not quite as bad as the solution I started with (which involved checking something like Request.Form[lstContacts.UniqueId + "$ctrl00$cboSubType"]) but I still think I must be missing something obvious. Oh well, maybe the light will come on at some point, until then I got my stuff working.
The code is here in case it is of use to anyone (or in case I need a refresher in 2 months, hah!).





