You are most likely reading this article because you got one of the following errors
while attempting to bind the values selected from cascading lists to the context
data record of a templated server control:
- Databinding methods such as Eval(), XPath(), and Bind() can only be used in the
context of a databound control
- 'DropDownList1' has a SelectedValue which is invalid because it does not exist in
the list of items.
The problem is that you are attempting to bind to a dependent dropdown list a value
from its container’s data context when the latter was not bound to the container.
The container is the ListView in this demo. Its data context is the address record
that you are attempting to Edit or Insert.
In this example, a change in the country dropdown list selection causes the datasource
for the province’s list to retrieve a list of provinces specific to that country
selection. This does not cause a problem. The problem appears when you have a BIND
statement that attempts to bind the selectedvalue of the province dropdown list
to a property on the address object, e.g. SelectedValue='<%#Bind("provinceId")
%>'. However the postback (triggered by the country's selection change) did not
re-bind the container (the ListView) to its data context (the address object) rather
the container's template was rendered from the saved viewstate. Therefore you get
the error that informs you that you cannot use BIND on a control that is not databound
to its data context.
There are two ways to deal with the problem; you can either try a technical workaround
to remove the 2-way databinding from the cascading dropdownlists and replace it
with customized code during processing the ItemUpdating event as I did in this demo
in ASP.NET 2.0., or
you can change the way you look at the data in a cascading relationship.
Why do you have to save the values of all the members of the cascading relationship
(e.g. country id, province Id, city id) to the address object? We can design the
data such as the City record would have a unique Id that can identify the remaining
elements of the cascade. For example a record for London, Ontario, Canada would
have a CityId different from a record for London, England, UK.
In this demo, I use a SQL database whose structure is represented by the following
Schema
in which the hierarchical country-province-city relation is stored in one table
named Location. The parentId of each location record points to the Id of another
location record (self-join). Therefore the primary key Id of the City is enough
to identify the province and the country.
This strategy avoids running into the problem caused by the BIND statement. In this
demo, I delegate the function of selecting the elements in the cascading relationship
to a web user control. The wucCity.ascx exposes a public property named CityId which
I "Bind" to my address record.
To improve the user experience I add several AJAX features to this ListView in this
UpdatePanel Demo
The sample demo that Microsoft presented in its AJAX Toolkit CascadingDropDown sample used a case where
the color for a particular car make-model does not necessarily have to be a unique
data record, i.e. the green color record is not unique to Acura-Integra, it can
also be a color selection for Audi-A6. Therefore a car's record saves all three
values (car make, model and color). The 3 keys represent a unique identifier to
be able to retrieve the cascading relationship that the user specified. But if we
were to change this relationship such as the green color record for Acura-Integra
has an Id that is different from the green color record for Audi-A6 then we do not
need to save the user selection of the car make and model. We can use the color
record selection to identify both the car make and model.
On this thread you will find an exchange between me and one of Microsoft team
who argued with me over this solution. You might want to verify his argument for yourself.