Implementing cascading DropDownList binding in a templated control

pseudocoder picture pseudocoder · Jul 19, 2011 · Viewed 8.9k times · Source

I have 2 DropDownList controls on my form, the second of which uses the SelectedValue of the first as one of its binding parameters.

Both DropDownList controls are in a FormView.InsertItemTemplate with SelectedValue properties bound to the FormView's datasource using a Binding Expression.

The first time the FormView renders in Insert mode, everything works fine. The problem is after an AutoPostBack from the first DropDownList, the FormView doesn't (re-)bind, however since the ControlParameter on the second DropDownList has changed, it DOES bind (as intended), but an exception occurs on the Binding Expression of the second DDL, I assume since the FormView is not binding on that pass:

System.InvalidOperationException: Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.

Here is the markup:

<InsertItemTemplate>
.
.
.
<tr class="GridViewRowB">
                    <td class="GridViewCell">
                        Offense Type
                    </td>
                    <td class="GridViewCell">
                        <asp:DropDownList ID="ddlOffenseType" runat="server" DataSourceID="dsOffenseType"
                            AutoPostBack="true" DataValueField="OffenseTypeID" DataTextField="Description"
                            SelectedValue='<%# Bind("OffenseTypeID") %>'>
                        </asp:DropDownList>
                        <asp:ObjectDataSource ID="dsOffenseType" runat="server" TypeName="OffenseType"
                            SelectMethod="GetAll">
                            <SelectParameters>
                                <asp:Parameter Name="ActiveOnly" DefaultValue="True" Type="Boolean" />
                            </SelectParameters>
                        </asp:ObjectDataSource>
                    </td>
                </tr>
                <tr class="GridViewRowA">
                    <td class="GridViewCell">
                        Attorney
                    </td>
                    <td class="GridViewCell">
                        <asp:DropDownList ID="ddlAttorney" runat="server" DataSourceID="dsAttorney" DataValueField="AttorneyID"
                            DataTextField="AttorneyNameWithCount" SelectedValue='<%# Bind("AttorneyID") %>'>
                        </asp:DropDownList>
                        <asp:ObjectDataSource ID="dsAttorney" runat="server" TypeName="Attorney"
                            SelectMethod="GetAttorneyWithCaseCount">
                            <SelectParameters>
                                <asp:Parameter Name="ActiveOnly" DefaultValue="True" Type="Boolean" />
                                <asp:ControlParameter Name="OffenseTypeID" Type="Int32" ControlID="ddlOffenseType"
                                    PropertyName="SelectedValue" />
                            </SelectParameters>
                        </asp:ObjectDataSource>
                    </td>
                </tr>
.
.
.
</InsertItemTemplate>

My question is: What is the best way to make this functionality work? Is it possible to keep both DDL's inside the template? I would prefer to avoid using the AJAX toolkit or other client-side solutions.

Answer

Muhammad Akhtar picture Muhammad Akhtar · Jul 19, 2011

This is an issue when we use cascading dropdownlist in Databinding Controls like DetailsView/FormView and I have faced it many times. You have to remove the Binding Expression from your Second Dropdownlist SelectedValue='<%# Bind("AttorneyID") %>', then it will work.

Secondly if you remove the Binding expression, you have to pass the value manually in FormView ItemInserting Event. e.g.

 protected void frmAsset_ItemInserting(object sender, FormViewInsertEventArgs e)
 {
    eValues["AttorneyID"] = ((DropDownList)((FormView)sender).FindControl("ddlAttorny")).SelectedValue;
 }