Developing applications using the Web ADF - Web controls  

Working with AJAX capabilities of the Web ADF


ASP.NET 2.0 supports both synchronous page postbacks and asynchronous client callbacks.  The Web ADF controls are designed leverage AJAX capabilities with the ASP.NET 2.0 Callback Framework to enhance the Web application developer and user experience.  It is recommended that you are familiar with these technologies and concepts before proceeding.

The Web ADF extends the ASP.NET 2.0 callback framework to generate and process asynchronous requests within a Web application.  In general, any client-server interaction in the Web ADF will involve an initial callback request and response, followed by subsequent callbacks to synchronize changes made in the initial callback. 

 

It is important to note that each callback request will iterate through the page lifecycle in the Web application.  As a result, a single user action in the browser may iterate through the page lifecycle multiple times, once for each callback.   This is required because, like a full postback, a callback must reconstruct the page and its contents to process a request.  Note that the ASP.NET Page control contains a property named IsCallback to determine whether the current request is a callback.  The IsPostback property will return true for both full postbacks and callbacks.       


Listed below are four areas of logic in the ADF designed to support and utilize the ASP.NET callback framework.  The Web ADF controls leverage these areas to support callbacks out-of-the-box.  As a developer, you can utilize one or more of these components to create and manage custom callback solutions.  

Two properties of the ASP.NET Callback framework are important to understand from the perspective of Web ADF.  First, the callback message for both the request and response can only contain string content.   The format of the callback message is arbitrary.  The Web ADF includes it's own string format definition explicitly defined to support callback utilization by the Web ADF in the client browser and on the server.   And second, only one control on the server is responsible for processing a callback request and generating a response.  Any ASP.NET server control can implement the System.Web.UI.ICallbackEventHandler interface and be utilized for processing callbacks (note that a Page is a server control).   All Web ADF controls implement the ICallbackEventHandler interface. 

How do you know which control is responsible for processing the callback?  On the client, the first parameter in the WebForm_DoCallback() JavaScript function defines the id of the server side control to process the callback.  The WebForm_DoCallback() function is included with JavaScript embedded in the ASP.NET System.Web.dll assembly and is available when a client script reference is registered (see the walkthrough in the AJAX capabilities with the ASP.NET 2.0 Callback Framework  topic).   In the server application, you can interrogate the current Request object for the parameter "__CALLBACKID".  The string returned is the id of a control in the page.  The following code provides an example to determine if a postback is a callback, and which server-side control will process the callback:

[C#]
if (Page.IsCallback)
{
  string control_id = Request.Params["__CALLBACKID"];
  Control control = Page.FindControl(control_id);
}
  

Custom Callback Scenarios

There are three scenarios you may choose from to customize the Web ADF and utilize its implementation of the ASP.NET 2.0 Callback framework.  In all scenarios, the Web ADF CallbackResult and CallbackResultCollection classes are integral to synchronizing changes to Web ADF controls on the server with the client browser.   A detailed discussion of Web ADF callback result classes will be provided after the presentation of all three scenarios.    


1) Use only Web ADF components to generate and process callbacks on the client and server

The diagram below illustrates how a Web ADF control on the client (rendered using HTML\JavaScript in the client browser) initiates the callback request, a Web ADF control processes the request on the server and generates one or more Web ADF CallbackResults, then the client processes the callback response using Web ADF JavaScript designed for Web ADF CallbackResult strings.   The most important point here is that the Web ADF control is responsible for processing the callback and generating a response message in a format that the Web ADF JavaScript can understand, namely the format defined by Web ADF CallbackResult classes and collections.  Note that the Web ADF control creates the callback response for you - no explicit work is required on your part as the developer to construct or return the callback response.         



This is most common scenario for leveraging Web ADF AJAX capabilities - it includes the pattern for creating custom toolbar items that execute server-side logic using the Web ADF Toolbar architecture.  When using the Web ADF Toolbar the Callback framework is managed for you, so it's relatively easy to incorporate and execute custom code solutions in a Web ADF without explicitly managing callback messages or classes.   Walkthrough #1 below demostrates this scenario. 

This scenario also includes working with events on Web ADF controls that generate callbacks.  The Web ADF control on which the event occurred is responsible for generating the callback response, but changes to other controls or browser content can be packaged with the response.  The Web ADF control generating the callback response maintains a collection of CallbackResult objects accessible via the CallbackResults property.   CallbackResults generated by other Web ADF controls can be added to the CallbackResults of the Web ADF generating the response.  In addition, custom CallbackResult objects can be created and added to the collection.   Walkthrough #3 below demostrates this scenario.      

2) Use a non-Web ADF component to generate and process the callback request and Web ADF JavaScript to process the callback response on the client

The diagram below illustrates how a callback request can be generated by non-Web ADF content on the client and processed by a non-Web ADF control on the server.   Since Web ADF JavaScript is still responsible for processing the callback response, the format of the response string needs to be in a format the processCallbackResult() function understands.  The format is defined by the CallbackResult class.  As a developer, this means you can use the CallbackResult and CallbackResultCollection class to construct the response string.  The easiest way to accomplish this is to either create a CallbackResultsCollection or work with a Web ADF control's callback result collection and add CallbackResult objects to it.  Every Web ADF control maintains a collection of CallbackResult objects available via the CallbackResults property.   For example, a simple HTML input button triggers a JavaScript call to generate a callback request to the server specifying that the Page process the callback.   The ICallbackEventHandler is implemented on the Page, thus RaiseCallbackEvent() and GetCallbackResult() methods are called.  In the process, the Web ADF Map control is changed and a GridView needs to be rendered on the client.   In the GetCallbackResult() method in the Page, a new CallbackResultCollection object is created and the CallbackResults from the Map control are copied to it (via the CopyFrom() method).  A custom CallbackResult object is created to update the HTML content that represents the GridView on the server.  The object is then added to the CallbackResultCollection (via the Add() method).   The callback response string is returned from the GetCallbackResult() method by converting the CallbackResultCollection to a string (via the ToString() method).   The Web ADF processCallbackResult() JavaScript function processes the callback results in the string and updates the appropriate content on the client.
     



This may be a common scenario for developers who want to leverage the Web ADF client-side callback framework with actions on non-Web ADF controls in the page.  Walkthrough #2 below demostrates this scenario.
      

3) Use non-Web ADF components to generate and process callbacks on the client and server

The diagram below illustrates the same behavior discussed in the previous scenario, except the callback response is processed by a non-Web ADF JavaScript function on the client.   The difference is the developer is responsible for handling the raw callback response string on the client, which may contain CallbackResult formatted strings for Web ADF content.   When a client script reference is generated on the server, the client-side JavaScript function to handle the callback response is defined.  For example, the following line will generate a line of JavaScript that can be called to initiate a callback request on the client at runtime:

[C#]

string m_ADFCallbackFunctionString =
  Page.ClientScript.GetCallbackEventReference(this, "message", "customFunction", "context", "handleError", true);
			

The string returned from the GetCallbackEventReference method contains the call to the JavaScript function WebForm_DoCallback() with the provided parameters.   The third parameter defines the JavaScript function to process the callback response string.  In Scenario #2 the value is "processCallbackResult" - the Web ADF JavaScript function to process callback response strings in the format defined by CallbackResults.  However, in this scenario the value is a developer-defined JavaScript function (in the example code, "customFunction").  If Web ADF CallbackResult formatted strings are returned to the custom function, the developer must parse the CallbackResult related content and forward it to the Web ADF's processCallbackResult() function.  In general, a single CallbackResult uses the delimiter ":::" to separate parameters.  Multiple CallbackResults are separated by "^^^".   See the AJAX and the ASP.NET Callback Framework topic for additional information on GetCallbackEventReference. 




This scenario is not common, but technically possible so it is presented here.  The primary use-case for implementing this scenario is a business requirement to manually process the callback response string using custom JavaScript.  If you have an existing Callback framework implementation and want to utilize Web ADF content, this scenario may provide a solution.  Essentially you will need to parse the Web ADF specific content in the response string and pass it to the Web ADF's processCallbackResult() JavaScript function.      


Working with CallbackResults and CallbackResultCollections

Web ADF controls communicate changes in their state on the server with the client browser via content in a callback response string.  The format of the callback response is determined by the format of the string the Web ADF JavaScript needs to synchronize client-server content.   On the server, the format is defined by the Web ADF CallbackResult class.  Each CallbackResult is associated with a client action.  A single callback response can contain many CallbackResults.  Interaction with a Web ADF control on the server will often generate one or more CallbackResult objects stored by the control and accessible via the CallbackResults property.  The CallbackResults property references a CallbackResultCollection.   In many cases, a Web ADF control is responsible for generating the callback response, which it will generate by converting the CallbackResultCollection to a string.  Often a developer may want to change or interact with other controls or elements in the page besides the Web ADF control generating the callback response.  This situation is illustrated in scenario #1 above.  The developer must package any custom callback messages in a CallbackResult class and add it to the Web ADF control generating the response.  Likewise, to update another Web ADF control the developer must copy its CallbackResultCollection into the collection of the Web ADF control responsible for generating the callback response.    The code and diagrams below provide an example where the ADF Map control is generating a callback response, but the Toc control needs to be refreshed based on other changes in the application.  The Toc's CallbackResultCollection is copied to the Maps CallbackResultCollection via the CopyFrom() method.   When the callback code executes, the callback process illustrates the process by which callback results are managed.  The diagram showing the actual callback string being combined and returned in the callback response provides a brief view of the underlying format of a CallbackResult string and collection. 

Callback example code

[C#]

Map1.Zoom(2);
Toc1.Refresh();
Map1.CallbackResults.CopyFrom(Toc1.CallbackResults);


Callback process



Actual callback string content




Where would this code be used?  Any situation where a Web ADF Map control is responsible for generating the callback response string.  For example, handling an event on the Map control or in a custom tool implementation.  The same pattern would apply for other situations where another Web ADF control is responsible for generating the callback response.  For example, the Toolbar generates the callback response when clicking on a command toolbar item.  To change and update the Map control, copy its callback results to the Toolbar.   

The CallbackResultCollection class provides a convenient means for managing many CallbackResult objects.  While each Web ADF control maintains a CallbackResultCollection (accessible via the CallbackResults property) you can also create a CallbackResultCollection for your own use.  The following table lists a number of methods on a CallbackResultCollection object you may find beneficial:
 
CallbackResultCollection Method Name Description
Add Used to add individual CallbackResult objects to a collection.  Often useful when creating custom CallbackResult objects and adding them to the existing callback collection for a Web ADF control. 
CopyFrom Used to copy an entire CallbackResultCollection to another collection.  Often useful when you need to update a Web ADF control that is not responsible for generating the callback response string.  Merely copy its callback results into the Web ADF control responsible for generating the callback response. 
ToString Used to convert a CallbackResultCollection into a string with the appropriate formatting for the processCallbackResult() function to parse and utilize it's content.  Often useful in cases where the control responsible for generating the callback response is not a Web ADF control.  


So far, this section has discussed existing CallbackResults and CallbackResultCollections associated with Web ADF controls.  In many cases, you may want to work with non-Web ADF content in your page.  In this case, a custom CallbackResult object should suit your needs.  The next section focuses solely on custom CallbackResult objects and capabilities.  


Custom callback results

The CallbackResult class defines four different arguments that enable you to work with almost any html content in the page by utilizing the browser DOM (document object model) to locate and interact with elements in the page.  In addition, custom JavaScript code can be provided and called on the client as needed.   Use following constructor signature shows the input parameters for a custom CallbackResult object.  The table below presents the four event arguments provided to the CallbackResult constructor to interact with client-side content. 

CallbackResult(string controlType, string controlID, string eventArgument, object[] parameters)


Event Argument Description
 "content" Used to set the outerHTML property of an html element. The html element on the client defined by the CallbackResultís controlType and controlID is completely replaced by the html content provided as a parameter in the object array (object[]).
 "innercontent" Used to set the innerHTML property of an html element. The content inside the html element on the client defined by the CallbackResultís controlType and controlID is completely replaced by the html content provided as a parameter in the object array (object[]).
 "image" Used to set the src property of an image element. The source of the image element on the client defined by the CallbackResultís controlType and controlID is changed to the url string provided as a parameter in the object array (object[]).
 "javascript" Used to execute JavaScript on the client. The CallbackResultís controlType and controlID and set to null. The JavaScript code is provided as a parameter in the object array (object[]).

The CallbackResult object is designed to be processed by the Web ADF JavaScript function processCallbackResult() in the display_dotnetadf.js library.  The processCallbackResult() contains the following logic to process the custom CallbackResult content:

[JavaScript]
if (action=="content") {
	o = document.getElementById(actions[1]);
	if (o != null)
	{
	    o.outerHTML=actions[3];
	}
}
else if (action=="innercontent") {
	o = document.getElementById(actions[1]);
	if (o != null)
	{
	    o.innerHTML=actions[3];
	}
}
else if (action=="image")
{
	o = document.images[actions[1]];
	if (o != null)
	{
	    o.src = actions[3];
	}
	else alert (actions[1] + " was null");
}
else if (action=="javascript") {

	eval(actions[3]);
}

				
The following example illustrates how to execute a line of JavaScript code associated with a custom CallbackResult object.  A Map control named "Map1" will generate the callback response, so the custom CallbackResult will be added to Map1's callback results collection. When this code is triggered at runtime, the callback response will display a message box in the browser containing the phrase "Hello World":

[C#]
string javascriptString = "alert('Hello');";
CallbackResult cr = new CallbackResult(null, null, "javascript", javascriptString);
Map1.CallbackResults.Add(cr);
				
Many samples included with the developer help illustrate the use of custom callbacks and the CallbackResults object, including Common_Callback, and Common_SelectBufferTool


Exceptions


Walkthroughs

To interact with Web ADF controls on the client using callbacks, the Web ADF JavaScript libraries must be used.   To process the callbacks on the server, you have two options: use the existing Web ADF Toolbar framework or implement your own solution to explicitly manage callback content.  In addition, you may want to work with non-Web ADF content in your Web page.  All three situations will be discussed in walkthroughs presented below:

Walkthrough #1: Working with the Toolbar

The following steps will walk you through adding a custom toolbar item to a Toolbar control in a Web ADF application.  The custom tool will center the map on the user click.

  1. To start, create a Web application using the QuickStart Creating a Web application with the Web ADF controls as a guide.
  2. In Solution Explorer, right-click the Web project and select "Add New Item...".  In the Add New Item dialog, under the Visual Studio Installed Templates section, select the "Class" item.  Set the class file name to "CustomTool.cs" and make sure the language is "Visual C#".  Visual Studio will prompt you to create an "App_Code" folder and place the new class file inside.  Click Yes.  The CustomTool.cs file should open for you to start adding content.
  3. At the top of the CustomTool.cs file, add the following statements:

    [C#]
    using ESRI.ArcGIS.ADF.Web.UI.WebControls.Tools;
    using ESRI.ArcGIS.ADF.Web.UI.WebControls;
    
  4. Remove the CustomTool constructor and implement the IMapServerToolAction interface on the CustomTool class.  Add a ServerAction method to explicitly implement the interface.  The code for the class should appear as follows:

    [C#]
    public class CustomTool : IMapServerToolAction
        {
            public void ServerAction(ToolEventArgs args)
            {
            }
        }
    
  5. Within the ServerAction method, get a reference to the Map control from the ToolEventArgs.Control property.

    [C#]
    Map map = (Map) args.Control;
    
  6. Since the ClientAction for the tool will be set to "Point", cast the ToolEventArgs to PointEventArgs. 

    [C#]
    PointEventArgs pargs = (PointEventArgs) args;
    
  7. Call the CenterAt method on the Map object and provide the screen point.  This will change the extent of the map for the current session.  Since the content of the map does not change, the map does not need to be refreshed.  More specifically, changes in map scale or extent do not require a call to map.Refresh().  Changes to layer visibility or rendering or adding/removing content (e.g. graphics) require a call to map.Refresh(). 
          
    [C#]
    map.CenterAt(pargs.ScreenPoint);
    
  8. The server-side code for the custom tool is finished.  Now you need to add a tool item to the Toolbar control to trigger the action that executes the custom tool.  Select the Toolbar control in design view or in the Properties window, then click the ellipsis for the ToolbarItems property.  In the ToolbarItems Collection Editor dialog, add a new Tool item.  In the Toolbar Items section, select the Tool item and click the Add button.  The new Tool should appear under the Current Toolbar Items section.
  9. Select the new Tool item and click the Show Properties button.  A properties dialog for the new Tool should be displayed.   



    Note that the EnablePostBack property is set to false by default.  If false, using the tool at runtime will trigger an asynchronous callback.  If true, using the tool will trigger a synchronous postback.  Keep the EnablePostBack property set to "False".  Set the following properties:

    Property Value Description
     Text

     CenterAt Tool

    Label for the tool in the Toolbar
     ClientAction

     Point

    Client event passed to the server
     ServerActionAssembly

     App_Code

    Class libraries associated with a Web site are compiled into an assembly named App_Code
     ServerActionClass

     CustomTool

    The name of the custom class which implements IMapServerToolAction and will be executed when this tool is used in the map

  10. Run the application.  Use the "Center At" tool by activating the tool in the toolbar and clicking on the map.  The initial mouse click will trigger a callback to execute the code in the CustomTool class.  The custom tool causes the Map control to generate a callback string telling the client to retrieve the map and a new extent.   The callback response to the initial mouse click is processed by the Web ADF JavaScript libraries and triggers one or more additional callbacks to retrieve new map images.  Note that the Web ADF completely manages the callback messages for you.  When finished, the map should center on the point clicked. 

    The following diagram illustrates a Web ADF toolbar initiated callback event at runtime.  The green circles indicate the sequence of events for the initial tool action.  The orange circles track the subsequent callbacks initiated by the response to the tool action.  A short description for each event is provided below:

     

    Initial Callback (1-6):

    1. After the Web page loads, the Center At tool is activated and the user clicks on the map.  When the onClick event for the map is triggered, the MapMouseDown JavaScript function in the display_map.js resource is called.  This function handles all mouse down events in the map.  The postBack function in the display_dotnetadf.js resource consolidates the tool action details.  The properties of the callback request include the name of the control to callback to on the server (the Map control) and the data associated with the user action (the click location in screen units).  

    2. The postBack function sets the message and context variables to be sent to the server and calls the ASP.NET 2.0 defined WebForm_DoCallback function to create and send the callback request.  The JavaScript function processCallbackResult, which is designed to process a Web ADF callback response, is registered when the call is made:
    WebForm_DoCallback('Map1',argument,processCallbackResult,context,postBackError,false)
    

    A new HttpRequest object is created using the Microsoft.XMLHTTP component.  This component will manage the asynchronous call to the server. 

    3. The Web ADF Map control implements the ICallbackEventHandler interface via its parent, the abstract WebControl class.  When the callback request is received by the server, the Page initiates a limited callback version of the lifecycle.  After Page Load, the callback request is handled by the Map's RaiseCallbackEvent() method, which merely stores the current event argument string.  The bulk of the business logic for a callback in the Map control is present in the GetCallbackResult() method.  Here the event argument is parsed to determine what type of action should be performed.  Since this action involves a custom tool, the handlePostBack method calls the ServerAction method for the appropriate tool.     

    4. The ServerAction method contains the custom code to modify the Map control, depending on the user-defined action.  The Map control generates one or more callback strings or CallbackResult objects to tell the client what to do to see the changes to the Map.  The CallbackResult object contents consist of a simple string.  In this example, the string will look something like:  
    "Map:::Map1:::shiftgrid:::1:::1:::66:::24"
    

    This information will be used by the client to update a control in the browser.  The delimiter ":::" separates parameters for a client-side action on a specific control.  Multiple actions, often on different controls, are separated by "^^^".  Once the Map control generates the content of the callback response, the GetCallbackResult method passes this string back to the HttpRequest object waiting on the client. 

    5. The ASP.NET 2.0 WebResource for callbacks directs the callback response string to the registered callback response function, in this case, the Web ADF defined processCallbackResult function.  This function is designed to parse the callback response for Web ADF controls on the client and trigger subsequent actions to update client-side display.

    6. The processCallbackResult function processes the callback response string and possibly, depending on the content of the callback, calls another JavaScript function. In this example, the callback response indicates that the "shiftgrid" function on the client-side Map object will be called (in the JavaScript file display_map.js).  This marks the completion of the initial callback and the beginning of subsequent callbacks to update the client-side based on changes made in the initial callback.

    Subsequent Callbacks (A - C):

    A.  Processing changes made by an initial callback may take one or more subsequent callbacks to the server.  In this example, the "shiftgrid" function is designed to evaluate the change in map extent and determine if new map images need to be generated by the server.  It uses the other parameters in the callback response to determine which map regions (or tiles) needs to be retrieved, and if necessary, uses the ASP.NET 2.0 WebResource for callbacks directly.  Since this example may request new content for a map tile, the updateTiles JavaScript function will process the callback response.  The updateTiles function is associated with the callback when using the ASP.NET 2.0 WebResource:
    WebForm_DoCallback('Map1',argument,updateTiles,context,postBackError,true)
    

    B.  Similar to the initial callback, the Page initiates a limited callback version of the lifecycle and the Map's GetCallbackResult() method processes the callback request.  In this case, the event argument is "DrawTile" which triggers the server to create a map image for a grid (region) of the map.  The grid, or tiling scheme, is generated by the Map control and shared with the Web ADF client libraries to manage map image retrieval more effectively.  The callback response contains the map image as a bytestream or url, depending on Map control setting for blending, browser or Web-tier.  The response is processed by the updateTiles function in display_map.js. 

    C. Elements in the client browser are updated to reflect the new content from the callback response.  In this example, the callback response updates style sheet layers associated with the client-side rendering of the Map control.                

   

Walkthrough #2: Working with Web ADF controls using a custom callback

The following steps will walk you through adding an HTML button to a Web page.  The button's onClick event will trigger an asynchronous callback to the server to change map extent, retrieve the callback string from the Map control, and return it to the client to trigger subsequent callbacks to update the map.   You will add two input textboxes and a button, all client-side HTML elements.  The textboxes will receive an x and y coordinate for the map to center on.  The button will trigger the action via an asynchronous callback to the server.  The Web ADF JavaScript libraries will be used to process the callback response. 

  1. To start, create a Web application using the QuickStart Creating a Web application with the Web ADF controls as a guide or enhance the Web application created in Walkthrough #1.
  2. Create the client-side code to trigger the callback first.  Open the Default.aspx page in Design view.  In the Visual Studio toolbox, expand the HTML tab and add two "Input (Text)" controls and an "Input (Button)" control.  Optionally, you can also add labels for each textbox.  The interface should appear as follows:

     
  3. Open the Default.aspx in Source view.  Assign an id value of "TextboxX" to the first textbox, an id value of "TextboxY" to the second textbox, and an onclick value of "ZoomToPointClick();" to the button.  The HTML should look similar to the following:
    <div style="left: 50px; position: absolute; top: 442px">X:</div>
    <input type="text" id="TextBoxX" style="width: 72px; left: 71px; position: absolute;top: 441px" />
    <div style="left: 183px; position: absolute; top: 442px">Y:</div>
    <input type="text" id="TextBoxY" style="width: 72px; left: 203px; position: absolute;top: 440px" />
    <input type="button" value="Zoom To Point" onclick="ZoomToPointClient();" style="left: 318px; position: absolute; top: 438px" />
    
  4. Note the onclick event for the button references a JavaScript function, ZoomToPointClient(). Create this function near the top of the aspx page within the <head> tag or in the body of the page within a <div> tag.  In both cases, the runat parameter must be set to "server" because the script variable <%= sCallBackFunctionInvocation %> is resolved at runtime .  The function should retrieve the values from the textboxes and indicate the type of event.   The event can be any valid string.  You will use it on the server to determine how to process the request.  In the following example, the event is "zoomtopoint".  
    <script language="javascript" type="text/javascript">
     function ZoomToPointClient()
     {
        var x = document.getElementById('TextBoxX').value;
        var y = document.getElementById('TextBoxY').value;
    
        var message = 'zoomtopoint';
        message += ',' + x + ',' + y;
        var context = 'Map1';
        <%=sCallBackFunctionInvocation%>
     }
    </script>
    
  5. Now let's create the server-side code for the callback.  First, set the sCallBackFunctionInvocation variable when the page loads.  This variable is a string that represents the JavaScript method used by ASP.NET Web resources to send a callback to the server.   As part of the ASP.NET Callback Manager implementation, the GetCallbackEventReference method will generate this string depending on a few input parameters.  This method is discussed in greater detail above. In this implementation, we want the Web ADF JavaScript libraries to process the callback response, so the method's clientCallback argument is equal to the Web ADF JavaScript function that processes all initial callback responses.  That function is named processCallbackResult.

    [C#]
    public string sCallBackFunctionInvocation;
    
    protected void Page_Load(object sender, EventArgs e)
    {
        sCallBackFunctionInvocation = Page.ClientScript.GetCallbackEventReference(this,
            "message", "processCallbackResult", "context", "postBackError", true);
    }
    

    At runtime, the variable sCallBackFunctionInvocation is set to WebForm_DoCallback('__Page', message, processCallbackResult, context, postBackError, true) and rendered using a script variable in the aspx page, discussed in the previous step. 
  6. Next, we need to add code on the server to process the callback.  Since the Page will be handling the callback request, it must implement the ICallbackEventHandler interface and define two methods: RaiseCallbackEvent and GetCallbackResult.  RaiseCallbackEvent will recieve the callback event message from the client.  GetCallbackResult will send a callback response (string) back to the client after the server has finished processing the callback request. 

    RaiseCallbackEvent will parse the callback event message to determine which method to call, then pass the message contents to that method.  In this example, the callback message contains "zootopoint", the message contents will be sent to the ZoomToPointServer method.  The ZoomToPointServer method will parse the callback message to get the user-defined point.  The point will be used to contruct an envelope and the map extent will be set to this envelope.  When the map properties have changed, it will generate a callback string that the Web ADF JavaScript libraries can use to update the map display in the client.  Like all Web ADF controls, the CallbackResults store the collection of callback strings to be used by the client.  Simply calling ToString on the CallbackResults property will return all callback messages for the control as a string.  Create a member variable, global to the Page class, as a private string to store the callback response.   If the content of the Map control changes (e.g. layer visibility changed, layer added or removed) then the Map needs to be refreshed using Map.Refresh().

    [C#]
    private string mapstring;
    
    public void RaiseCallbackEvent(string eventArgs)
    {
        if (eventArgs.Contains("zoomtopoint"))
        {
            ZoomToPointServer(eventArgs);
        }
    }
    
    public void ZoomToPointServer(string ea)
    {
        char[] parser_char = { ',' };
        string[] messages = ea.Split(parser_char);
        double map_width_eight = Map1.Extent.Width/8;
        double x_center = Double.Parse(messages[1]);
        double y_center = Double.Parse(messages[2]);
    
        ESRI.ArcGIS.ADF.Web.Geometry.Envelope env = new ESRI.ArcGIS.ADF.Web.Geometry.Envelope(x_center - map_width_eight, y_center - map_width_eight, x_center + map_width_eight, y_center + map_width_eight);
        Map1.Extent = env;
    
        mapstring = Map1.CallbackResults.ToString();
    
    }
    

    When the ZoomToPointServer method completes, the mapstring variable will contain a Web ADF specific string.  For example:  
    "Map:::Map1:::changelevel:::-1:::9:::9:::5:::3:::0:::0:::444:::304:::-1"
    

    The Web ADF JavaScript libraries are designed to parse and handle this string.
  7. At the end of the callback version of the page lifecycle, the GetCallbackResult method is called to return a string to the client.  In this example, GetCallbackResult merely returns the mapstring variable set in the ZoomToPointServer method.

    [C#]
    public string GetCallbackResult()
    {
        return mapstring;
    }
    

    Recall when the GetCallbackEventReference method was used to create the client-side JavaScript call to initiate the callback.  In this example, the clientCallback argument was set to processCallbackResult, a Web ADF managed client-side JavaScript function.  As a result, when the client receives the callback response, the ASP.NET Callback Manager knows to send the response to processCallbackResult function.  The function parses the response and updates the client session.  In many cases, depending on the controls that need to be updated in the page, a series of additional callbacks are generated and processed. 
  8. Run the application.  Enter a point (a valid location for the map) and click the ZoomToPoint button.  The map should change extent and zoom to the point specified. 

    The following diagram illustrates a custom callback event that works with the Web ADF controls at runtime.  The green circles indicate the sequence of events for the initial action.  The orange circles track the subsequent callbacks initiated by the response to the action.  A short description for each event is provided below:







    Initial Callback (1-5):

    1. After the Web page loads, an x and y value for a point are entered in the appropriate text boxes.  The onClick event for the ZoomToPoint HTML button calls the ZoomToPointClient JavaScript function.   

    2. The ZoomToPointClient function gets the values of each textbox containing the user-defined point, sets the custom message and context parameters for the callback.  All are used by the ASP.NET 2.0 WebForm_DoCallback function to initiate a callback.  The line containing this function is generated during page load by a call to the Page.GetCallbackEventReference method.  The first parameter uses the page id to define that the Page object will receive the callback request.  The Page must implement the ICallbackEventHandler interface.   The message is a custom string containing the type of server action (zoom to point) and the x and y values.  The callback response will be processed by the Web ADF JavaScript function processCallbackResult, which is designed to process a Web ADF callback content.  The context may optionally be used by the client to reference the callback request.  The JavaScript function will appear as follows :
    WebForm_DoCallback('__Page',message,processCallbackResult,context,postBackError,true)

    A new HttpRequest object is created using the Microsoft.XMLHTTP component.  This component will manage the asynchronous call to the server. 

    3. The Page implements the ICallbackEventHandler interface.  When the callback request is received by the server, the Page initiates a limited callback version of the lifecycle.  After Page Load, the callback request is handled by the Page's RaiseCallbackEvent() method, which checks the message for an action.  If it finds "zoomtopoint", the ZoomToPointServer method is called.  The ZoomToPointServer method parses the message to find the x and y coordinates, uses them to define a new extent, then changes the extent on the Map control.  When the map extent changes, the Map control generates a string that can be used by Web ADF JavaScript to update the map in the browser.  To get this string, use the CallbackResults property on the Map and convert it to a string using ToString().  In this example, the string will look something like:  
    "Map:::Map1:::changelevel:::1:::1:::66:::24"
    

    This information will be used by the client browser to update the Map control in the browser.  The delimiter ":::" separates parameters for a client-side action on a specific control.  Multiple actions, often on different controls, are separated by "^^^".  Return this string via the return value from GetCallbackResult() method.   

    4. The ASP.NET 2.0 WebResource for callbacks directs the callback response string to the registered callback response function, in this case, the Web ADF defined processCallbackResult function.  This function is designed to parse the callback response for Web ADF controls on the client and trigger subsequent actions to update client-side display.

    5. The processCallbackResult function processes the callback response string and possibly, depending on the content of the callback, calls another JavaScript function. In this example, the callback response indicates that the "setUpLevelGrid" function on the client-side Map object will be called (in the JavaScript file display_map.js).  This marks the completion of the initial callback and the beginning of subsequent callbacks to update the client-side based on changes made in the initial callback.

    Subsequent Callbacks (A - C):

    A.  Processing changes made by an initial callback may take one or more subsequent callbacks to the server.  In this example, the "setUpLevelGrid" function is designed to evaluate the change in map extent and determine if new map images need to be generated by the server.  It uses the other parameters in the callback response to determine which map regions (or tiles) needs to be retrieved, and if necessary, uses the ASP.NET 2.0 WebResource for callbacks directly.  Since this example may request new content for a map tile, the updateTiles JavaScript function will process the callback response.  The updateTiles function is associated with the callback when using the ASP.NET 2.0 WebResource:
    WebForm_DoCallback('Map1',argument,updateTiles,context,postBackError,true)
    

    B.  Similar to the initial callback, the Page initiates a limited callback version of the lifecycle and the Map's GetCallbackResult() method processes the callback request.  In this case, the event argument is "DrawTile" which triggers the server to create a map image for a grid (region) of the map.  The grid, or tiling scheme, is generated by the Map control and shared with the Web ADF client libraries to manage map image retrieval more effectively.  The callback response contains the map image as a bytestream or url, depending on the Map control setting for blending, browser or Web-tier.  The response is processed by the updateTiles function in display_map.js. 

    C. Elements in the client browser are updated to reflect the new content from the callback response.  In this example, the callback response updates style sheet layers associated with the client-side rendering of the Map control.   

 

Walkthrough #3: Using the Web ADF callback framework to interact with non-Web ADF elements

The following steps will walk you through extending Walkthrough #1 or #2 to update text in four <div> tags in the same Web page as the Map control.  Each tag will represent the north, south, east, and west extent of the map.  Note that the div tags only reside on the client, in the browser.  When the map extent changes, the text within each div will be updated to represent the current extent parameters.   A custom CallbackResult object for each div tag will be created and appended to the callback result for the Map control.  The Web ADF JavaScript function processCallbackResult() contains logic to process non-Web ADF content depending on the parameters provided.  The parameters are listed and discussed in the custom callback events  section above.  The same function flow will occur as specified in the previous walkthroughs, we will merely append additional content on the initial callback response to update non-Web ADF elements in the Web page.

  1. Start with by completing walkthrough #1 or #2, then add the content in this walkthrough.
  2. In the aspx page, add four div tags, one on each side of the Map control.  Note, these are simple HTML elements. 
            <div id="LabelW" style="left: 13px; position: absolute; top: 259px; width: 50px;"></div>
            <div id="LabelS" style="left: 262px; position: absolute; top: 436px; width: 50px;"></div>
            <div id="LabelE" style="left: 533px; position: absolute; top: 259px; width: 50px;"></div>
            <div id="LabelN" style="left: 262px; position: absolute; top: 87px; width: 50px;"></div>
    					

    The Web page should appear as follows.  Note that no additional JavaScript code is required by the browser:

  3. Add a new event handler for an ExtentChanged event on the Map control.  In this example, the name of the method is Map1_ExtentChanged.

    [C#]
    protected void Map1_ExtentChanged(object sender, ExtentEventArgs extentEventArgs)
    {
    				
  4. Get the new map extent via the ExtentEventArgs parameter and create a sorted list to store the north, east, south, and west values.

    [C#]
      ESRI.ArcGIS.ADF.Web.Geometry.Envelope adfEnvelope = extentEventArgs.NewExtent;
    
      SortedList<string, string> extentList = new SortedList<string, string>();
      extentList.Add("n", adfEnvelope.YMax.ToString("N"));
      extentList.Add("e", adfEnvelope.XMax.ToString("N"));
      extentList.Add("s", adfEnvelope.YMin.ToString("N"));
      extentList.Add("w", adfEnvelope.XMin.ToString("N"));
    					
  5. Iterate through the sorted list to construct a new CallbackResult object for each client-side element that needs to be changed.  In this case, each HTML element is a type of "div" and has a unique id.  We are going to change the HTML content (or inner content) within the div tag.  Four parameters will be used to construct the CallbackResult.   The first parameter is a string representing the type of html element, the second represents the unique id of the element.  The third parameter is one of four actions: "content", "innercontent", "image", and "javascript".   The fourth and final parameter is the value or content on which a client-side action will take place.  Its use is determined by the action.  In this case, we will change the content within the div tag, so we choose "innercontent".   The final parameter is the string value that will be used to change the div tag content.

    [C#]
      foreach (KeyValuePair<string, string> key in extentList)
      {
        string value = key.Value.ToString();
        CallbackResult callbackResult = new CallbackResult("div",
        "Label" + key.Key.ToString(), "innercontent", value);
    
        		
  6. Once the CallbackResult objects are created, they need to be added to the control responsible for sending a callback response string to the client browser.  In this case, the string returned from the Map controls GetCallbackResult() method will be returned to the client.   As a result, we can add each CallbackResult object to the Map.CallbackResults collection.  Since layer visibility and rendering did not change, the Map control does not need to be explicitly refreshed.

    [C#]
        Map1.CallbackResults.Add(callbackResult);
      }
    }
    					

    The callback result string will look something like:

    "Map:::Map1:::changelevel:::-1:::36:::37:::10:::12:::0:::0:::444:::304:::-1:::0:::0^^^
    div:::Labele:::innercontent:::-70.74^^^div:::Labeln:::innercontent:::43.78^^^
    div:::Labels:::innercontent:::36.94^^^div:::Labelw:::innercontent:::-80.74"
    					
  7. At runtime, the callback string generated by the custom CallbackResults will enable the modification of non-Web ADF controls and elements.  Upon the initial map extent change, the div tag content around the map will be updated dynamically in the browser, without a full page postback.  The image below shows a runtime example of the Web ADF application built in this scenario.