The new Microsoft ASP.NET AJAX web development framework (which we'll refer to simply as AJAX throughout the rest of this article) is like a mythological figure with three faces to its personality: one is friendly but rigid, one is playful, and the third is, to most ASP.NET developers, a bit frightening.
Face number one is the friendly ASP.NET-like face of AJAX that lets you drag and drop AJAX controls onto your forms just as you would standard ASP.NET server controls, but gives you client-side, wiz-bang performance without having to understand a thing about JavaScript, the Document Object Model, DHTML, or Asynchronous communication with the host. Cool, eh? I love that face of AJAX, and I deeply resent that this wonderful, lovely, beautiful ASP.NET-like face is often the last one shown to ASP.NET programmers, which I why I wrote a rant on the subject last summer.
That said, there are times that you want to go beyond the controls AJAX provides you, whether server-side or client-side. Server-side, you might want to create a custom-control. Client-side, you might want to create your own AJAX extender to modify the behavior of an AJAX client-side control; that is the second face of AJAX. Microsoft anticipates this, and provides help for creating your own custom AJAX extenders.
Finally, sometimes (and far less often than some would have you believe) you may be forced to set aside all maps and guidebooks and bravely go where none have gone before. If you are a JavaScript and DHTML programmer, this is a piece of cake. It's what you do every day, and you'll laugh (laugh, I say!) at how trivial it is.
If, like us, however, you've studiously avoided writing JavaScript (ugly, untyped, nasty little language that looks like C but will bite you the first chance it gets), then you'll want to draw a careful pentangle on the floor, light the candles in just the right order, say the ancient incantations in Aramaic, and follow along closely as we reveal the third (rather horrifying) face of AJAX: the underlying gibberish that makes it all work.
To work with any of these characters, you'll need to set up AJAX for your development environment. Fortunately, this is now incredibly easy (and safe) to do. First, buy a computer. Second, install Visual Studio 2005. (You can also use Visual Web Developer, a free download, except you will not be able to create your own AJAX control extenders, which is the second face described in this article.)
Third, and finally, download and install the AJAX software. Go to the AJAX site (http://ajax.asp.net/) and complete the following steps:
If you are using VS2005, and you want to create your own control extenders, complete the following additional steps:
The friendly face of AJAX is drag and drop, which lets you place client-side controls on your web pages just as ASP.NET lets you do with its server controls. Begin by opening Visual Studio (or VWD) and choosing Create Web Site . From the templates, choose ASP.NET AJAX-Enabled Web Site. Pick your language of choice (Visual Basic for this article) and a location on your hard drive; name your new application DragAndDropAjax, and click OK.
To get started, you'll create a simple application with a text box, into which an administrator may type a user's name and a Delete button (initially disabled). When the administrator enters a name into the text box, the Delete button will be enabled, and when the Delete button is pressed, the username will be deleted from the membership database (we'll fake that last part!).
Visual Studio (or VWD) will set up your environment to support an AJAX-enabled application. This includes adding an object of type ScriptManager to your default.aspx file, as shown in Figure 1.

Figure 1. Script Manager in boilerplate markup
(Click to enlarge.)
The job of the Script Manager is to coordinate the action of all the AJAX controls on the page. Now drag and drop the following controls from the toolbox onto the page.
| Control | ID | Text |
|---|---|---|
| Label | Label1 | Name: |
| TextBox | Name | |
| Button | Delete | Delete |
Set the Enabled property of the Button to false.
Source view will look something like Figure 2.

Figure 2. TextBox & Save Button in Design view
(Click to enlarge.)
|
You want to ensure that when any text is entered, the Delete button is enabled. To do so, you'll implement an event handler for the TextChanged event of the text box. TextChanged is the default event for a TextBox, so you can quickly create this event handler by switching to Design view and double-clicking on the TextBox. The IDE will create an event handler for you named for the control (Name) and the event (TextChanged), and place you inside the Name_TextChanged event handler, ready to enter the following highlighted line of code:
Protected Sub Name_TextChanged( _
ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles Name.TextChanged
Delete.Enabled = Name.Text.Length > 0
End Sub
As you can see, whenever any text is entered in the Name text box, the button is enabled; otherwise, it is disabled.
Our next goal is to handle the event fired when the administrator clicks the Delete button. Normally, we'd check the database for the name, and then delete that user from the database (perhaps after receiving confirmation). For now, we'll just set the text box field to empty and the button back to disabled.
The default event handler for the button is Click, so you can just double-click on the button, which will create the event handler. Enter the highlighted code below:
Protected Sub Delete_Click( _
ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Delete.Click
Name.Text = String.Empty
Delete.Enabled = False
End Sub
Run the application and type a name in the text box. Unfortunately, the button never becomes enabled. The text box does not automatically post back after each keystroke (and a good thing, because it would cause enormous inconvenience!). We can fix that by switching to Design view, clicking on the text box, and then in the Properties window, setting the text box's AutoPostBack property to True. Now, run the application again. Type some text, and then tab off of the text box (forcing the post back to the server). Aha! The page posts back and the button becomes enabled!
If you click the button, the page posts back again, the text box is cleared, and the button becomes disabled.
It may be difficult to see the disadvantage of posting back the entire page in this small example, but normally there are many other controls on a page like this that do not need to be updated and that will flicker when your data is updated. To simulate this, let's temporarily add two other controls to the page so that we can see the effect of a postback on these controls. We'll add a list of checkboxes and a calendar control.
Make sure you are in Design view, and drag a CheckBoxList onto the page from the Toolbox. Use the Edit Items link on the smart tag to add some items to the list (see Figure 3 for some examples). Next drag a Calendar control onto the page. The result should resemble Figure 3.

Figure 3. Adding items to the page
Run the example again, and watch the new items flicker each time you post back the page! It is to avoid this flicker that we'll use the AJAX update panel.
In Design view, drag an UpdatePanel control from the AJAX Extensions section of the Toolbox onto the page, below all the other controls. Then drag all three of the original UI controls—the label, the text box and the button—directly into the UpdatePanel. It should look more or less like Figure 4.

Figure 4. Placing an UpdatePanel in Design view
In Design view, drag an UpdatePanel control from the AJAX Extensions section of the Toolbox onto the page, below all the other controls. Then drag all three of the original UI controls—the label, the text box, and the button—directly into the UpdatePanel. It should look more or less like Figure 4.
Switch to source view and find the markup for the Update panel (UpdatePanel1 in the following snippet).
asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="Label1" runat="server"
Text="Name:" />
<asp:TextBox ID="Name" runat="server" AutoPostBack="True" />
<asp:Button ID="Delete" runat="server"
Text="Delete" Enabled="false" />
</ContentTemplate>
</asp:UpdatePanel>
Other than adding a ContentTemplate for you (thanks!), the Label, TextBox, and Button controls are undisturbed by UpdatePanel. This is key: AJAX controls do not change the ASP.NET controls, they just extend their magic over existing ASP.NET controls.
Run the application. The Update Panel will handle the AutoPostBack, and rather than posting back the entire page, it will post back just the contents of the panel, asynchronously, sparing the rest of the page the postback and eliminating the flicker. Ahhh, so much better!
|
The AJAX controls are provided in two packages: the smaller set that is placed in the AJAX tab and the far larger set that you placed in the AJAX Control Toolkit tab that you created when you installed the toolkit.
Many of the tools in the AJAX Control Toolkit are not standalone controls, they are extenders; that is, they extend the capabilities of existing AJAX controls . To see this at work, create a new website named ToolkitExtenders that is an exact copy of the previous website.
We'll use an extender to make sure the administrator really, truly wants to delete a user. Rather than creating a special button that confirms the user's intent, or even adding code to the button's Click event, we'll add client-side code to confirm the user's decision by adding a ConfirmButtonExtender and telling it that its "target control" is our Delete button. This will create client-side code to check the decision without requiring a roundtrip to the server, thus creating a much faster and more responsive application.
In Source view, locate the Delete button within the UpdatePanel and drag a Confirm Button Extender from the AJAX Control Toolkit tab directly below the Delete button, taking care that the new control stays within the ContentTemplate element. Your new control will have the prefix CC1 (or AjaxToolkit, depending on how your system is configured). You'll note two other changes: a Register directive will have been placed at the top of your page, and, if you expand your bin directory, you'll see the .dll and .pdb files for the AJAX Control Toolkit. Handy.
While the ConfirmButtonExtender will have automatically been assigned an ID, you'll have to assign the TargetControlID property yourself, though Intellisense will help. You'll also want to set the text for the confirmation in the ConfirmText property, using the following code snippet:
<asp:Button ID="Delete" runat="server"
Text="Delete" Enabled="false" />
<cc1:ConfirmButtonExtender
ID="ConfirmButtonExtender1"
runat="server"
TargetControlID = "Delete"
ConfirmText = "Really, truly delete?" />
When the administrator clicks the Delete button, the ConfirmButtonExtender will take control and cause a confirmation dialog to appear with your confirmation text, as shown in Figure 5.

Figure 5. Confirmation Extender
(Click to enlarge.)
If the extenders that are provided with the toolkit do not meet your needs, you are free to create your own. AJAX will even help, and a walk through is provided, with the Toolkit from which we have borrowed liberally for this example (don't write in, we know we're not being original, but we hope we're adding clarity and context!)
One limitation of the previous examples is that the button does not become enabled until you actually leave the TextBox, at which point the TextChanged event fires. It would be better if you could detect keystrokes in the TextBox directly and enable the Button accordingly.
A custom AJAX Control Extender is just the ticket. To create one, copy the ToolkitExtenders website to a new website, called CustomExtender.
After you've created your copy, immediately right-click on the solution and choose Add New Project. Select ASP.NET AJAX Control Project from the My Templates section of the Add New Project dialog box. Name your new project ChangeAwareButton, as shown in Figure 6.

Figure 6. Adding an AJAX Control Project to the website
(Click to enlarge.)
This will add a project to the website, and that project will contain several autogenerated files: ChangeAwareButtonBehavior.js, ChangeAwareButtonDesigner.vb, and ChangeAwareButtonExtender.vb, as shown in the Solution Explorer shown in Figure 7.

Figure 7. Solution Explorer showing the AJAX Control Project
The JavaScript file is the one where you will be doing most of your coding. This file contains the script that runs on the client, implementing the logic and behavior (hence the name of the file) of the control.
The VB file ending with "Designer" contains a class that enables design time functionality and is not typically modified.
You will be coding in the other VB file, ChangeAwareButtonExtender.vb. This is the server-side class that allows an extender to be created. It also allows you to create and access properties of the control extender. Any properties created in this class must also have matching properties defined in the JavaScript Behavior file.
|
Open the Extender file, ChangeAwareButtonExtender.vb. You will see there are several attributes decorating the ChangeAwareButtonExtender class, including one called TargetControlType. In the boilerplate, the type of the target control is Control, meaning any type of control will qualify. Change this to TextBox to limit the target types to TextBoxes, as shown in the highlighted code in the following snippet:
<Designer(GetType(ChangeAwareButtonDesigner))> _
<ClientScriptResource("ChangeAwareButton.ChangeAwareButtonBehavior", _
"ChangeAwareButton.ChangeAwareButtonBehavior.js")> _
<TargetControlType(GetType(TextBox))> _
Public Class ChangeAwareButtonExtender
Inherits ExtenderControlBase
Rename the default property name MyProperty to something more meaningful, such as TargetButtonID. This must be changed in three places in the Extender file, ChangeAwareButtonExtender.vb, and in five places in the JavaScript Behavior file, ChangeAwareButtonBehavior.js.
this._MyPropertyValue with this._TargetButtonIDValue.
Add a second property, ButtonText, that will allow you to dynamically set the text on the button. In order to add a property to an AJAX control extender, you must modify two files in synchrony: the JavaScript Behavior file and the VB Extender file.
First add the following public string property definition to ChangeAwareButtonExtender.vb:
<ExtenderControlProperty()> _
<DefaultValue("")> _
Public Property ButtonText() As String
Get
Return GetPropertyValue("ButtonText", "")
End Get
Set(ByVal value As String)
SetPropertyValue("ButtonText", value)
End Set
End Property
Even though VB is not normally case-sensitive, in this case the property name is case-sensitive because it must correspond exactly with the property name in the JavaScript file.
This extender will extend the text box, but it will reference the button. To make that clear to the framework, there is a special attribute, IDReferenceProperty, that you must attach to the TargetButtonID property to signal that it is the button that is referenced.
<ExtenderControlProperty()> _
<DefaultValue("")> _
<IDReferenceProperty(GetType(Button))> _
Public Property TargetButtonID() As String
Get
Return GetPropertyStringValue("TargetButtonID")
End Get
Set(ByVal value As String)
SetPropertyValue("TargetButtonID", value)
End Set
End Property
Having set up the VB, it is time to set up the corresponding JavaScript. Open the behavior file, ChangeAwareButtonBehavior.js and add the one line of code to the ChangeAwareButton.ChangeAwareButtonBehavior function (the last line in the method; we are defining a new member variable to hold the property value for the button's text value).
ChangeAwarebutton.ChangeAwarebuttonBehavior = function(element)
{
ChangeAwarebutton.ChangeAwarebuttonBehavior.initializeBase(this, [element]);
this._TargetButtonIDValue = null;
this._ButtonTextValue = null; // we added this line
We'll need accessor functions for the new property as well. The following lines of code get and set the value for _ButtonTextValue:
get_ButtonText : function()
{
return this._ButtonTextValue;
},
set_ButtonText : function(value)
{
this._ButtonTextValue = value;
},
You can place these either above or below the existing accessors for the TargetButtonID.
Finally, and this is (you should pardon the pun) key to our extender, we want to implement a client-side method to respond to the keyup event in the TextBox control. There is no server-side keyup event, but there is one for the client side, which is what makes AJAX so powerful. Unfortunately, Microsoft does not provide access to this event in any of its extenders, which, after all, is why we are writing our own extender.
We do this as classic JavaScript, putting the declaration in the prototype section, where you find the comment "Add your initialization code here."
$addHandler(this.get_element(), 'keyup',
Function.createDelegate(this, this._onkeyup));
this._onkeyup();
Put the method itself inside the class, just below the definition of the dispose method,
_onkeyup : function()
{
// set a local variable to represent the button
var e = $get(this._TargetButtonIDValue);
if (e) // if we got the button
{
// set the variable disabled to whether the text box is empty
var disabled = (this.get_element().value == "");
// set the button disabled to the value of the variable
e.disabled = disabled;
// if the button has text...
if (this._ButtonTextValue)
{
// if the button is disabled
if (disabled)
{
// set the control's old value to the current value
this._oldValue = e.value;
// set the current value to the button's text value
e.value = this._ButtonTextValue;
}
else // if the button is not disabled
{
if (this._oldValue) // if there is an old value
{
// set the text to the old value
e.value = this._oldValue;
} // end if _oldvalue
} // end else
} // end if TextValue
} // end if e
}, // end on key up function
|
Build the Solution by clicking on Build→Build Solution. This will cause a new section called ChangeAwareButton Components to be added to the top of the Toolbox, as shown in Figure 8.

Figure 8. New Toolbox Section
(Click to enlarge.)
There needs to be a component in this toolbox section called ChangeAwareButtonExtender. If it is there, great. Drag it onto the page.
If not, you can manually add the control directly to the page. You need to add to the website a reference to the Control Extender project. Right-click on the website in Solution Explorer. Click on Add Reference…, then click on the Projects tab of the Add Reference dialog box, as shown in Figure 9.

Figure 9. Projects tab of the Add Reference dialog
Switch to the Source view of Default.aspx. Add the following Register directive to the top of the page:
<%@ Register Assembly="ChangeAwareButton"
Namespace="ChangeAwareButton.ChangeAwareButton "
TagPrefix="extndr"%>
Now add a declaration for a ChangeAwareButtonExtender control inside the UpdatePanel, which already contains the TextBox and Button, with the following line of markup:
<extndr:ChangeAwareButtonExtender ID="cab" runat="server" />
Once the ChangeAwareButtonExtender is on the page, switch back to Design view. Click on that control, and then go to the Properties window.
Remember to hook this extender to its target TextBox, by setting the TargetControlID property to the TextBox's ID.
Click on the TextBox. The Properties window will now include an item corresponding to the ChangeAwareButtonExtender. The BehaviorID will already be filled in. Add values for the DisabledButtonText and TargetButtonID subproperties, as shown in Figure 10.

Figure 10. TextBox Properties showing the ChangeAwareButtonExtender subproperties
Run the app. The page will look like Figure 11. The button is initially disabled and its Text property will be what you entered as the DisabledButtonText property of the ChangeAwareButtonExtender.

Figure 11. ChangeAwareButton, initially disabled
As soon as any text is entered in the text box, the button will become enabled and its Text property will revert to it's original value declared in the Button declaration in the markup, as shown in Figure 12.

Figure 12. ChangeAwareButton enabled after entering text
Since this example is based on the previous example, clicking the button will call into play the ConfirmButtonExtender, to ask the user if he is sure he wants to delete this user.
There are times when even a custom extender won't do—when you have to reach down into the Document Object Model (by way of DHTML) and manipulate it directly with JavaScript, calling upon the asynchronous capabilities of the browse directly, not mediated through the Update Panel, but by interacting directly with the XMLHttp object. This is hard core; this is pedal to the metal; this is raw AJAX; this is ugly.
To demonstrate how unvarnished AJAX works, we'll create a word-wheel: a control that allows you to type letters into a text box and see all the city names that begin with the letters that you've typed so far. To do this, we need a database of city names; we'll use the list of cities in the AdventureWorks database.
To begin, copy the previous example to a new website called HardCoreAjax.
Open the HardCoreAjax website and set Default.aspx to be the default start page. Run it to make sure it still works correctly.
Add a new page to the website called UserNameLookup.aspx. This page is used as a vehicle for allowing JavaScript on Default.aspx to run some server-side code. When it runs, it's Page_Load method will read the usernames from the database, then construct the HTML to build a select list from the results. It will return this HTML text string to the calling JavaScript, which will then insert it into the Default page asynchronously.
When UserNameLookup.aspx displays in Source view, delete the entire contents of the page except for the Page directive on the first line. Replace the content with a single ASP.NET Literal control, so that the entire contents of the file look like Example 1. You can ignore the red squiggly line under the asp tag of the Literal control, which normally indicates a syntax error.
Example 1. UserNameLookup.aspx
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="UserNameLookup.aspx.vb"
Inherits="UserNameLookup" %>
<asp:Literal id="UserNames" runat="server" />
Let's be crystal clear on what this page will do: its job is to look up the city names in the database, assemble the HTML that we want to inject into our Default.aspx page, and put that HTML into the Literal control. We will then insert that HTML into the page at the client, using DHTML and JavaScript.
Open the code-behind file for UserNameLookup, UserNameLookup.aspx.vb. Add all the highlighted code from Example 1-2, below. This includes two imports statements outside the class declaration to provide access to the required Data namespaces, one event handler method, Page_Load, and one helper method, UserNamesForPartialName. All of this is straightforward server-side ASP.NET code; nothing special.
When the page loads, it looks at the query string that is passed in and formulates a query to select city names from the database. If it gets back names, its job is to create an HTML statement that will display the cities in a listbox (a Select object). It does so by assembling the HTML line by line. The first line creates the select object: </p>
"<select id='slctName' size=5 onchange=""SelectName();"">"
The code then iterates through all the rows of the data table, and for each city name an option is created, with its value and its text set to the name of the city.
For Each row In dt.Rows
returnString = returnString + "<option value='" + _
row("UserName").ToString() + "' >" + _
row("UserName").ToString() + _
"</option>"
Next
When all the cities have been accounted for, the select statement is completed.
returnString = returnString + "</select>"
This entire string is then placed into the Literal control, which becomes the entirety of the .aspx page. Thus, this page is nothing more than the HTML of the listbox, which can be placed, as is, right into the Default page markup, in the appropriate <div >, using DHTML.
|
Example 2 shows how the <select> element is created.
Example 2. UserNameLookup.aspx.vb
Imports System.Data
Imports System.Data.SqlClient
Partial Class UserNameLookup
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Me.Load
If (Request.QueryString.Count > 0) Then
Dim queryUserName As String = Request.QueryString.Get(0).ToString()
Dim dt As DataTable = UserNamesForPartialName(queryUserName)
If (dt Is Nothing Or dt.Rows.Count = 0) Then
UserNames.Text = _
"Sorry no user names found with that letter combination."
Else
Dim returnString As String = String.Empty
returnString = returnString + _
"<select id='slctName' size=5 onchange=""SelectName();"">"
Dim row As DataRow
For Each row In dt.Rows
returnString = returnString + "<option value='" + _
row("UserName").ToString() + "' >" + _
row("UserName").ToString() + _
"</option>"
Next
returnString = returnString + "</select>"
UserNames.Text = returnString
End If
Else
UserNames.Text = String.Empty
End If
End Sub
Public Function UserNamesForPartialName(ByVal strPartialName As String) _
As DataTable
Dim connectionString As String = _
ConfigurationManager.AppSettings("AdventureWorks")
Dim connection As SqlConnection = New SqlConnection(connectionString)
Dim queryString As String = _
"select distinct LastName as UserName from Person.Contact where " + _
"LastName like '" + _
strPartialName + "%' order by LastName"
Dim ds As DataSet = New DataSet()
Try
Dim dataAdapter As SqlDataAdapter = _
New SqlDataAdapter(queryString, connection)
dataAdapter.Fill(ds, "Names")
Catch ex As Exception
'' Handle exception
UserNames.Text = ex.Message
Finally
connection.Close()
End Try
Return ds.Tables("Names")
End Function
End Class
Open Default.aspx in Source view. You'll add all the JavaScript inside the <head> element at the top of the page. You'll also add an attribute to the TextBox txtName so that it will point to one of the JavaScript functions to handle the keyup event. Finally, you'll add a <div> element where the JavaScript function can insert the HTML you created in UserNameLookup's code behind.
Default.aspx is unchanged except for the script, so let's walk through the script one piece at a time.
<script language="javascript" type="text/javascript">
var xmlHttp
function showHint(str)
{
if (str.length==0)
{
document.getElementById("TextBoxHint").innerHTML=""
return
}
xmlHttp = new XMLHttpRequest()
var url="UserNameLookup.aspx"
url=url+"?q="+str
xmlHttp.onreadystatechange=stateChanged
xmlHttp.open("GET",url,true)
xmlHttp.send(null)
}
The first method, showHint, takes a string (which it will get from the contents of the text box). If the string is empty, it sets the <div> (which we named TextBoxHint) to empty and returns. The innerHTML property of the
If the text is not empty, then the function creates XMLHttpRequest to handle asynchronous communication.
We set the url variable to the name of the aspx page that is going to look up the cities (UserNameLookup.aspx) and append to that name the characters "?q" indicating that we're going to pass in values to the query string object, and then we append whatever value we obtained from the text box (that is, the portion of the city name typed by the user).
var url="UserNameLookup.aspx"
url=url+"?q="+str
We are now ready to open UserNameLookup.aspx and wait for its processing to be completed, asynchronously. We need a call back so that we are notified when the xmlHttp object's onreadystatechange method fires (which it will when the called page finishes its work), and we assign the name of the method state changed:
xmlHttp.onreadystatechange=stateChanged
Having done, so we open the page and then send null to force the method call and to put us into a wait state.
Each time the onreadystatechange event fires, our stateChanged method is called and we examine it to see if the page is complete (ready state is complete or value 4) and the status is OK. If so, we take the responseText from the xmlHttp object and we assign it to the innerHTML of the Div, thus putting the listbox where we earlier had nothing!
function stateChanged()
{
var OK = 200
if (( xmlHttp.readyState == 4 || xmlHttp.readyState == "complete" )
&& xmlHttp.status == OK )
{
document.getElementById("TextBoxHint").innerHTML = xmlHttp.responseText
}
}
You will need to add a connection string to web.config so that UserNameLookup can connect to the database. Add the following code snippet to web.config just before the <system.web> element:
<appSettings>
<add key="AdventureWorks" value="Data Source=ServerName;
Initial Catalog=AdventureWorks;Integrated Security=True;" />
</appSettings>
Run the app and type a character in the text box. The select list will appear below the text box and, as with the previous example, the button will become enabled and its text will change. Type a second character, and the list in the select box will narrow.

Figure 13. HardCoreAjax in action
(Click to enlarge.)
We believe strongly that you can go for a long time using AJAX with the controls provided by Microsoft, and we strongly recommend compiling and exploring the samples provided with the Control Toolkit.
For more on extenders, the best bet is to start off with our sample and then to vary it, probably with a good book on JavaScript by your side (we recommend JavaScript The Definitive Guide by David Flanagan).
If you decide to dive into DHTML and to write raw AJAX code, our strongest recommendation is Dynamic HTML, the Definitive Reference by Danny Goodman, which was specifically updated for AJAX. Goodman is a wonderful writer.
All of the material in this article is expanded on for the new ASP.NET programmer in our forthcoming book Learning ASP.NET by Jesse Liberty and Dan Hurwitz. For a somewhat different perspective on the same material, it is also covered in Programming .NET 3 by Jesse Liberty and Alex Horovitz.
The code for this article is available on Jesse's website: http://www.GotDotNet3.com along with links to a support forum, errata, and other items of interest.
Jesse Liberty is a senior program manager for Microsoft Silverlight where he is responsible for the creation of tutorials, videos and other content to facilitate the learning and use of Silverlight. Jesse is well known in the industry in part because of his many bestselling books, including O'Reilly Media's Programming .NET 3.5, Programming C# 3.0, Learning ASP.NET with AJAX and the soon to be published Programming Silverlight.
Dan Hurwitz is the president of Sterling Solutions, Inc., where for nearly two decades he has been providing contract programming and database development to a wide variety of clients.
Return to the Windows DevCenter.
Copyright © 2009 O'Reilly Media, Inc.