ne key problem that AJAX addresses is improving Web application user interfaces. By reducing the need to constantly refresh the entire Web page, Atlas helps ensure that your Web application is always responsive to user actions. For example, suppose you’re filling in a Web form to register for a free email account, or a free magazine and you are asked to select the country you are from. After you select the appropriate country, the entire page refreshes so that the application can now ask you specific questions related to the selected country, such as the state you are in, etc. Even worse, the information you have already painstakingly entered may disappear along with the refresh?and then you have to enter it all over again. Sounds familiar? This is the problem that I address in this fouth article on Atlas (now renamed to Microsoft ASP.NET AJAX).
In this article, I’ll show you how to create a sample application to control and monitor the user’s selection in DropDownList controls so that selecting an item in one control automatically changes the list of items in another control; all without refreshing the entire page.
Creating the Sample Application
Using Visual Studio 2005, create a new Atlas application and name it as C:CascadingDropDown. Drag and drop a CascadingDropDown control from the ToolBox onto the default.aspx page (see Figure 1).
Tip: The CascadingDropDown control is one of many controls available in the Atlas Control Toolkit, an extended library of Atlas controls developed by both Microsoft and community developers.
Next, insert a 2×2 table and populate it with two DropDownList controls as shown in Figure 1).
|Figure 1. Sample Application Form: Here’s how the default.aspx page should look after you place the controls|
Switch to Source View and add two CascadingDropDownProperties elements, shown in bold in the following code:
The CascadingDropDownProperties element associates the CascadingDropDown control with a DropDownList control. Here’s a brief explanation of the attributes for the element:
- Category?indicates the name of the category the DropDownList represents; more on this later.
- TargetControlID?specifies the control you are extending.
- ParentControlID?specifies the control that affects the content of the control as indicated in the TargetControlID attribute; this attribute is optional.
- ServiceMethod?specifies the name of the Web method that returns the list of values for the DropDownList control; you will define this later.
- ServicePath?specifies the name of the Web service .asmx file containing the Web method specified in the ServiceMethod attribute.
- PromptText?the text to display when no values are selected in the control.
Adding a Web Service to the Project
With the UI of the page out of the way, add a new WebService item to the current project. Using the default name of WebService.asmx, you should find the WebService.vb file located in the App_Code folder of the project.
Open the WebService.vb file and import the following namespaces (place them at the top of the file):
Imports AtlasControlToolkit Imports System.Data Imports System.Data.SqlClient
Next, define a GetAuthors() function that will provide data to populate the Authors dropdown list. This is the function named in the ServiceMethod attribute of the CascadingDropDownProperties element in the code above. Here’s the function code:
_ Public Function GetAuthors( _ ByVal knownCategoryValues As String, _ ByVal category As String) As _ CascadingDropDownNameValue() Dim values As New _ System.Collections.Generic.List( _ Of AtlasControlToolkit.CascadingDropDownNameValue) Dim conn As New SqlConnection( _ "Data Source=.SQLEXPRESS;Initial Catalog=pubs;" & _ "Integrated Security=True") Dim comm As New SqlCommand( _ "SELECT au_id, au_lname, au_fname FROM authors", conn) conn.Open() Dim reader As SqlDataReader = comm.ExecuteReader While reader.Read values.Add(New CascadingDropDownNameValue( _ reader("au_lname") & " " & _ reader("au_fname"), _ reader("au_id"))) End While conn.Close() Return values.ToArray End Function
The method must have exactly the following signature; even the parameter names must match exactly:
_ Public Function GetAuthors( _ ByVal knownCategoryValues As String, _ ByVal category As String) As _ CascadingDropDownNameValue()
The knownCategoryValues parameter will contain the key/value of the selected category, and the category parameter will contain the name of the category. For example, when the page loads the first time, it passes the following arguments to GetAuthors():
Here, the first argument is empty while the second argument is the category name defined in the CascadingDropDownProperties element.
This function returns an array of name/value pairs of type AtlasControlToolkit.CascadingDropDownNameValue; the return type is mandatory. The code shown connects to the pubs database in a SQL Server 2005 Express server instance and retrieves the authors’ names and respective ID values (from the au_id column), using the au_id as the key for the name/value pair. For this function, you don’t really care about the arguments passed in because we will load the entire list of authors anyway.
|Author’s Note: I am assuming you have the pubs database installed on a SQL Server instance. I used SQL Server 2005 Express Edition, which ships with Visual Studio 2005.|
The relationships between the authors, titleauthor and titles tables are shown in Figure 2.
|Figure 2. Pubs Database Relationships: The figure shows the relationships between the authors, titleauthor, and titles tables in the pubs database.|
When a user selects an author from the Authors dropdown list, you’ll need to retrieve the titles written by that author from the database, so the next function you need to define is GetTitles() (see Listing 1).
As you can see, the GetTitles() function is a little more complex than GetAuthors(). First, this function gets called only when the user selects an author in the “Authors” DropDownList control. At that point, the application invokes the GetTitles() function to fill the titles list with titles written by the selected author, using arguments such as:
In the preceding example call, the “Author” is the key and the “238-95-7766” is its corresponding value.
Using those values, the GetTitles() function will return only the relevant titles based on the author id (238-95-7766). To make it easy for you to extract the category value(s) contained in the knownCategoryValues parameter, the CascadingDropDown class has a helper method called ParseKnownCategoryValuesString():
Dim kv As StringDictionary = CascadingDropDown. _ ParseKnownCategoryValuesString(knownCategoryValues)
To check if the argument contains an au_id, the function contains the following code:
If Not (kv.ContainsKey("Author")) Then Return Nothing End If
Finally, GetTitles() retrieves the list of titles belonging to the specific author, which it finds by joining the titleauthor and titles table when it builds the SQL statement:
Dim sql As String = _ "SELECT titles.title, titles.title_id FROM titleauthor," & _ " titles WHERE (titleauthor.title_id = titles.title_id)" & _ " AND (titleauthor.au_id='" & kv("Author") & "')"
Testing the Application
To test the application, press F5 (Run) in Visual Studio. From the resulting page in the browser, select an author from the first DropDownList control to fill the second DropDownList control with that author’s titles (see Figure 3).
|Figure 3. Dependent Lists in Action: Selecting an author from the first dropdown list shows titles written by that author’s in the second dropdown list.|
How it Works
Obviously, this doesn’t all happen magically, even though you didn’t have to write any client-side code at all. The reason this works lies in the configuration discussed earlier in this article:
The preceding configuration code associates the two DropDownList controls with the backend Web services?causing the framework to write client-side code that handles changes to the selected item in the DropDownList control, calling the specified Web service when the user changes the selection to a valid item. The framework then uses the Web service-provided data to automatically populate the list with the appropriate items.
Caching to Improve Performance
Because the application must connect to the database server to fetch the author list whenever a user first loads the application, it might be a good idea to cache the author list to limit the server workload imposed by subsequent launches by other users. Thus, you can improve the GetAuthors() function by using the HttpContext.Current.Cache object:
_ Public Function GetAuthors( _ ByVal knownCategoryValues As String, _ ByVal category As String) As _ CascadingDropDownNameValue() Dim values As New System.Collections.Generic.List( _ Of AtlasControlToolkit.CascadingDropDownNameValue) Dim ds As DataSet ' ---check the cache--- ds = HttpContext.Current.Cache("authors") If ds Is Nothing Then ' ---create new dataset--- Dim conn As New SqlConnection( _ "Data Source=.SQLEXPRESS;Initial " & _ "Catalog=pubs;Integrated Security=True") Dim comm As New SqlCommand("SELECT au_id, au_lname, " & _ "au_fname FROM authors", conn) Dim ad As New SqlDataAdapter(comm) ds = New DataSet ad.Fill(ds) '---save the dataset into cache--- HttpContext.Current.Cache.Insert("authors", ds) Else ds = CType(HttpContext.Current.Cache("authors"), DataSet) End If For Each row As DataRow In ds.Tables(0).Rows values.Add(New CascadingDropDownNameValue( _ row("au_lname") & " " & row("au_fname"), row("au_id"))) Next Return values.ToArray End Function
Note that the new GetAuthors() version uses a DataSet rather than a DataReader. The new version now checks whether the cache contains the “authors” dataset, and if not, creates and caches it. When another user loads the page, the function fetches the dataset from the cache instead of from the database. As usual, you can modify the above code to support expiration, etc., but I’ll leave such enhancements as an exercise for readers.
One last thought regarding caching. While I have cached only the authors list, it might also be good to cache some of the titles list for those authors that are most likely to be chosen. With a large user base, this decision will save far more resources than simply caching the authors list.
In this article, you have seen how easy it is to use the CascadingDropDown control from the Atlas Control Toolkit to control the items list of DropDownList controls. Note that you don’t have to write any client-side code; all you need to do is tie the CascadingDropDown control’s ServiceMethod to a back end Web service.