AjaxControlToolkit (ASP.NET/C#) : CascadingDropDown Extender - Part 1

October 7, 2011 by C#   ASP.NET  

When working with relational data on a frontend one of the common practices (among TreeViews) are to make use of DropDowns that update in relation to one another - cascading.

In the following image we've got three DropDownLists that demonstrates this cascading effect.

Cascading DropDowns

When an user selects a country, it updates the list of provinces (or states) available for that country, which in turn updates the list of available cities for the selected province.

One way to achieve this (many ways to skin a cat) is to attach handlers to the SelectedIndexChanged events of our DropDownLists and manually handling the cascade relation like seen in the following snippet.

protected void ddlCountry_SelectedIndexChanged(object sender, EventArgs e)
{
    ddlCountry.Cascade(ddlProvince, (CountryId) => Province.Get(CountryId));
    ddlProvince.Cascade(ddlCity, (ProvinceId) => City.Get(ProvinceId));
}

protected void ddlProvince_SelectedIndexChanged(object sender, EventArgs e)
{
    ddlProvince.Cascade(ddlCity, (ProvinceId) => City.Get(ProvinceId));
}

Basically when an user selects a country, we clear all provinces and cities and rebind the province DropDownList with provinces relating to the selected country. If the user selects a province we clear the cities DropDownList and rebinds it with cities relating to the selected province.

Now the Cascade method seen in the snippet above is an extension method to which we pass our DataSource via a lambda expression, the method can be seen in the snippet below.

public static class ExtensionMethods
{
    public static void Cascade<T>(this DropDownList parentDropDownList, DropDownList childDropDownList, Func<Int32, T> dataSource)
    {
        childDropDownList.Items.Clear();
        childDropDownList.AppendDataBoundItems = true;
        childDropDownList.Items.Add(new ListItem("- Please Select -", ""));
        if (!String.IsNullOrEmpty(parentDropDownList.SelectedValue))
        {
            childDropDownList.DataSource = dataSource(Convert.ToInt32(parentDropDownList.SelectedValue));
            childDropDownList.DataBind();
        }
    }
}

If we need to get rid of the postbacks caused by the cascade we can put our DropDownLists in an UpdatePanel like seen in the download accompanying this post.

Another option (which brings is to the title of this post) is to make use of the CascadingDropDown extender which is part of the AjaxControlToolkit

Observe the following markup:

<asp:DropDownList runat="server" ID="ddlCountry">
</asp:DropDownList>
<asp:DropDownList runat="server" ID="ddlProvince">
</asp:DropDownList>
<asp:DropDownList runat="server" ID="ddlCity">
</asp:DropDownList>

<asp:CascadingDropDown ID="cddlCountries" runat="server" TargetControlID="ddlCountry"
    Category="Country" PromptText="- Please Select -" ServicePath="~/Services/Service.asmx"
    ServiceMethod="GetCountries">
</asp:CascadingDropDown>

<asp:CascadingDropDown ID="cddlProvinces" runat="server" TargetControlID="ddlProvince"
    ParentControlID="ddlCountry" Category="Province" PromptText="- Please Select -"
    ServicePath="~/Services/Service.asmx" ServiceMethod="GetProvinces">
</asp:CascadingDropDown>

<asp:CascadingDropDown ID="cddlCities" runat="server" TargetControlID="ddlCity" ParentControlID="ddlProvince"
    Category="City" PromptText="- Please Select -" ServicePath="~/Services/Service.asmx"
    ServiceMethod="GetCities">
</asp:CascadingDropDown>

Okay, so what we've got here is our three DropDownLists (country, province, city) and three CascadingDropDown extenders, here is quick list of the attributes you're seeing (there are others) on the extenders.

TargetControlID ID of the DropDownList being extended / populated.
ParentControlID ID of the DropDownList thats above this control in the hierarchy, the extender needs the SelectedValue of the parent DropDownList in order to be able to databind the target control.
Category Category represented by the control, this property is used as key(s) to distinguish the values of each DropDownList SelectedValue in the knownCategoryValues parameter passed to the webmethod - parseable by the CascadingDropDown.ParseKnownCategoryValuesString method.
PromptText The text of the default item in the DropDownList (almost feels like something that should have been part of the DropDownlist WebControl, hint hint). PromptValue sets the value of the default item.
ServicePath Path to the webservice, uhm... yes you're going to need to write a webservice which will be used by the extender to databind DropDownLists client side.
ServiceMethod Method in the webservice thats going to be called in order to populate the DropDownList.

In the following code example you can get an idea of what the required web service will look like.

[WebService(Namespace = "http://cstruter.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class Service : System.Web.Services.WebService
{
    [WebMethod]
    public CascadingDropDownNameValue[] GetCountries(
      string knownCategoryValues,
      string category)
    {
        return Country.Get().Select(p => 
            new CascadingDropDownNameValue(p.Title, p.CountryId.ToString())
        ).ToArray();
    }

    [WebMethod]
    public CascadingDropDownNameValue[] GetProvinces(
      string knownCategoryValues,
      string category)
    {
        StringDictionary values = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);            
        Int32 CountryId = Convert.ToInt32(values["Country"]);
        return Province.Get(CountryId).Select(p =>
            new CascadingDropDownNameValue(p.Title, p.ProvinceId.ToString())
        ).ToArray();
    }

    [WebMethod]
    public CascadingDropDownNameValue[] GetCities(
      string knownCategoryValues,
      string category)
    {
        StringDictionary values = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
        Int32 ProvinceId = Convert.ToInt32(values["Province"]);

        return City.Get(ProvinceId).Select(p =>
            new CascadingDropDownNameValue(p.Title, p.CityId.ToString())
        ).ToArray();
    }
}

Like seen in the example above, each method needs to return an array of CascadingDropDownNameValue - which is essentially our ListItems.

You might have noticed the methods Country.Get/Province.Get and City.Get, these are all part of the dummy data class provided within the accompanying download to this post.

In the next part of the post we're going to have a look at how to databind the CascadingDropDown extender and I am going to share a small concern I've got regarding this extender and some possible solutions.


Related Downloads

ASP.net webforms CascadeDropDown Demo


Leave a Comment


sPjiyYuiEIKE December 21, 2011 by Cordy

Could you write about Phsycis so I can pass Science class?


    Related Posts

    AjaxControlToolkit (ASP.NET/C#) : CascadingDropDown Extender - Part 2

    November 3, 2011


    Latest Posts

    Dependency Injection using Ninject and PostSharp

    April 26, 2016

    Aspect Orientated Programming (AOP) - A Brief look at PostSharp

    April 15, 2016

    How to become a better programmer.

    April 5, 2016

    JavaScript Fundamentals - Hoisting

    March 22, 2016

    Quick and dirty look at ECMAScript 6

    March 17, 2016

    JavaScript Promises

    March 9, 2016

    CSS Combinators

    March 7, 2016

    Shingles Awareness

    March 4, 2016

    Google Analytics : Measurement Protocol

    February 24, 2016

    Interesting JavaScript - SQLite (sql.js)

    February 18, 2016


    Most Commented on Posts

    Moving items between listboxes in ASP.net/PHP example

    Move items between two listboxes in ASP.net(C#, VB.NET) and PHP
    June 12, 2008

    Simple WYSIWYG Editor

    Creating a WYSIWYG textbox for your website is actually quite simple.
    February 1, 2007

    C# YouTube : Google API

    Post on how to integrate with YouTube using the Google Data API
    March 12, 2011

    Populate a TreeView Control C#

    Populate a TreeView control in a windows application.
    August 27, 2009

    ASP.NET(C#) : Autocomplete TextBox - Part 1 (From Scratch)

    Part one of how to create an autocomplete textbox using ASP.NET(C#)
    March 27, 2011


    Angular   Architecture   ASP.NET   Astronomy   Breaking News   C#   C++   Coding Horrors   Comedy   Cross Browser   CSS   Design Patterns   ES6   Google API   IIS   Integration   JavaScript   Jquery Mobile   Love and War   Microsoft Office   Ninject   Personal   PHP   PostSharp   SEO   South Africa   SQL   Threading   TypeScript   Visual Basic   XML