Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Cascading Menus Without Page Refresh With the AJAX Control Toolkit

4.85/5 (48 votes)
12 Jul 2011Ms-PL4 min read 99.6K   2K  
This is a very easy and user friendly technique for web applications to implement cascading menus without page refresh.

Sample Image

Introduction

This is a very easy and user friendly technique for web applications to implement cascading menus without page refresh.

You do not have to know about AJAX functions, just download the AJAX Control Toolkit on CodePlex and follow the steps described in this article. When you have two dropdownlists and both of them are related to each other, such as Country and City in a registration page where you want to save information about the user's hometown. When you choose a certain row from the first dropdownlist (Country), you will expect the second dropdownlist (City) to be filtered according to the chosen row without refreshing the page. This article shows a very easy and user friendly solution that can be used in web applications.

Background: What is the AJAX Control Toolkit?

The ASP.NET AJAX Control Toolkit is an Open-Source project built on top of the Microsoft ASP.NET AJAX framework, and contains more than 30 controls that enable you to easily create rich, interactive web pages. If you want to know more about it, visit here.

Using the code

The first step is to download the AJAX Control Toolkit from here for .NET 3.5 or here for .NET 4.0.

Copy the AJAX Control Toolkit to the Bin folder and right click on Solution, choose Add Reference, in the Browse tab, double click on the Bin folder, and double click on AJAX Control Toolkit, then on the Build menu, click Rebuild.

Database

Create a database and name it "Db". Here is the query to create the required tables:

SQL
--Create Country Table 
CREATE TABLE [dbo].[tblCountry](
    [Country] [nvarchar](50) NULL,
    [IDC] [int] IDENTITY(1,1) NOT NULL
    ) ON [PRIMARY]

--Fill Country Table 
insert into dbo.tblCountry(Country) values('United States')
insert into dbo.tblCountry(Country) values('United Kingdom')
insert into dbo.tblCountry(Country) values('Spain')
insert into dbo.tblCountry(Country) values('France')
insert into dbo.tblCountry(Country) values('Norway')

--Create City Table 
CREATE TABLE [dbo].[tblCity](
[City] [nvarchar](50) NULL,
[CountryID] [int] Not NULL,
[ID] [int] IDENTITY(1,1) NOT NULL
)
ON [PRIMARY]

--Fill City Table 
insert into dbo.tblCity(City,CountryID) values('Michigan',1)
insert into dbo.tblCity(City,CountryID) values('New York',1)
insert into dbo.tblCity(City,CountryID) values('London',2)
insert into dbo.tblCity(City,CountryID) values('Barcelona',3)
insert into dbo.tblCity(City,CountryID) values('Madrid',3)
insert into dbo.tblCity(City,CountryID) values('Paris',4)
insert into dbo.tblCity(City,CountryID) values('Kristiansand',5) 
insert into dbo.tblCity(City,CountryID) values('Oslo',5)

Visual Studio 2008 - .NET 3.5

Create a website and name it Cascading menus. Create a Web Form and name it CascadingDropDown.aspx. In the HTML view, write the code below. The code is a little different between C# and VB in this section. If you are a VB coder, modify two sections in the page tag in the first line:

  • language="VB"
  • CodeFile="CascadingDropDown.aspx.vb"
ASP.NET
<%@ Page Language="C#" AutoEventWireup="false" 
         CodeFile="CascadingDropDown.aspx.cs" Inherits="CascadingDropDown" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" 
         TagPrefix="ajaxToolkit" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>CascadingDropDown</title>
</head>
<body>
<form id="form1" runat="server">
<ajaxToolkit:ToolkitScriptManager  ID="ScriptManager1" runat="server" > 
</ajaxToolkit:ToolkitScriptManager>
<div>
<asp:DropDownList ID="ddListCountry" runat="server" Width="170" /> 

<asp:DropDownList ID="ddListCity" runat="server" Width="170" /> 

<ajaxToolkit:CascadingDropDown 
  ID="CascadingCountry" 
  runat="server"
  TargetControlID="ddListCountry"
  Category="Country"
  PromptText="Please select a Country"
  LoadingText="[Loading Country...]"
  ServicePath="Cascading.asmx"
  ServiceMethod="GetCountries" >
  </ajaxToolkit:CascadingDropDown>

<ajaxToolkit:CascadingDropDown 
  ID="CascadingCity" 
  runat="server"
  TargetControlID="ddListCity"
  Category="City"
  PromptText="Please select a City"
  LoadingText="[Loading City...]"
  ServicePath="Cascading.asmx"
  ServiceMethod="GetCities"
    ParentControlID="ddListCountry" >
  </ajaxToolkit:CascadingDropDown>
</div>

</form>
</body>
</html>

For VB: Create Web Service: Solution > right click > Add New Item > Web Service >. Name: Cascading.asmx. Language: Visual Basic. Go To > App_Code > Cascading.vb.

VB
' (c) Copyright Microsoft Corporation.
' This source is subject to the Microsoft Public License.
' See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
' All other rights reserved.
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Collections.Generic
Imports AjaxControlToolkit
Imports System.Data
Imports System.Data.SqlClient
Imports System.Text.RegularExpressions
Imports System.Collections.Specialized
Imports System.Xml

' To allow this Web Service to be called from script,
' using ASP.NET AJAX, uncomment the following line.
<System.Web.Script.Services.ScriptService()> _
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class Cascading
    Inherits System.Web.Services.WebService
    Dim cn As New SqlClient.SqlConnection()
    Dim ds As New DataSet
    Dim dt As New DataTable

<WebMethod()> _
Public Function GetCountries(ByVal knownCategoryValues As String, _
       ByVal category As String) As CascadingDropDownNameValue()

    'ADO.Net
    Dim strCn As String = "data source=.;Initial Catalog=Db;Integrated Security=True"

    cn.ConnectionString = strCn
    Dim cmd As New SqlClient.SqlCommand
    cmd.Connection = cn
    cmd.CommandType = CommandType.Text
    cmd.CommandText = "select * from tblCountry"

    Try
        cn.Open()
        cmd.ExecuteNonQuery()
        Dim da As New SqlDataAdapter(cmd)
        da.Fill(ds)
    Catch ex As Exception
    Finally
        cn.Close()
    End Try

    dt = ds.Tables(0)
    Dim CountryValues As New List(Of CascadingDropDownNameValue)()

    For Each row As DataRow In dt.Rows
       CountryValues.Add(New CascadingDropDownNameValue(row("Country").ToString(), _
                         row("IDC").ToString()))
    Next

    Return CountryValues.ToArray()

End Function

<WebMethod()> _
Public Function GetCities(ByVal knownCategoryValues As String, _
                ByVal category As String) As CascadingDropDownNameValue()

    Dim kv As StringDictionary = _
        CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues)
    'ContainsKey("Country") is one of property in Ajaxcontroltoolkit
    Dim countryId As Integer

    If ((Not kv.ContainsKey("Country")) Or _
        (Not Int32.TryParse(kv("Country"), countryId))) Then
        Return Nothing
    End If

    'ADO.Net
    Dim strCn As String = "data source=.;Initial Catalog=Db;Integrated Security=True"
    cn.ConnectionString = strCn
    Dim cmd As New SqlClient.SqlCommand
    cmd.Connection = cn
    '-----I Defined a parameter instead of passing value 
    '               directly to prevent sql injection--------'
    cmd.CommandText = "select * from tblCity where CountryID=@myParameter Order by City"
    cmd.Parameters.AddWithValue("@myParameter", countryId.ToString())

    Try
        cn.Open()
        cmd.ExecuteNonQuery()
        Dim da As New SqlDataAdapter(cmd)
        da.Fill(ds)
    Catch ex As Exception
    Finally
        cn.Close()
    End Try

    dt = ds.Tables(0)
    Dim CityValues As New List(Of CascadingDropDownNameValue)()

    For Each row As DataRow In dt.Rows
       CityValues.Add(New CascadingDropDownNameValue(row("City").ToString(), _
                      row("ID").ToString()))
    Next

    Return CityValues.ToArray()

End Function

End Class

For C#: Web Service: Solution > right click > Add New Item > Web Service >. Name: Cascading.asmx. Language: C#. Go To > App_Code > Cascading.cs.

C#
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
// All other rights reserved.
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Collections.Generic;
using AjaxControlToolkit;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Xml;

///<summary>
/// Summary description for Cascading
///</summary>

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script,
// using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class Cascading : System.Web.Services.WebService {

    public Cascading () {
        //Uncomment the following line if using designed components
        //InitializeComponent();
    }

    [WebMethod]
    public CascadingDropDownNameValue[] GetCountries(string knownCategoryValues, 
                                        string category)
    {
        //ADO.Net
        SqlConnection cn =new SqlConnection();
        DataSet ds = new DataSet();
        DataTable    dt = new DataTable();
        string strCn = "data source=.;Initial Catalog=Db;Integrated Security=True";
        cn.ConnectionString = strCn;
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = cn;
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = "select * from tblCountry";

        try
        {
            cn.Open();
            cmd.ExecuteNonQuery();
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            da.Fill(ds);
        }
        catch
        {
        }
        finally
        {
            cn.Close();
        }
        dt = ds.Tables[0];

        List<CascadingDropDownNameValue> CountryValues = 
                    new List<CascadingDropDownNameValue>();
        foreach (DataRow row   in dt.Rows)
        {
            CountryValues.Add(new CascadingDropDownNameValue(
              row["Country"].ToString(), row["IDC"].ToString()));
        }

        return CountryValues.ToArray();
    }

    [WebMethod]
    public CascadingDropDownNameValue[] GetCities(string knownCategoryValues, 
                                                  string category)
    {
        StringDictionary kv = 
          CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);

        //'ContainsKey("Country") is one of property in Ajaxcontroltoolkit
        int countryId;

        countryId = System.Convert.ToInt32(kv["Country"]);

        //ADO.Net
        SqlConnection cn = new SqlConnection();
        DataSet ds = new DataSet();
        DataTable    dt = new DataTable();
        string strCn = 
          "data source=.;Initial Catalog=Db;Integrated Security=True";

        cn.ConnectionString = strCn;
        SqlCommand cmd = new SqlCommand();

        cmd.Connection = cn;
        cmd.CommandType = CommandType.Text;
        //-----I Defined a parameter instead of passing value 
        //     directly to prevent sql injection--------//
        cmd.CommandText = "select * from tblCity where CountryID=@myParameter Order by City";
        cmd.Parameters.AddWithValue("@myParameter", countryId.ToString());
        try
        {
            cn.Open();
            cmd.ExecuteNonQuery();
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            da.Fill(ds);
        }
        catch
        {
        }
        finally
        {
            cn.Close();
        }
        dt = ds.Tables[0];

        List<CascadingDropDownNameValue> CityValues = 
                  new List<CascadingDropDownNameValue>();
        foreach (DataRow row   in dt.Rows)
        {
            CityValues.Add(new CascadingDropDownNameValue(
               row["City"].ToString(), row["ID"].ToString()));
        }
        return CityValues.ToArray();
    }
}

GetCountries and GetCities are two functions that get two string arguments: knownCategoryValues and category. They have an output which is an array of strings: CascadingDropDownNameValue. In the GetCountries function, the Country dropdownlist is filled: we connect to the database and execute a query using ADO.NET, then in the foreach loop, fill the dropdownlist. When you select a certain row (in the above example, a country such as United States), knownCategoryValues will be equal to "country:1", which means category is country and knownCategoryValues is "1". 1 is the ID for United States in the database. In the GetCities function, the City dropdownlist is filled and filtered by knownCategoryValues, which is "1" in this case. In the Web Form, I have specified ParentControlID="ddListCountry" in CascadingCity, so the parent is country and the city will be filtered by the country ID. Additionally, I defined a parameter instead of passing value directly, to prevent SQL Injection.

Trying the demo step by step

  1. Go here for .NET 3.5 or here for .NET 4.0 and download the AJAX Control Toolkit file.
  2. Copy the folder "AjaxControlToolkit.Dll" and all its dependencies, they are 18 objects, to your web site in the Bin folder (C:\Cascading\Bin).
  3. Right click on Solution, choose Refresh, then right click again and click Add reference. Then in the Browse tab, double click on the Bin folder and double click on ajaxcontroltoolkit. On the Build menu > click Rebuild.
  4. Create the database and tables like above, and add some rows which have common words.
  5. Create a Web Form and name it CascadingDropDown.aspx. In the HTML view, write some code like above. (This should be exactly like my code because this section is case sensitive.)
  6. Create a Web Service: Solution > right click > Add New Item > Web Service > Name: Cascading.asmx. Language: C# or VB. Go to > App_Code > Cascading.cs or Cascading.vb.
  7. If you are a VB coder, use the VB sample, otherwise use the C# sample.
  8. Run the program, select a country such as United States, and you will see a list of cities in that country such as Detroit or New York City.

Feedback

Feel free to leave any feedback on this article; it is a pleasure to see your opinions and vote about this code. If you have any questions, please do not hesitate to ask me here.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)