I've been using Microsoft's ASP.NET AJAX implementation since the early "Atlas" beta days, but am just now starting to feel comfortable enough with its stability to use it in actual projects. Most of the sample controls that are included with the framework are remarkably easy (and fun) to setup and use. It's pretty amazing how much usability can be added to an application through the use of AJAX.
Well, I recently set out to use the CascadingDropDown control on one of my sites, but found that I ran into several different issues. The example that is included with the ASP.NET AJAX Control Toolkit grabs its values from an XML dataset. I, however, wanted the controls to grab their values from tables in a SQL Server 2005 database. Luckily, there is a pretty decent walkthrough for setting up the control with a database available on the ASP.NET AJAX webpage. I found it incomplete in a couple of important areas, though, so I thought that I would put up my version of a walkthrough for those who find themselves running into issues with implementation.
Walkthrough
I decided to use ASP.NET 2.0's datasets feature, with the new TableAdapters functionality to provide access to the data in my tables. Here are the steps to setting up the data access side:
1. Create a new dataset (right-click in Solution Explorer, "Add New Item...", Dataset).
2. Configure your TableAdapter(s) using the "TableAdapter Configuration Wizard".
- If you don't have an existing connection to your database, you will need to create one in the first dialog of the wizard.
- Build as many TableAdapter queries as you need. You should have a query built for each CascadingDropDown control that you'll be using in your application.
- The first query will likely be a simple select query on a single column (example, as presented in the ControlToolkit and in the code below: make of a car).
- The next query(ies) will need to "GetData" by a certain parameter that was returned from the previous query(ies) (example: model of a car, filtered by the make that was returned from the first query).
- You will probably want to sort by one or more fields in your query(ies), as well.
- Make sure that you validate your query(ies) to make sure that they return the proper records before moving on.
- If you are grabbing data from multiple tables, you will have to add each table into the dataset and build TableAdapter(s) for each one.
Now that you can access your data and return the necessary values through the dataset, you are ready to build your Page/Web Methods. I chose to make calls to Web Methods, as a known limitation of using Page Methods in the ASP.NET AJAX beta 1 release (note: they just released beta 2, but I'm not sure if this issue has been addressed in the new release) is that if you use Page Methods, the code has to live in the .aspx page.
1. To create a new Web Method, right-click on the directory where you want your code to live, select "Add New Item...", select "Web Service" from the templates, check the "Place code in separate file" option, and click "Add".
2. Make sure that you have all of the following namespaces, along with the [Microsoft.Web.Script.Services.ScriptService()] entry at the top of your .cs, assuming, of course, that you are programming in C#:
using System;
using System.Web;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Web.Services;
using System.Web.Services.Protocols;
using AjaxControlToolkit;
using System.Data;
using System.Data.SqlClient;
/// <summary>
/// Summary description for DataService
/// </summary>
[WebService(Namespace = "http://yourdomain/webservices")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[Microsoft.Web.Script.Services.ScriptService()]
public class DataService : System.Web.Services.WebService
{
public DataService ()
{
//Uncomment the following line if using designed components
//InitializeComponent();
}
3. build the Web Method(s). Following are two sample web methods:
[WebMethod]
public CascadingDropDownNameValue[] GetMakes(string knownCategoryValues, string category)
{ CarsTableAdapters.MakeTableAdapter makeAdapter = new CarsTableAdapters.MakeTableAdapter();
Cars.MakeDataTable makes = makeAdapter.GetMakes();
List<CascadingDropDownNameValue>values = new List<CascadingDropDownNameValue>();
foreach (DataRow dr in makes.Rows)
{ string make = (string)dr["Make"];
int makeId = (int)dr["MakeID"];
values.Add(new CascadingDropDownNameValue(make, makeId.ToString()));
}
return values.ToArray();
}
[WebMethod]
public CascadingDropDownNameValue[] GetModelsByMake(string knownCategoryValues, string category)
{ StringDictionary kv = CascadingDropDown.ParseKnownCategoryValuesString(knownCategoryValues);
int makeId;
if (!kv.ContainsKey("Make") || !Int32.TryParse(kv["Make"], out makeId)) { return null;
}
CarsTableAdapters.ModelTableAdapter modelAdapter = new CarsTableAdapters.ModelTableAdapter();
Cars.ModelDataTable modelTable = modelAdapter.GetModelsByMake(makeId);
List<CascadingDropDownNameValue> values = new List<CascadingDropDownNameValue>();
foreach (DataRow dr in modelTable.Rows)
{ values.Add(new CascadingDropDownNameValue((string)dr["Model"], dr["ModelID"].ToString()));
}
return values.ToArray();
}
The first method simply grabs the "Make" and "makeId" (key field) from the Make table and returns them to an array, displaying the Makes in the first dropdown. The second method parses out the valid car models, using the "makeId" to identify the valid returns, and displays them in the second dropdown.
The next step includes adding the dropdown list(s) to your ASP.NET page and pointing it/them to your Web Method(s). Here is the code to create two dropdown lists and hook them up to the Web Methods. Make sure that you turn EnableEventValidation off in the page directive (see code below). If you don't, your CascadingDropDowns will not populate and you'll get a helpful [Method 500 error]:
<%@ Page Language="C#" AutoEventWireup="true" EnableEventValidation="false" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="AjaxToolkit" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head1" runat="server">
<title>CascadingDropDown Example</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
Make:
<asp:DropDownList ID="ddlMakes" runat="server">
</asp:DropDownList><br />
<br />
Model:
<asp:DropDownList ID="ddlModels" runat="server">
</asp:DropDownList>
<AjaxToolkit:CascadingDropDown ID="CascadingDropDown1" runat="server" TargetControlID="ddlMakes"
Category="Make" PromptText="Select a Make" ServicePath="CarsService.asmx" ServiceMethod="GetMakes"
LoadingText="Loading Makes..." SelectedValue="" />
<AjaxToolkit:CascadingDropDown ID="CascadingDropDown2" runat="server" TargetControlID="ddlModels"
Category="Model" PromptText="Select a Model" ServicePath="CarsService.asmx"
ServiceMethod="GetModelsByMake" LoadingText="Loading Models..." SelectedValue=""
ParentControlID="ddlMakes" />
</form>
</body>
</html>
Resources
Some helpful resources that I used:
Oh, and by the way, the code snippets in this blog entry were formatted using Omar Shahine's "Insert Code for Windows Live Writer" extension.