Saturday, October 9, 2010

Cascading Drop Down List using ASP.NET and C# + LINQ TO SQL

This article explains how to build a cascading drop-down list, using the language of ASP.NET and C #. Cascading drop-down list allows you to filter information sent from the database, allowing users to select relevant information according to its previous selection. For example, when we want to allow the user to search our website name of a street, we can present him with a scrolling list of states / countries, and the user to select a state. The next drop-down list will be updated automatically from the database and ask for the relevant towns only.

The first thing we should do is create 3 tables in the database, the first one call “Countries”. This table including 2 columns for countId (int) and countName (nvchar = for the name of the country). The second table is called “Cities” and including 3 columns: cityId (int), countId (int – inherits from the Countries countId), cityName (nvchar) and “Streets” which including streetId (int), cityId (int – inherits from the Cities cityId), streetName (nvchar).

dbCount

At this article we are using LINQ TO SQL, so the next thing we are needing to do is to create Data Class and drag into it the database tables. My data class call “DataClass.dbml”.

Now lets Fill the database tables with data!

countries

at the cities table, don’t forget to fill up the countId!

cities

and finally, the streets table

streets

as you can see, i was filled some values for Miami and New York City only! so later when we will select for other cities, the street drop down list will not fire cause there will be no data to display. Lets begin write some code.

Create Aspx page and check the Place code in separate file. in this case i am not using Master Page. In the design mode, drag and drop 3 drop down list controls (ddl) to the Div section. Give each ddl unique id: countDDL, cityDDL and streetDDL. For the countDDL and cityDDL make sure you are using AutoPost and Enabled the cityDDL + streetDDL to false. next you will need to use the OnSelectedIndexChanged function for the 2 first DDLs.

<form id="form1" runat="server">
<
div>

<
asp:DropDownList ID="countDDL" AutoPostBack="true" runat="server" Height="16px" Width="217px" OnSelectedIndexChanged="countDDL_OnSelectedIndexChanged">
</
asp:DropDownList>
<
br />
<
br />
<
asp:DropDownList ID="cityDDL" AutoPostBack="true" runat="server" Height="16px" Width="217px" Enabled="false" OnSelectedIndexChanged="cityDDL_OnSelectedIndexChanged">
</
asp:DropDownList>
<
br />
<
br />
<
asp:DropDownList ID="streetDDL" runat="server" Height="16px" Width="217px" Enabled="false">
</
asp:DropDownList>

</
div>
</
form>

The next step, is to fill the countDDL with the countries names from the Countries table while the page is load. We need to do that on the Page_load event.

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
countDDL.AppendDataBoundItems = true;
countDDL.Items.Add(new ListItem("Select country...", ""));
DataClassesDataContext db = new DataClassesDataContext();
var selectCountries = from t in db.Countries
orderby t.countName
select t;
countDDL.DataSource = selectCountries;
countDDL.DataTextField = "countName";
countDDL.DataValueField = "countId";
countDDL.DataBind();

cityDDL.AppendDataBoundItems = true;
cityDDL.Items.Add(new ListItem("Select City...", ""));

streetDDL.AppendDataBoundItems = true;
streetDDL.Items.Add(new ListItem("Select Street...", ""));
}
}
protected void countDDL_OnSelectedIndexChanged(object sender, EventArgs e)
{

}
protected void cityDDL_OnSelectedIndexChanged(object sender, EventArgs e)
{

}

Don’t forget to use the OnSelectedIndexChanged functions for the countDDL and cityDDL so there will be no compile errors.At the Page_Load event we are first using the AppendDataBoundItems to display some content like “Select Country…” that not send from the SQL database. Next we are using Linq to sql select statement to read all the countries names from the Countries table (order by the name of the countries). Then we set the DataTextField to the countName column which hold the county name and the DataValueField that hold the countId integer.  The last action is to put some values in the cityDDL and the streetDDL AppendDataBoundItems property.


The next step is to fire the OnSelectedIndexChanged event for each DDL so when we will select some value from the countDDL the cityDDL will fill up with the relevant content.


The OnSelectedIndexChanged event code is very similar to the Page_Load event with few changes. First of all we need to Clear() the coutDDL so that the data inside the ddl will not duplicate again and again.

protected void countDDL_OnSelectedIndexChanged(object sender, EventArgs e)
{
cityDDL.Items.Clear();
cityDDL.Items.Add(new ListItem("Select City...", ""));
streetDDL.Items.Clear();
streetDDL.Items.Add(new ListItem("Select Street...", ""));

cityDDL.AppendDataBoundItems = true;
DataClassesDataContext db = new DataClassesDataContext();
var selectCities = from t in db.Cities
where t.countId == Convert.ToInt32(countDDL.SelectedItem.Value)
orderby t.cityName
select t;
cityDDL.DataSource = selectCities;
cityDDL.DataTextField = "cityName";
cityDDL.DataValueField = "cityId";
cityDDL.DataBind();

if (cityDDL.Items.Count > 1)
{
cityDDL.Enabled = true;
}
else
{
cityDDL.Enabled = false;
streetDDL.Enabled = false;
}
}

The changes we used in this code is the where statement to filter and match the cities values to the selected country (by using the countId integer) and the if statement that check the return value(s) from the cityDDL so if there is no value the cityDDL will stay enabled false. We need to handle the cityDDL OnSelectedIndexChanged function to fire the streetDDL. We are doing that exactly the same way we’ve thread the cityDDL OnSelectedIndexChanged function.

protected void cityDDL_OnSelectedIndexChanged(object sender, EventArgs e)
{
streetDDL.Items.Clear();
streetDDL.Items.Add(new ListItem("Select Street...", ""));

streetDDL.AppendDataBoundItems = true;
DataClassesDataContext db = new DataClassesDataContext();
var selectStreets = from t in db.Streets
where t.cityId == Convert.ToInt32(cityDDL.SelectedItem.Value)
orderby t.streetName
select t;
streetDDL.DataSource = selectStreets;
streetDDL.DataTextField = "streetName";
streetDDL.DataValueField = "streetId";
streetDDL.DataBind();

if (streetDDL.Items.Count > 1)
{
streetDDL.Enabled = true;
}
else
{
streetDDL.Enabled = false;
}
}

I hope you find this article useful, feel free to comment.


This is the full Default.aspx.cs code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
countDDL.AppendDataBoundItems = true;
countDDL.Items.Add(new ListItem("Select country...", ""));
DataClassesDataContext db = new DataClassesDataContext();
var selectCountries = from t in db.Countries
orderby t.countName
select t;
countDDL.DataSource = selectCountries;
countDDL.DataTextField = "countName";
countDDL.DataValueField = "countId";
countDDL.DataBind();

cityDDL.AppendDataBoundItems = true;
cityDDL.Items.Add(new ListItem("Select City...", ""));

streetDDL.AppendDataBoundItems = true;
streetDDL.Items.Add(new ListItem("Select Street...", ""));
}
}
protected void countDDL_OnSelectedIndexChanged(object sender, EventArgs e)
{
cityDDL.Items.Clear();
cityDDL.Items.Add(new ListItem("Select City...", ""));
streetDDL.Items.Clear();
streetDDL.Items.Add(new ListItem("Select Street...", ""));

cityDDL.AppendDataBoundItems = true;
DataClassesDataContext db = new DataClassesDataContext();
var selectCities = from t in db.Cities
where t.countId == Convert.ToInt32(countDDL.SelectedItem.Value)
orderby t.cityName
select t;
cityDDL.DataSource = selectCities;
cityDDL.DataTextField = "cityName";
cityDDL.DataValueField = "cityId";
cityDDL.DataBind();

if (cityDDL.Items.Count > 1)
{
cityDDL.Enabled = true;
}
else
{
cityDDL.Enabled = false;
streetDDL.Enabled = false;
}
}
protected void cityDDL_OnSelectedIndexChanged(object sender, EventArgs e)
{
streetDDL.Items.Clear();
streetDDL.Items.Add(new ListItem("Select Street...", ""));

streetDDL.AppendDataBoundItems = true;
DataClassesDataContext db = new DataClassesDataContext();
var selectStreets = from t in db.Streets
where t.cityId == Convert.ToInt32(cityDDL.SelectedItem.Value)
orderby t.streetName
select t;
streetDDL.DataSource = selectStreets;
streetDDL.DataTextField = "streetName";
streetDDL.DataValueField = "streetId";
streetDDL.DataBind();

if (streetDDL.Items.Count > 1)
{
streetDDL.Enabled = true;
}
else
{
streetDDL.Enabled = false;
}
}
}