Building AJAX Controls


AJAX (Asynchronous JavaScript and XML) is the future of the web. By taking advantage of AJAX, you can avoid performing a postback each and every time you perform an action in an ASP.NET page. A control that uses AJAX can communicate directly with a web server by performing a "sneaky postback."

Three of the standard ASP.NET controls use AJAX: the TReeView, GridView, and DetailsView controls. When you expand a node in a TReeView, the child nodes of the expanded node can be retrieved from the web server without a postback. When you sort or page rows in a GridView control, the rows can be retrieved from the server without a postback. Finally, when you page through records in a DetailsView control, you do not need to post the page back to the server.

Note

Microsoft has developed a new framework (code named Atlas) that enables you to build rich AJAX applications on top of the ASP.NET Framework. You can learn more about Atlas by visiting http://atlas.asp.net.


In this section, you learn how to take advantage of AJAX when building custom controls. We'll start simple. First, we create a ServerTimeButton control that retrieves the current time from the server and displays it in the browser without requiring a postback. Next, we'll create a more practical control. We re-create Google Suggest by creating an AJAX-enabled ComboBox control.

Implementing AJAX

To implement AJAX in a custom control, you must perform the following steps:

1.

Render the JavaScript that initiates the AJAX call.

2.

Create the methods on the server that reply to the AJAX call.

3.

Create the JavaScript on the browser that displays the result from the server.

You initiate an AJAX call from the browser in order to execute methods on the server. The server returns a result that can be used on the client.

You create the JavaScript that initiates the AJAX call by calling the Page.ClientScripts.GetCallbackEventReference() method. This method returns a string that represents a JavaScript function call that looks like this:

WebForm_DoCallback('myControl',null,showResult,null,showError,false) 


The GetCallbackEventReference() method is overloaded. Here are the parameters for one of the overloaded versions of this method:

  • control The control that initiates the AJAX call.

  • argument The argument that is sent to the web server in the AJAX call.

  • clientCallback The name of the JavaScript function that executes after a result is returned from the web server.

  • context The argument that is passed back to the clientCallback() and clientErrorCallback() methods after the AJAX call completes.

  • clientErrorCallback The name of the JavaScript function that executes when an error on the server results from an AJAX call.

  • useAsync When true, the AJAX call is performed asynchronously.

Next, you need to implement the methods on the server that respond to the AJAX call. To do this, you need to implement the ICallbackEventHandler interface.

Note

AJAX, in Microsoft terminology, is called client callbacks. The name AJAX was invented in a Blog post by Jesse James Garrett.


The ICallbackEventHandler interface has two methods that you must implement:

  • RaiseCallbackEvent This method is called first on the server when an AJAX call is performed.

  • GetCallbackResult This method returns the result of the AJAX call to the client.

Finally, you must implement a JavaScript function on the client that is called when the results are returned from the server.

Building a ServerTimeButton Control

We start by creating a really simple control that uses AJAX. The ServerTimeButton control retrieves the current time from the server and displays it in the web browser (see Figure 32.4). The ServerTimeButton control is contained in Listing 32.10.

Figure 32.4. Displaying the server time with the ServerTimeButton control.


Listing 32.10. ServerTimeButton.vb

[View full width]

Imports System Imports System.Web.UI Imports System.Web.UI.WebControls Namespace myControls     Public Class ServerTimeButton         Inherits WebControl         Implements ICallbackEventHandler         ''' <summary>         ''' Add the onclick attribute that initiates the AJAX call         ''' </summary>         Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)             Dim eRef As String = Page.ClientScript.GetCallbackEventReference( _                 Me, _                 Nothing, _                 "showResult", _                 Nothing, _                 "showError", _                 False)             writer.AddAttribute(HtmlTextWriterAttribute.Onclick, eRef)             MyBase.AddAttributesToRender(writer)         End Sub         ''' <summary>         ''' Add the Javascript file that process the AJAX call results         ''' to the page         ''' </summary>         Protected Overrides Sub OnPreRender(ByVal e As EventArgs)             Page.ClientScript.RegisterClientScriptInclude("serverTimeButton", Page .ResolveClientUrl("~/ClientScripts/ServerTimeButton.js"))         End Sub         Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)             writer.Write("Show Server Time")         End Sub         Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag             Get                 Return HtmlTextWriterTag.Button             End Get         End Property         ''' <summary>         ''' Called on server by AJAX         ''' </summary>         ''' <param name="eventArgument"></param>         Public Sub RaiseCallbackEvent(ByVal eventArgument As String) Implements  ICallbackEventHandler.RaiseCallbackEvent         End Sub         ''' <summary>         ''' Returns result back to AJAX call         ''' </summary>         ''' <returns></returns>         Public Function GetCallbackResult() As String Implements ICallbackEventHandler .GetCallbackResult             'throw new Exception("Server Exception");             Return DateTime.Now.ToString()         End Function     End Class End Namespace 

The ServerTimeButton control renders an HTML button. Clicking the button initiates the AJAX call to the server. The script for initiating the AJAX call is generated in the AddAttributesToRender() method by a call to the Page.ClientScript.GetCallbackEventReference() method.

Notice that the ServerTimeButton control implements the ICallbackEventHandler interface. This interface includes two methods named RaiseCallbackEvent and GetCallbackResult(). In Listing 32.10, the RaiseCallbackEvent() does nothing. The GetCallbackResult() method returns the current time as a string.

Finally, the ServerTimeButton control uses a JavaScript file named ServerTimeButton.js. This file is registered in the control's OnPreRender() method. The contents of the ServerTimeButton.js file are contained in Listing 32.11.

Listing 32.11. ClientScripts\ServerTimeButton.js

function showResult(result, context) {     alert( result ); } function showError(error, context) {     alert( error ); } 

The JavaScript file in Listing 32.11 includes two functions. The first function, named showResult(), displays the result of the AJAX call. This method simply displays the server time in a JavaScript alert box. The showError() function displays any error message returned by the server as a result of the AJAX call.

The page in Listing 32.12 uses the ServerTimeButton control.

Listing 32.12. ShowServerTimeButton.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show Server Time</title> </head> <body>     <form  runat="server">     <div>     Initial Time: <%= DateTime.Now.ToString() %>     <br /><br />     <custom:ServerTimeButton                  Runat="Server" />     </div>     </form> </body> </html> 

If you open the page in Listing 32.12 and click the button, the time is retrieved from the server and displayed in the browser. At no time is the page posted back to the server. The server time is retrieved through an AJAX call.

Building an AJAX Combobox Control

The application that caused all the excitement over AJAX was Google Suggest (http://www.google.com/webhp?complete=1). Google Suggest is an enhanced version of Google Search. As you type a search phrase, Google Suggest automatically displays a list of matching entries in a drop-down list (see Figure 32.5).

Figure 32.5. Using Google Suggest.


The amazing thing about Google Suggest is that every time you enter a new letter into the search box, an AJAX call is performed to retrieve a list of matching results from the server. The fact that the AJAX calls can be performed in real time blew everyone's mind.

In this section, we create a ComboBox control that mimics the functionality of Google Suggest. Each time you enter a new letter into the combo box, a new AJAX call is performed against the web server to retrieve matching entries (see Figure 32.6).

Figure 32.6. Displaying matching moves with the ComboBox control.


The ComboBox control is contained in Listing 32.13.

Listing 32.13. ComboBox.vb

[View full width]

Imports System Imports System.Collections.Generic Imports System.Data Imports System.Configuration Imports System.Web Imports System.Web.UI Imports System.Web.UI.WebControls Namespace myControls     Public Class ComboBox         Inherits CompositeControl         Implements ICallbackEventHandler         Private _comboTextBox As TextBox         Private _dataKeyName As String = String.Empty         Private _selectCommand As String = String.Empty         Private _connectionString As String = String.Empty         Private _clientArgument As String         ''' <summary>         ''' Name of the database field used for the lookup         ''' </summary>         Public Property DataKeyName() As String             Get                 Return _dataKeyName             End Get             Set(ByVal Value As String)                 _dataKeyName = value             End Set         End Property         ''' <summary>         ''' SQL Select command issued against database         ''' </summary>         Public Property SelectCommand() As String             Get                 Return _selectCommand             End Get             Set(ByVal Value As String)                 _selectCommand = value             End Set         End Property         ''' <summary>         ''' Connection String for database         ''' </summary>         Public Property ConnectionString() As String             Get                 Return _connectionString             End Get             Set(ByVal Value As String)                 _connectionString = value             End Set         End Property         Private ReadOnly Property ComboSelectId() As String             Get                 Return Me.ClientID + "_select"             End Get         End Property         Public Property Text() As String             Get                 EnsureChildControls()                 Return _comboTextBox.Text             End Get             Set(ByVal Value As String)                 EnsureChildControls()                 _comboTextBox.Text = value             End Set         End Property         Public Property Columns() As Integer             Get                 EnsureChildControls()                 Return _comboTextBox.Columns             End Get             Set(ByVal Value As Integer)                 EnsureChildControls()                 _comboTextBox.Columns = value             End Set         End Property         Protected Overrides Sub OnPreRender(ByVal e As EventArgs)             ' Make sure all the properties are set             If _dataKeyName = String.Empty Then                 Throw New Exception("DataKeyName cannot be empty")             End If             If _connectionString = String.Empty Then                 Throw New Exception("ConnectionString cannot be empty")             End If             If _selectCommand = String.Empty Then                 Throw New Exception("SelectCommand cannot be empty")             End If             ' Register Include File             If Not Page.ClientScript.IsClientScriptIncludeRegistered("ComboBox") Then                 Page.ClientScript.RegisterClientScriptInclude("ComboBox", Page.ResolveUrl ("~/ClientScripts/ComboBox.js"))             End If         End Sub         Protected Overrides Sub CreateChildControls()             ' Create the TextBox             _comboTextBox = New TextBox()             _comboTextBox.Attributes("autocomplete") = "off"             Controls.Add(_comboTextBox)         End Sub         Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)             ' Define the callback         Dim callBackRef As String = Page.ClientScript.GetCallbackEventReference( _                 Me, _                 "this.value", _                 "comboBox_ClientCallback", _                 String.Format("'{0}'", ComboSelectId), _                 "comboBox_ErrorCallback", _                 True)             ' Render the text box             _comboTextBox.Attributes.Add("onkeyup", callBackRef)             _comboTextBox.Attributes.Add("onblur", "comboBox_Blur(this)")             _comboTextBox.RenderControl(writer)             writer.WriteBreak()             ' Render the drop-down             writer.AddAttribute(HtmlTextWriterAttribute.Id, ComboSelectId)             writer.AddStyleAttribute(HtmlTextWriterStyle.Display, "none")             writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "absolute")             writer.RenderBeginTag(HtmlTextWriterTag.Select)             writer.RenderEndTag()         End Sub         Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag             Get                 Return HtmlTextWriterTag.Div             End Get         End Property         Public Sub RaiseCallbackEvent(ByVal clientArgument As String) Implements  ICallbackEventHandler.RaiseCallbackEvent             _clientArgument = clientArgument         End Sub         Public Function GetCallbackResult() As String Implements ICallbackEventHandler .GetCallbackResult             ' If no text, then return nothing             If _clientArgument.Trim() = String.Empty Then                 Return "[]"             End If             ' Otherwise, get the matching rows             Dim src As SqlDataSource = New SqlDataSource(_connectionString, _selectCommand)             src.SelectParameters.Add(_dataKeyName, _clientArgument)             Dim dvw As DataView             Try                 dvw = CType(src.Select(DataSourceSelectArguments.Empty), DataView)             Catch                 Return "[]"             End Try             ' Return matching rows in a JavaScript array             Dim rows As New List(Of String)()             Dim row As DataRowView             For Each row In dvw             rows.Add(String.Format("'{0}'", row(0).ToString().Replace("'", "\'")))             Next             Return "[" + String.Join(",", rows.ToArray()) + "]"         End Function     End Class End Namespace 

The control in Listing 32.13 renders a text box and a list box. As you type letters into the text box, a list of matching database records is displayed in the list box.

The ComboBox uses the JavaScript functions contained in Listing 32.14.

Listing 32.14. ClientScripts\ComboBox.js

// Display rows from database in the SELECT tag function comboBox_ClientCallback(result, context) {     // convert rows into an array     var rows;     eval( 'rows=' + result );     // Get the Select element     var comboSelect = document.getElementById( context );     // Add the options     comboSelect.options.length = 0;     for (var i=0;i<rows.length;i++)     {       var newOption = document.createElement("OPTION");       newOption.text= rows[i];       newOption.value= rows[i];       if (document.all)         comboSelect.add(newOption);       else         comboSelect.add(newOption, null);     }     // If results, show the SELECT, otherwise hide it     if (comboSelect.options.length > 0)     {       comboSelect.size = comboSelect.options.length + 1;       comboSelect.selectedIndex = 0;       comboSelect.style.display='block';     }     else       comboSelect.style.display='none'; } // When leaving comboBox, get selected value from SELECT function comboBox_Blur(src) {     var container = src.parentNode;     var comboSelect = container.getElementsByTagName('select')[0];     if ( comboSelect.style.display != 'none' && comboSelect.selectedIndex != -1)         src.value = comboSelect.value;     comboSelect.style.display = 'none'; } // If server error, just show it function comboBox_ErrorCallback(result) {     alert( result ); } 

The JavaScript library in Listing 32.14 contains three functions. The first function, comboBox_ClientCallback(), displays the results of a database lookup after each callback. This function updates the list of matching entries displayed by the list box.

The comboBox_Blur() function updates the TextBox with the item in the ListBox that matches the text entered into the TextBox. Finally, the comboBox_ErrorCallback() method displays any errors returned from the server.

The page in Listing 32.15 illustrates how you can use the ComboBox control. When you enter text into the combo box, a list of matching movie titles is displayed.

Listing 32.15. ShowComboBox.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As EventArgs)         lblResult.Text = ComboBox1.Text     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show ComboBox</title> </head> <body>     <form  runat="server">     <div>     <custom:ComboBox                  ConnectionString='<%$ ConnectionStrings:Movies %>'         DataKeyName="Title"         SelectCommand="SELECT Title FROM Movies             WHERE Title LIKE @Title+'%'             ORDER BY Title"         Style="float:left"         Runat="Server" />     <asp:Button                  Text="Submit"         Runat="server" OnClick="btnSubmit_Click" />     <hr />     <asp:Label                  Runat="server" />     </div>     </form> </body> </html> 

In Listing 32.15, the ConnectionString, DataKeyName, and SelectCommand properties are set. The ConnectionString property represents a connection to a database. The DataKeyName property represents a primary key column from a database table. Finally, the SelectCommand uses a SQL SELECT command to retrieve a list of matching records. Notice that the SELECT command uses a LIKE operator to retrieve all records that begin with the text entered into the combo box.




ASP. NET 2.0 Unleashed
ASP.NET 2.0 Unleashed
ISBN: 0672328232
EAN: 2147483647
Year: 2006
Pages: 276

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net