Section 6.5. Lower the Cost of Server Callbacks


6.5. Lower the Cost of Server Callbacks


Note: Send data back to the server without a postback by using the Client Callback Manager.

One of the inherent limitations of web applications is the costly delay that occurs when a web page posts data to the server that requires the page to be reloaded. Instead of updating only the portion of the page affected by the postback, the entire page has to be refreshed when some elements of the page need to be changed. For example, if a user selects "US" as the country in a registration web page, a drop-down list should display all the states in the U.S. If the user selects a country other than the U.S., the drop-down list should change to display the states of the selected country. Current implementation refreshes the entire page, which is often slow and frustrating. (Another technique is to send all of the states to the client and use JavaScript to display the related states when the user chooses a country, but this method largely inflates the size of the page and increases loading time.)

One technique that current ASP.NET 1.x developers use to overcome this postback problem is employing the Microsoft XMLHTTP ActiveX object to send requests to server-side methods from client-side JavaScript. In ASP.NET 2.0, this process has been simplified and encapsulated within the function known as the Client Callback Manager.

XMLHTTP

The ASP.NET Client Callback Manager uses XMLHTTP behind the scenes to encapsulate the complexities in sending data to and from the servers and clients. And so, in order for the Callback Manager to work, you need a web browser that supports XMLHTTP. Internet Explorer is, obviously, one of them. To use the Client Callback Manager, you need IE 5.0 or higher.


6.5.1. How do I do that?

To see how the Callback Manager in ASP.NET 2.0 works, let's build the simple web application shown in Figure 6-13.

Figure 6-13. Developing the sample application for client callback


The example will allow a user to enter a Zip Code into a text box and retrieve the city and state information without posting back to the server. A TextBox control is used for entering a Zip Code, while a Button control is used to invoke the code (to get the city and state based on the Zip Code) on the server. The result is then displayed in the city and state text boxes.

The application also has two DropDownList controls on the form. When a user selects a particular country, the states (or cities) belonging to the selected country are retrieved from the server and displayed in the second DropDownList control.

  1. Launch Visual Studio 2005 and create a new web site project. Name the project C:\ASPNET20\chap06-ClientCallback.

  2. Populate the default Web Form with the controls shown in Figure 6-14.

    Figure 6-14. Populating the form with controls


  3. Add three items to the ddlCountry DropDownList control. You can add them by switching the default Web Form to Source View and entering the following bold lines:

    <asp:DropDownList  Runat="Server" >   <asp:ListItem>Select Country</asp:ListItem>   <asp:ListItem Value="US">United States</asp:ListItem>   <asp:ListItem Value="Sing">Singapore</asp:ListItem>   <asp:ListItem Value="UK">United Kingdom</asp:ListItem> </asp:DropDownList>

  4. Switch to the code-behind of the default Web Form. The Web Form that is going to receive the postback needs to implement the ICallbackEventHandler interface. You also need to declare a public String (its use will be evident later on):

    Partial Class Default_aspx    Inherits System.Web.UI.Page    Implements ICallbackEventHandler    Public callbackStr As String

    The ICallbackEventHandler interface has only one method to implement: RaiseCallbackEvent( ). This method is invoked when the client sends a postback to the server. In this case, this is the place to check the city and state information of a Zip Code, as well as retrieve the states and cities of a country.

    Ideally, all of this information should be retrieved from a web service, but for simplicity you will hardcode the result to be returned for a limited number of queries. Code the RaiseCallbackEvent( ) method as shown in Example 6-1.

    Example 6-1. ClientCallBack Server-side RaiseCallbackEvent( )
    Public Function RaiseCallbackEvent(ByVal eventArgument As String) As _   String Implements _   System.Web.UI.ICallbackEventHandler.RaiseCallbackEvent   If eventArgument.StartsWith("1:") Then     '---strips away the command     eventArgument = eventArgument.Substring(2)     '---get city and state based on Zipcode     Select Case eventArgument       Case "95472" : Return "Sebastopol,CA"       Case "02140" : Return "Cambridge,MA"       Case Else          Throw (New Exception("ZipCode not valid!"))         End Select   ElseIf eventArgument.StartsWith("2:") Then     '---strips away the command     eventArgument = eventArgument.Substring(2)     '---get states and cities related to country     Select Case eventArgument       Case "Sing" : Return "Singapore,"       Case "US" : Return _   "Alabama,California,Maryland,Massachusetts,New York,Oklahoma,Wisconsin,"       Case "UK" : Return _   "Birmingham,Cambridge,Christchurch,Leeds,Sheffield,"       Case Else         Return ""     End Select   Else       Return "Command not recognized"   End If End Function

  5. Notice that the RaiseCallbackEvent( ) function takes in and returns a result of String data type. Therefore, if you have complex data types to transfer from the client to the server (and vice versa), you need to serialize the complex object into a string and then back.

    The eventArgument parameter is passed from the client. To retrieve the state and city based on Zip Code, the eventArgument parameter would look like this:

    1:02140

    where 1: is the command and 02140 is the Zip Code.

    To retrieve all states and cities based on country, the eventArgument parameter would look like this:

    2:US

    where 2: is the command and US is the country code.


    Tip: Note that for the first command, the returning result (city and state) is separated by a commafor example, Sebastopol,CA. For the second command, the returning result (states or cities) is also separated by commasfor example, Alabama,California, Maryland,Massachusetts,New York,Oklahoma,Wisconsin,.

  6. In the Page_Load event, you need to wire up the "Get City and State" button and the Country drop-down list. You also need to generate the code that performs the callback by using the GetCallbackEventReference( ) method from the ClientScriptManager class:

    The ClientScriptManager class is the one that handles tasks such as client-side scripts registration. You can get an instance of the ClientScriptManager class through the Page's ClientScript property.

    Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) _   Handles Me.Load   '---prevent the Get City & State button from postback   btnGetCityandState.Attributes.Add("onClick", "return false")                ddlCountry.Attributes.Add("onChange", "GetStatesFromServer( )")   callbackStr = Page.ClientScript.GetCallbackEventReference(_                 Me, "Command", "CallBackHandler", _                 "context", "onError", False) End Sub

  7. The parameters for the GetCallbackEventReference( ) method are:


    control (control)

    A reference to the caller itself


    argument (string)

    The string to be passed to the server side for processing


    clientCallback (string)

    The reference to the callback handler on the server side


    context (string)

    A token used to identify who actually initiated the callback


    errorCallback (string)

    The reference to the callback handler when an error occurs


    async (Boolean)

    Specifies if the callback script should be called asynchronously

    The callbackStr variable will store the following string when the form is loaded:

    WebForm_DoCallback('_  _Page',Command,                     CallBackHandler,context,onError,false)

    What is important here is that Command refers to the string that is going to be passed to the server, while CallBackHandler is the function that is invoked (on the client) when the server returns a result to the client.

  8. Let's now define the functions required on the client side. Switch to Source View for the default Web Form and add in the script block shown in Example 6-2.

    Example 6-2. Script for ClientCallback client-side functions
    ... <head runat="server">     <title>Untitled Page</title> <script> function GetStateFromZip( ){   var Command = "1:" + document.forms[0].elements['txtZipCode'].value;   var context = new Object( );   context.CommandName = "GetStateFromZip";   <%=callbackStr%> }      function GetStatesFromServer( ) {   var Command = "2:" + document.forms[0].elements['ddlCountry'].value;   var context = new Object( );   context.CommandName = "GetStatesFromCountry";   <%=callbackStr%> }      function CallBackHandler(result, context) {   if (context.CommandName == "GetStateFromZip" ) {     var indexofComma = result.indexOf(",");     var City = result.substring(0,indexofComma);     var State = result.substring(indexofComma+1,result.length);     document.forms[0].elements['txtState'].value = State;      document.forms[0].elements['txtCity'].value = City;    } else    if (context.CommandName == "GetStatesFromCountry")   {     document.forms[0].elements['ddlState'].options.length=0;     while (result.length>0) {       var indexofComma = result.indexOf(",");       var State = result.substring(0,indexofComma);       result = result.substring(indexofComma+1)       opt = new Option(State,State);        document.forms[0].elements['ddlState'].add(opt);     }   } }      function onError(message, context) {   alert("Exception :\n" + message); } </script> </head> ...

  9. The GetStateFromZip( ) and GetStatesFromServer( ) methods basically formulate the request to be sent to the server side; in this case, it takes the value of the TextBox control (and DropDownList control) and puts it into the callbackStr variable. The <%=callbackStr%> statement will insert the generated string into the function, so that at runtime it becomes:

    function GetStateFromZip( ){   var Command = "1:" + document.forms[0].elements['txtZipCode'].value;   var context = new Object( );   context.CommandName = "GetStateFromZip";   WebForm_DoCallback('_  _Page',Command,CallBackHandler,context,onError, false) }      function GetStatesFromServer( ){   var Command = "2:" + document.forms[0].elements['ddlCountry'].value;   var context = new Object( );   context.CommandName = "GetStatesFromCountry";   WebForm_DoCallback('_  _Page',Command,CallBackHandler,context,onError, false) }

  10. Notice that both functions return the call to the CallBackHandler( ) method; the CallBackHandler( ) method will be invoked when the server returns the result to the client. Hence, there is a need to differentiate who the return caller is. You use the context variable to set the command name for each type of call (GetStateFromZip or GetStatesFromCountry).

    The result will be returned as the variable result. The result is then parsed and displayed accordingly in the controls on the page.

  11. To complete this example, remember to wire the GetStateFromZip( ) function to the Button control.

    <asp:Button          OnClientClick="GetStateFromZip( )"       runat="server" Text="Get City and State"       Width="144px" />

  12. As for the Country DropDownList control, recall that earlier in the Page_Load event we have this statement:

    ddlCountry.Attributes.Add("onChange", "GetStatesFromServer( )")

  13. Essentially, this means that when the item in the DropDownList control changes, the GetStatesFromServer( ) function will be called.

  14. Press F5 to test the application. You can now access the server without a postback (see Figure 6-15).

Figure 6-15. Using the Callback Manager to avoid a postback



Note: JavaScript is case sensitive, so be sure to use the correct case for control names.

  1. If you click the "Get City and State" button without supplying a Zip Code, a JavaScript pop-up window will display the error message (see Figure 6-16). This is handled by the onError( ) function on the client side.

Figure 6-16. Displaying the error pop-up window when an error occurs


6.5.2. What about...

...detecting web browsers that do not support Client Callback?

As I mentioned, you need IE 5.0 or higher to use the Client Callback manager. So how do you detect if the user's browser could support Client Callback? Use the SupportsCallback property, like this:

Protected Sub Page_Load(ByVal sender As Object, _                         ByVal e As System.EventArgs) _                         Handles Me.Load     If Request.Browser.SupportsCallback Then         '---prevent the Get City & State button from postback         btnGetCityandState.Attributes.Add("onClick", "return false")         ddlCountry.Attributes.Add("onChange", "GetStatesFromServer( )")         callbackStr = Page.ClientScript.GetCallbackEventReference(Me,                       "Command", _"CallBackHandler", "context",                       "onError", True)     Else         Response.Write("Your browser does not support Client Callback.")     End If End Sub

6.5.3. Where can I learn more?

For a discussion of the implications of script callbacks in ASP.NET 2.0, check out this article: http://msdn.microsoft.com/msdnmag/issues/04/12/CuttingEdge/.

For a basic understanding of how the XMLHTTP ActiveX object works, check out http://www.devx.com/getHelpOn/10MinuteSolution/20358.



ASP. NET 2.0(c) A Developer's Notebook 2005
ASP. NET 2.0(c) A Developer's Notebook 2005
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 104

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