Those demanding users are my motivation to create this custom control. Let me introduce
AjaxDropDownList, my first attempt to contribute into AJAX world.
AjaxDropDownList is a dropdownlist control that has the following features:
- Fetch data asynchronously in the background from a server source, with no postback.
- Can trigger change event to other dropdownlists, thus generating a cascading linked dropdownlist effect.
- Is encapsulated into a single control which can be easily dragged and dropped into the designer or added from the server code.
- Uses common code to access the
selectedItem just like a normal dropdownlist, thus can be easily integrated with other UI framework.
- Compatible with Internet Explorer 6, Mozilla Firefox 1.04, and Netscape 8.02.
Although I call the control as
How to use the control
Starting from the sample project
Download and open the project in VS.NET 2003. Edit GetLookupData.aspx.cs and change the connection string to point to a valid NorthWind database. If for some reason you don’t have NorthWind database, you can download the database script from Microsoft. Just do a search on Google.
Once the solution is built successfully, run and browse the default.aspx. Evaluate if this is the control you are looking for.
What the demo is about
In this demo page, we have three
AjaxDropDownLists: Customers (
ddlCustomers), Orders (
ddlOrders) and Products (
Orders dropdownlist depends on Customers. It means if we make a selection in Customers dropdownlist, then the Order dropdownlist will be filtered based on our selection. Furthermore, Products is depending on Orders. So a selection on Customers will trigger a change in Orders, and subsequently trigger a change in Products. Please note that all this happens without any postbacks.
Now press the Submit button and the page will finally do a postback. The selected text of each dropdownlist will be shown on the right hand side. This is to demonstrate that the standard code to access the selection in ASP.NET
DropDownList still applies to
What is in there
There are three important parts:
It contains the custom control. Put this file in a separate Class Library project so that we can add the control to the toolbox without any trouble.
It is a sample web page that uses
AjaxDropDownList controls. The controls are dropped into the designer from the Toolbox.
AjaxDropDownList into your toolbox, right click on the toolbox pane, and then select Add/Remove Items. It will bring up Customize Toolbox dialog. Press Browse button then select CustomControl.dll or the assembly that contains
AjaxDropDownList. A big list of control with checkboxes will appear. Ensure that
AjaxDropDownList is selected and close the dialog. The control will appear in the toolbox, ready to be dropped to the designer.
This is the page that handles the request from
xmlHttp and returns the appropriate JSON. It needs to handle two query strings:
id” is the lookup name or identifier, e.g., Country, Currency, Order, Product, and InvoiceStatus.
filter” (optional) describes the filter in name-value pair, e.g., Customer, ALFKI.
A request like:
"Get data from Orders for Customer code = ALFKI"
It is up to you how you want to implement the request handler.
Warning: GetLookupData.aspx is just a simple example which is not suitable for a product environment.
Therefore, in the sample project I provide the source code in its original format and all comments still in place. Please refer to SourceScript.aspx during this walkthrough.
xmlHttp object to make requests to the web server either asynchronously or synchronously. As the request can be made without refreshing the page, the web page looks more responsive and interactive. The method
getXMLHTTP() is called to a get reference to the
xmlHttp object, regardless of how the browser implements this object.
AjaxDropDownList is rendered as a
<SELECT> element in HTML. Each of these elements has its controller, called
AjaxDropDownController. The controller has a lot of things to do:
- Execute asynchronous request to web server to get data.
- Populate the dropdownlist.
- Listen to the change event of dropdownlist.
- Be the observer and the observable.
- Persist the content of dropdownlist in the client side.
Think of controller in the context of Model-View-Controller, being the
<SELECT> element as the view, the data that resides in the web server as the model, and the controller itself as the controller, although I don't want to emphasize this pattern as it is not fully implemented.
Performing asynchronous background request
When the controller needs to update its dropdownlist, it will call
load() which in turn calls
getSource(). While calling these methods, it may pass a filter string, which is the name-value pair of the dropdownlist that it depends to. Inside the
getSource() method, a request URL is constructed which contains the
var requestUrl = baseUrl + "?id=" + self.lookupName;
if (filter != undefined && filter != "")
requestUrl += "&filter=" + filter;
Then after a reference to the
xmlHttp object is secured, it will send the request. Note the last parameter in
xmlHttp.open which is set to
true to indicate an asynchronous request.
xmlHttp = getXMLHTTP();
xmlHttp.onreadystatechange = doReadyStateChange;
xmlHttp.open("GET", requestUrl, true);
As the nature of the request is asynchronous, we could not determine when the response will be available. Therefore, we assign an event handler
doReadyStateChange to the
onreadystatechange property. This event handler will be called each time the state of the request changes.
if (xmlHttp.readyState == 4)
if (xmlHttp.status == 200)
eval("var d=" + xmlHttp.responseText);
if (d != null)
alert("There was a problem retrieving the XML data:\n" +
doReadyStateChange, we check for
readyState = 4 which means "complete" and
status= 200 which indicates an "OK" HTTP status code. Once these conditions are satisfied, it is time to process the response stream.
Processing the response stream
For example, when we send a request like this:
The server will return:
We concatenate the
eval("var d=" + xmlHttp.responseText);
As a result, an in-memory object hierarchy is created as follows:
d +---  +--- value: 24
| |--- name: Guaraná Fantástica
+---  +--- value: 55
| |--- name: Pâté chinois
+---  +--- value: 75
|--- name: Rhönbräu Klosterbier
We can traverse the object hierarchy with ease, for example:
d.value will return 24.
d.name will return Rhönbräu Klosterbier.
d.length will return 3.
This object is then passed to
populateList(), which is responsible to populate the corresponding
<SELECT> element. The
populateList will first clear the option items from the
select, then iterate through the object hierarchy and create new option items.
AjaxDropDownController implements the observer pattern. An instance of
AjaxDropDownController can be both the observer and the observable. Each instance keeps a list of observers, so that when the value changes in the corresponding dropdownlist, the controller can notify each observer about the changes.
The list of observers is kept in this array:
var observers = ;
This method is called to add a new observer into the list. Before adding into the list, it will check whether the new observer is already in the list, thus ensuring uniqueness..
removeObserver() - omitted.
A complete implementation of the observer pattern requires this method. This method is called to remove an observer from the list. Currently, I don't see any practical usage so I don't implement the method.
We call this method to notify all observers about the changes in the the corresponding dropdownlist. This method will construct a filter string based on the
selectedIndex of the dropdownlist, then iterate through the observers list and call the
When this method is called, the controller will call
getSource to get the source data asynchronously using
When the content of the dropdownlist is changed in the client side, the change is not carried to the server side during postback. Therefore, we could not use the standard code to access the selected item in the dropdownlist, e.g. using
SelectedIndex of the dropdownlist. This raises an issue when we want to incorporate the control into a data binding framework or we simply could not afford to write special code to handle the control.
That is the reason why I keep the content of the dropdownlist in the client side. For every
AjaxDropDownList, there will be one hidden field associated with it. This hidden field is the container of the dropdownlist content as a value delimited string, e.g.:
24|Guaraná Fantástica|55|Pâté chinois|75|Rhönbräu Klosterbier
The delimiter character is configurable in the control. Therefore, if you don't like '|', you can change to another character..
During postback, the value delimited string will be read and the corresponding items are created in the dropdownlist.
The server side
Discussion about the code is not complete without touching the server side because this is where the data comes from. Unlike the client side, the server side code is relatively simple. Basically, we need to handle the request sent by the
xmlHttp and provide the data in the form of JSON.
It is up to you how you want to get the data from the database. The GetLookupData.aspx is not a good example as it is prone to SQL injection attacks. In real life situations, you may need to call the data access framework instead of directly connecting to the database. You may also need to cache the data in memory to save the roundtrip to database.
What's most important for the client side is the format of the response you return to the client side. It has to be in this format:
where value1... valueN denote item values and name1... nameN denote item texts.
The custom control
AjaxDropDownList is a custom control that inherits from
System.Web.UI.WebControls.DropDownList. It encapsulates all code to provide dynamic data population from the client side and let the dropdownlist participate in the linked dropdownlist chain.
It has four additional public properties on top of the standard properties from
ArrayList of observer objects. When you add another
AjaxDropDownList into this, the
AjaxDropDownList will become dependent on the current
DropDownList. For example:
It will make
DropDownList2 dependent on
Set the URL of the source data without
filter parameters, e.g. http://localhost/getLookupHandler.aspx.
Identifier of the lookup list associated with the current
DropDownList, e.g. Country, InvoiceStatus. This identifier will be passed as
id parameter in the request, for example:
It will also be used to compose
filter, for example:
A character to separate values persisted in the hidden field in the client side. Defaulted to '|'
The control has been tested using Microsoft Internet Explorer 6, Mozilla Firefox 1.04, and Netscape 8.02.
Some works need to be done on the designer side. Comments and ideas are most welcome. If the community shows enough interest, I will invest more time to refine this control.
- 11 July 2005: Article and download file is updated - now compatible with Mozilla Firefox 1.04 and Netscape 8.02.
- 6 June 2005: Initial release.