Section 16.1. Client Callbacks


16.1. Client Callbacks

Contrary to popular belief, ASP.NET 2.0 does include built-in support for Ajax. Well, at least in a limited way. Rather than calling it Ajax, however, Microsoft refers to this ASP.NET 2.0 technology as client callbacks. Client callbacks enable ASP.NET web applications to implement asynchronous calls to the server using JavaScript: one built-in JavaScript function requests data and another fetches and asynchronously processes it.

You can think of client callbacks as a kind of lightweight postback, which is done using an XMLHttpRequest object. You can use JavaScript to display the data received from the server on the current page.

All that is required for this task is to create a page that implements the ICallbackEventHandler interface. You can do this in code, or declaratively by including an @ Implements directive in the page. Then you must implement two methods defined for the interface:


RaiseCallbackEvent()

To send the request


GetCallbackResult()

The callback function to receive the result

The mediator between the client and the server is the Callback Manager. It creates the XMLHttpRequest object and also receives the result from the server. The communication between client and Callback Manager happens with the ICallbackEventHandler interface.

Late in the beta phase of ASP.NET 2.0, the CallbackManager object was scrapped and the functionality was instead put in the Page.ClientScript class. However, the basic approach remains the same.


To demonstrate client callbacks, we'll port the familiar division example from Chapter 10 to Ajax. (Two numbers entered in an HTML form are divided using an Ajax call.) But before we delve into that example, we'll start with a simpler examplesquaring a number. To begin, we'll build the HTML form that a user will use to enter a number. Here's the markup:

 <form  runat="server">   <div>     <nobr>       <input type="text"  name="a" size="2" />       <sup>2</sup> =       <span  style="width: 50px;" />     </nobr>     <br />     <input type="button" value="Square Number"  runat="server" />   </div> </form> 

Note that the submit button is an ASP.NET HTML server control, because we want to access it in server code (unlike the other form elements, which we will change only with client script).

The page must implement the ICallbackEventHandler interface, so we'll add an @ Implements directive to the ASP.NET page:

 <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> 

The ICallbackEventHandler interface consists of the RaiseCallbackEvent() and GetCallbackResult() methods, whose signatures are as follows:

 void ICallbackEventHandler.RaiseCallbackEvent(string arg) string ICallbackEventHandler.GetCallbackResult() 

In the RaiseCallbackEvent() method, you provide a parameter (the input for the server call), which has to be of type string. The GetCallbackResult() method performs the actual calculation on the server and returns the output, also as a string. The second method does not accept any parameters, so you need a member variable to save the input for later.

In the RaiseCallbackEvent() method, you set this variable to the value of the argument sent from the client script:

 private string arg; void ICallbackEventHandler.RaiseCallbackEvent(string arg) {   this.arg = arg; } 

The GetCallbackResult() method then has to perform the calculation. This requires some type conversions: HTML form data is always a string, but calculations require numeric values. The result, however, must be a string again:

 string ICallbackEventHandler.GetCallbackResult() {   return Convert.ToString(            Math.Pow(              Convert.ToDouble(this.arg), 2)); } 

We are accepting input in RaiseCallbackEvent() and generating output in GetCallbackResult(), which concludes the work to be done on the server. In client script, we have to get the input (for example, from a text box filled in by the user). We must also accept the output returned by the server calculation to make the whole example work.

Conveniently, ASP.NET can generate the JavaScript code that sends the input to RaiseCallbackEvent(), using the Page.ClientScript.GetCallbackEventReference() method. This method has several overloads, but the most functional one expects this set of parameters:


control

A reference to the control that triggers the callback mechanism (usually, you choose this)


argument

The parameter you want to send as a JavaScript expression (usually, you access the form element you want to generate the parameter from)


clientCallback

The name of the (client) callback function as a string


context

An additional context (as a string) you can provide and receive again in the callback function


useAsync

A value indicating whether to use an asynchronous call (default: TRue) or a synchronous one

Usually, just the first four parameters suffice. In the particular example, the parameter to send is the value in the HTML input text box with the ID a. However, it is the HTML button that triggers the whole mechanism. To reference the contents of the text box, the following JavaScript expression is required:

 this.form.elements["a"].value 

The expression this.form links the current element (this) to the form in which it resides; from here on, the code can manipulate the text box in question. So here's the code you need to call GetCallbackEventReference():

 string js = Page.ClientScript.GetCallbackEventReference(   this,   "this.form.elements[\"a\"].value",   "callComplete",   null); 

The return value of this function call is JavaScript code that starts the mechanism on the client. All you have to do is to make the HTML button execute this code whenever it is clicked. Here's most convenient way is to use ASP.NET to attach the JavaScript code to the button:

 submit.Attributes.Add("onclick", js); 

The submit button has been configured into an HTML control to make it possible to add attributes using server code without having to use ugly inline code (<% ... %>).


All that is left to do is to implement the callback function, callComplete(). It accepts two parameters: the result (generated by GetCallbackResult()) and the context, if any (in our case, null). The resulting value is then written into the <span> element on the page.

 <script language="JavaScript" type="text/javascript"> function callComplete(result, context) {   document.getElementById("aSquare").innerHTML = result; } </script> 

Example 16-1 shows the complete markup and code.

Example 16-1. Using the ASP.NET 2.0 client callback function

 ClientCallbackSimple.aspx <%@ Page Language="C#" %> <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">   protected void Page_Load(object sender, EventArgs e)   {     string js = Page.ClientScript.GetCallbackEventReference(       this,       "this.form.elements[\"a\"].value",       "callComplete",       null);     submit.Attributes.Add("onclick", js);   }   private string arg;   void ICallbackEventHandler.RaiseCallbackEvent(string arg)   {     this.arg = arg;   }   string ICallbackEventHandler.GetCallbackResult()   {     return Convert.ToString(              Math.Pow(                Convert.ToDouble(this.arg), 2));   } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">   <title>Ajax</title>   <script language="JavaScript" type="text/javascript">   function callComplete(result, context) {     document.getElementById("aSquare").innerHTML = result;   }   </script> </head> <body>   <form  runat="server">     <div>       <nobr>         <input type="text"  name="a" size="2" />         <sup>2</sup> =         <span  style="width: 50px;" />       </nobr>       <br />       <input type="button" value="Square Number"  runat="server" />     </div>   </form> </body> </html> 

Figure 16-1 shows the result displayed when a user enters a number and clicks the Square Number button of Example 16-1.

Figure 16-1. Ajax with ASP.NET 2.0, but without Atlas


Looking at the source code in the browser, you can identify the code that was programmatically added to the submit button:

 <input name="submit" type="button"  value="Square Number" onclick="WebForm_DoCallback('_  _Page',this.form.elements[&quot;a&quot;] .value,callComplete,null,null,false)" /> 

The function WebForm_DoCallback() is loaded via the WebResource.axd virtual file and takes care of the XMLHttpRequest generation and server-side call (including a neat method of dynamically creating an invisible <iframe> element to exchange data with the server).

Although it takes a series of steps, ASP.NET client callbacks work really well. However, you can only provide one parameter, and it has to be of type string. Admittedly, there are actually two parameters, if you take the context into account. To submit more parameters, or other data types or complex types, you have to serialize the data into a string so you can pass it using the single string parameter. The .NET Framework offers several serializers that can be useful here. However in some cases, a handmade serialization method might be simpler to implement.

Going back to the division example, we can demonstrate this alternative approach of using nonstring values. In the division example, there are two numeric parameters that must be submitted to the server. The values come from the following HTML form:

 <form  runat="server">   <div>     <nobr>       <input type="text"  name="a" size="2" />       :       <input type="text"  name="b" size="2" />       = <span  style="width: 50px;" />     </nobr>     <br />     <input type="button" value="Divide Numbers"  runat="server" />   </div> </form> 

To handle these two values, the main change happens in the GetCallbackEventReference() call. The JavaScript expression to access the values is different. This time, the script reads the values of both text boxes and concatenates them using a newline character as a delimiterthis is our simple handmade serialization format. Here's the code for this task:

 string js = Page.ClientScript.GetCallbackEventReference(   this,   "this.form.elements[\"a\"].value + \"\\n\" + this.form.elements[\"b\"].value",   "callComplete",   null); 

In server code, the GetCallbackResult() method must then split this delimited string into two values, convert them to numbers, divide them, and convert the result back into a string:

 string ICallbackEventHandler.GetCallbackResult() {   char[] newline = {'\n'};   string[] values = this.arg.Split(newline);   return Convert.ToString(            (Convert.ToDouble(values[0]) /             Convert.ToDouble(values[1]))); } 

The remaining changes are only cosmetic. Example 16-2 shows the code needed to implement this example. Of course you would want to add some proper error handling to the script (for example, to prevent a divide-by-zero error). For now, it is omitted for brevity and clarity reasons.

Example 16-2. Using more than one parameter

 ClientCallback.aspx <%@ Page Language="C#" %> <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">   protected void Page_Load(object sender, EventArgs e)   {     string js = Page.ClientScript.GetCallbackEventReference(       this,       "this.form.elements[\"a\"].value + \"\\n\" + this.form.elements[\"b\"].value",       "callComplete",       null);     submit.Attributes.Add("onclick", js);   }   private string arg;   string ICallbackEventHandler.GetCallbackResult()   {     char[] newline = {'\n'};     string[] values = this.arg.Split(newline);     return Convert.ToString(          (Convert.ToDouble(values[0]) /           Convert.ToDouble(values[1])));   }   void ICallbackEventHandler.RaiseCallbackEvent(string arg)   {     this.arg = arg;   } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server">   <title>Ajax</title>   <script language="JavaScript" type="text/javascript">   function callComplete(result, context) {     document.getElementById("c").innerHTML = result;   }   </script> </head> <body>   <form  runat="server">     <div>       <nobr>         <input type="text"  name="a" size="2" />         :         <input type="text"  name="b" size="2" />         = <span  style="width: 50px;" />       </nobr>       <br />       <input type="button" value="Divide Numbers"  runat="server" />     </div>   </form> </body> </html> 

Although this works, the shortcomings of this approach are obvious. You have to take care of the serialization of multiple or complex parameters yourself, either by implementing your own solution or by using the built-in serializers of the .NET Framework. Also, the client callback mechanism does not add any features like data binding or built-in interaction with ASP.NET controls. Therefore, although it is suitable for lighter "Ajax" tasks, it can be really cumbersome for more complex requirements. For a more sophisticated approach to client callbacks, you may be interested in the blog postings by Bertrand Le Roy on this subject. Christoph Wille gives a good overview at http://chrison.net/CallbacksInASPNET20.aspx and also provides links to Le Roy's blog entries.

Serialization in the .NET Framework

The .NET Framework comes with the following serializers:


BinaryFormatter

Creates a binary byte stream


SoapFormatter

Creates a SOAP stream


XMLSerializer

Creates an XML byte stream

You have to instantiate one of these formatters and then call the Serialize() or Deserialize() method.





Programming Atlas
Programming Atlas
ISBN: 0596526725
EAN: 2147483647
Year: 2006
Pages: 146

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