Developing Web Applications with the Web ADF - Tutorials  

Using the Common Data Source API in a Web application



Through the Common Data Source API, the Web ADF provides endpoints for retrieving and manipulating data that can be used against any underlying data-source type. The classes of the Common API enable developers to build tools and applications that rely on resources and functionalities of varying data-source types without having to write data-source specific implementations. The following walkthrough will show how to query resources and create graphics showing query results using only the Common API.

This tutorial will provide step-by-step instructions for creating a Web ADF application in Visual Studio 2008 that contains a custom selection tool. This tool will use classes from the Common API to retrieve feature data from a user-specified resource and layer and spatially display this data as a Web ADF GraphicsLayer. Attributes for these features will be displayed in a Web ADF TaskResults control and in callouts (i.e. MapTips) that appear when the mouse passes over a feature on the map. The tutorial also demonstrates the use of ASP.NET AJAX functionality to dynamically update web controls via partial postback. The following Web ADF controls are used in the tutorial: MapResourceManager, Map, Toc, Toolbar, and TaskResults.  

Designing the Web application

Our web application will consist of a subset of Web ADF controls that provide basic GIS functionality and several other controls (Web ADF and non-Web ADF) that will be used in conjunction with our custom selection tool implementation. The basic controls include a Map for display of GIS data, a Toc to allow toggling of map layer visibility, a Toolbar to enable interaction with the map, and a MapResourceManager to provide the Web ADF controls access to map services. We will also add a Web ADF TaskResults control to display the attributes of features selected with our custom tool, and two ASP.NET DropDownLists to enable run time specification of the resource and layer to select features from. We will embed the DropDownLists in an ASP.NET AJAX UpdatePanel to allow asynchronous updating of these controls via partial postback, and we will include an ASP.NET AJAX ScriptManager to support asynchronous functionality.
  1. Create a new web application using the steps in the Creating a Web application with the Web controls tutorial. While that tutorial uses Visual Studio 2005, all the steps except for absolutely positioning the Toc (step eleven) are the same in Visual Studio 2008. In Visual Studio 2008, perform this step as follows:

    a. In design view, click the Toc control so that it is highlighted.
    b. From the Format menu, select “Position…”
    c. On the dialog that appears (figure to the right), in the Positioning Style section, select Absolute, then click OK. 
  2. In design view, drag a ScriptManager control from the AJAX Extensions section of the Visual Studio toolbox and drop it in the top left corner of the Default.aspx page, above the Map and MapResourceManager controls. For ASP.NET AJAX functionality to work properly, the ScriptManager must be located on the page before any controls that employ AJAX functionality.
  3. Drag and drop a Web ADF TaskResults control onto the page. We will use this control to display the attributes of selected features.
  4. Position the TaskResults control below the Toc. First, make the control absolutely positioned. Do this by using the Visual Studio Position dialog as described in step one. Then, drag the control until it is positioned below the Toc. Expand the width of the control so it is at least 300 pixels wide.
  5. Set the TaskResults control's BuddyControl property. Select the TaskResults control in design view or in the Properties window, then click the drop-down list for the BuddyControl property. The list will include the names of Map controls available on the page. Select the name of the Map control added previously ("Map1" by default).
  6. From the AJAX Extensions section of the Visual Studio toolbox, drag an UpdatePanel onto the page and drop it below the toolbar. We will use this to enable AJAX functionality for the two drop-down lists that will specify the resource and layer to select features from.
  7. Drag a DropDownList control from the Standard section of the Visual Studio toolbox and drop it in the UpdatePanel. On the menu that appears, check the Enable AutoPostBack item. This will make it so that the DropDownList issues a postback when a new item is selected at run time. And because we are placing the control within an UpdatePanel, the postback will be an asynchronous partial postback, rather than a full page postback. This DropDownList will be used to specify the map resource to select features from.
  8. Drag and drop another DropDownList into the UpdatePanel. Enable AutoPostBack for this list as well. We will use this control to specify the layer to select features from. In design view, the application should appear as shown in the figure below.



Implementing Page and Control Event Handlers

In configuring our application's user interface, we added two DropDownList controls to allow users to specify the resource and layer to select features from, and a TaskResults control to display the attributes of selected features. The DropDownLists won't contain any items out-of-the-box, so we'll have to explicitly initialize them with the names of the application's resources and layers. We also want the layers list to dynamically update any time a new resource is selected, which will require some code as well. As for the TaskResults control, since we want to use it to display our custom tool's results, we need to somehow give our custom tool access to it. We can implement logic to handle all of these requirements in the Default page's code-behind class. 

  1. In Solution Explorer, expand Default.aspx and double click on Default.aspx.cs (if you are using C#) or Default.aspx.vb (if you are using Visual Basic) to open Default's code-behind class.
  2. We want to add the results from our custom selection tool to the application's TaskResults control. To be able to do this, we will have to get a reference to the TaskResults control from within the custom tool's implementation. While there are a number of ways this can be done, we will use session storage in this case for simplicity. Store a reference to the control in session by adding the following code to the handler for the page's Load event. We place the code here so that when the custom tool is used at run time (which causes the load event to fire), the reference to the control is updated before execution passes to the custom tool implementation.

    [C#]
    // Store the TaskResults control in session for use by the custom tool
    Session["TaskResultsControl"] = TaskResults1;
    

    [VB]
    ' Store the TaskResults control in session for use by the custom tool
    Session("TaskResultsControl") = TaskResults1
    
  3. Add a handler for the page's PreRender event. We will add code to this handler to initialize the resource and layer selection drop-down lists. After the handler is added, the class should appear as follows:

    [C#]
    public partial class _Default : System.Web.UI.Page 
    {
       	protected void Page_Load(object sender, EventArgs eventArgs)
    	{
    		// Store the TaskResults control in session for use by the custom tool
    		Session["TaskResultsControl"] = TaskResults1;
    	}
    
    	protected void Page_PreRender(object sender, EventArgs eventArgs){}
    }
    

    [VB]
    Partial Class _Default Inherits System.Web.UI.Page
    
    	Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
    		' Store the TaskResults control in session for use by the custom tool
    		Session("TaskResultsControl") = TaskResults1
        	End Sub
    
        	Protected Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.PreRender
        	End Sub
    End Class
    
  4. Now we will add code to initialize DropDownList1, which will contain the names of the application's map resources. We implement the initialization in the page's PreRender event because, when this event fires, the page's controls have been loaded but have not yet been drawn. To initialize the drop-down list, we add to it the name of each resource referred to by the application's MapResourceManager. But before doing this, we first check whether the list already contains items, since we only want this code to execute on application startup.

    [C#]
    if (DropDownList1.Items.Count == 0)
    {
    	// Add resource names to the resource drop-down list
    	foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem
    	mapResourceItem in MapResourceManager1.ResourceItems)
    		DropDownList1.Items.Add(mapResourceItem.Resource.Name);
    }
    

    [VB]
    If DropDownList1.Items.Count = 0 Then 
    	' Add resource names to the resource drop-down list 
    	For Each mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem _
    	In MapResourceManager1.ResourceItems 
    		DropDownList1.Items.Add(mapResourceItem.Resource.Name)
    	Next 
    End If
  5. We want to initialize DropDownList2 with the names of queryable layers in the resource currently specified by DropDownList1. The Web ADF QueryFunctionality object's GetQueryableLayers function can be used to get an array containing these names. This requires creating an instance of QueryFunctionality, which in turn requires referencing a resource via the GISResource class. Note that all of these classes are part of the Web ADF Common API, and can therefore be used against any valid data source type.

    Since we will need to get these layer names any time a new resource is selected from DropDownList1 (not just during application initialization), we will implement this logic in a separate function. Insert the following code outside the PreRender event, but inside the page class:

    [C#]
    private string[] GetQueryableLayerNames(string resourceName)
    {
            // Get a reference to the resource
            ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
                Map1.GetFunctionality(resourceName);
            ESRI.ArcGIS.ADF.Web.DataSources.IGISResource gisResource = commonMapFunctionality.Resource;
    
            // Create a query functionality to use in querying the resource
            ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality commonQueryFunctionality =
                (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)
                gisResource.CreateFunctionality(
                typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), null);
    
            // Get the resource's queryable layers
            string[] layerIDs = null;
            string[] layerNames = null;
            commonQueryFunctionality.GetQueryableLayers(null, out layerIDs, out layerNames);
    
            return layerNames;
    }
    

    [VB]
    
    						
    Private Function GetQueryableLayerNames(ByVal resourceName As String) As String()
    	' Get a reference to the resource
    	Dim commonMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = _
    	Map1.GetFunctionality(resourceName)
    	Dim gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = _
    	commonMapFunctionality.Resource
    
    	' Create a query functionality to use in querying the resource
    	Dim commonQueryFunctionality As _
    	ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = _
    		    CType(gisResource.CreateFunctionality(GetType( _
    		    ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), Nothing),  _
    		    ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)
    
    	' Get the resource's queryable layers
    	Dim layerIDs() As String = Nothing
    	Dim layerNames() As String = Nothing
    	commonQueryFunctionality.GetQueryableLayers(Nothing, layerIDs, layerNames)
    
    	Return layerNames
    End Function
    
  6. Now we can add a call to GetQueryableLayerNames to the PreRender event handler and use the resulting array to initialize DropDownList2. To do this, insert the following code in PreRender, below the DropDownList1 initialization code and inside the if block added two steps prior.

    [C#]
    // Get the names of queryable layers for the selected resource and add them
    // to the layers drop-down
    string[] layerNames = GetQueryableLayerNames(DropDownList1.SelectedItem.Text);
    for (int i = 0; i < layerNames.Length; i++)
    	DropDownList2.Items.Add(layerNames[i]);
    

    [VB]
    ' Get the names of queryable layers for the selected resource and add them
    ' to the layers drop-down
    Dim layerNames() As String = GetQueryableLayerNames(DropDownList1.SelectedItem.Text)
    For i As Integer = 0 To layerNames.Length - 1
    	DropDownList2.Items.Add(layerNames(i))
    Next
    
  7. To complete our implementation of the Default page class, we need to add code to update the drop-down list of queryable layers whenever a new resource is selected. We can accomplish this by implementing a handler for DropDownList1's SelectedIndexChanged event that clears the items from the layers drop-down, gets the queryable layer names based on the item currently selected in the resources drop-down, and add these layer names to the layers drop-down. Place this event handler, shown below, outside the PreRender event but inside the page class. If you are working in C#, you will also need to add a statement to the handler for the page's Load event that explicitly adds the SelectedIndexChanged handler. In Visual Basic, this step is unnecessary because the event being handled is explicitly specified in the handler definition.

    [C#]

    Insert into Page_Load:
    // Add a handler for the resource drop-down's SelectedIndexChanged event
    DropDownList1.SelectedIndexChanged += new 
    EventHandler(DropDownList1_SelectedIndexChanged);
    

    Insert into the page class outside of PreRender:
    void DropDownList1_SelectedIndexChanged(object sender, EventArgs eventArgs)
    {
    // Clear the layers drop-down
    DropDownList2.Items.Clear();
    
    // Get the names of queryable layers for the selected resource and add them
    // to the layers drop-down
    string[] layerNames = GetQueryableLayerNames(DropDownList1.SelectedItem.Text);
    for (int i = 0; i < layerNames.Length; i++)
    	DropDownList2.Items.Add(layerNames[i]);
    }
    

    [VB]
    Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
    
    ' Clear the layers drop-down
    DropDownList2.Items.Clear()
    
    ' Get the names of queryable layers for the selected resource and add them
    ' to the layers drop-down
    Dim layerNames() As String = _ 
    GetQueryableLayerNames(DropDownList1.SelectedItem.Text)
    For i As Integer = 0 To layerNames.Length - 1
    	DropDownList2.Items.Add(layerNames(i))
    Next
    End Sub
    


Implementing the custom selection tool

Now that we've setup our application's user interface and implemented a way for users to specify the selection resource and layer, we are ready to implement our custom selection tool. This tool will use the rectangle drawn on the map by the user of the tool to query the layer specified by the resource and layer drop-downs, configure these results as a graphics layer, and then add them to the TaskResults control (which will automatically add them to the Map). All this is implemented using the Web ADF Common API – none of the data-source specific libraries are used.

  1. 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. If you are using Visual C#, set the class file name to "SelectTool.cs" and make sure the selected language is Visual C#. If you are using Visual Basic, set the class file name to "SelectTool.vb" and make sure the selected language is Visual Basic. Click the Add button. Visual Studio will prompt you to create an "App_Code" folder and place the new class file inside. At this prompt, click Yes. The SelectTool.cs file should open for you to start adding content. This file will contain the code that executes when the custom selection tool is used.
  2. Remove the SelectTool constructor and implement the IMapServerToolAction interface on the SelectTool class. The code for the class should appear as follows:

    [C#]
    public class SelectTool : 
    ESRI.ArcGIS.ADF.Web.UI.WebControls.Tools.IMapServerToolAction
    {
        	void ESRI.ArcGIS.ADF.Web.UI.WebControls.Tools.IMapServerToolAction.ServerAction(
    ESRI.ArcGIS.ADF.Web.UI.WebControls.ToolEventArgs toolEventArgs)
        	{
        	}
    }
    
    

    [VB]
    Public Class SelectTool
        Implements ESRI.ArcGIS.ADF.Web.UI.WebControls.Tools.IMapServerToolAction
    
    	Public Sub ServerAction(ByVal toolEventArgs As ESRI.ArcGIS.ADF.Web.UI.WebControls.ToolEventArgs) Implements _ 
    		ESRI.ArcGIS.ADF.Web.UI.WebControls.Tools.IMapServerToolAction.ServerAction
    	End Sub
    End Class
    
  3. To start our custom tool implementation, we need to get a reference to the Map control on which the tool was executed. The control that is the target of a ServerAction is stored in the Control property of the arguments that are passed to the ServerAction method, so we can get this reference from the following code. This and the remainder of the code in the tutorial should be placed inside the ServerAction method.

    [C#]
    ESRI.ArcGIS.ADF.Web.UI.WebControls.Map adfMap =
    	(ESRI.ArcGIS.ADF.Web.UI.WebControls.Map)toolEventArgs.Control;
    

    [VB]
    Dim adfMap As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map = _
    	CType(toolEventArgs.Control, ESRI.ArcGIS.ADF.Web.UI.WebControls.Map)
    
  4. Next we need the geometry of the rectangle drawn by the user. To get this, we cast the ToolEventArgs instance passed into the ServerAction method to a MapRectangleEventArgs object. Then we can get the reference simply by accessing this object's MapExtent property. Note that the type of geometry referenced by the ToolEventArgs object is determined by the ClientAction specified when the custom tool is added to the web page. Later in the tutorial, we will specify a ClientAction of "DragRectangle" for our custom tool, which will result in the ToolEventArgs referencing an envelope.  
     
    [C#]
    ESRI.ArcGIS.ADF.Web.UI.WebControls.MapRectangleEventArgs mapRectangleEventArgs =
    	(ESRI.ArcGIS.ADF.Web.UI.WebControls.MapRectangleEventArgs)toolEventArgs;
    ESRI.ArcGIS.ADF.Web.Geometry.Envelope adfEnvelope = 
    	mapRectangleEventArgs.MapExtent;

    [VB]
    Dim mapRectangleEventArgs As _
    	ESRI.ArcGIS.ADF.Web.UI.WebControls.MapRectangleEventArgs = CType(toolEventArgs, _ 
    	ESRI.ArcGIS.ADF.Web.UI.WebControls.MapRectangleEventArgs)
    Dim adfEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = _
    	mapRectangleEventArgs.MapExtent
  5. To select features from a resource, we need to execute a query on the resource to get a reference to the set of features to be selected. In the Web ADF, query functionality for a resource is exposed through a QueryFunctionality object. To get a reference to a resource's query functionality, we need to first get a reference to the resource via a GISResource instance.  The GISResource reference can be retrieved from the resource's MapFunctionality, which, given a Map control and the name of the resource, can easily be retrieved via the Map's GetFunctionality function.

    Since, in this case, we already have a reference to the Map control, creating a QueryFunctionality instance depends on retrieving the name of the resource to select features from. We know the name of this resource is specified by the resource drop-down, which is an ASP.NET server control with the ID “DropDownList1.” The value of a server control is always included on postbacks in the page's NameValueCollection of request parameters with the control ID as the key. For server controls that postback synchronously (i.e. via full page postback), the value in the request parameters is only updated on synchronous postbacks. But because we've included a ScriptManager on the page and placed the drop-down in an UpdatePanel, the drop-down list posts back asynchronously. The value of an asynchronous server control is updated in the page's request parameters on both synchronous and asynchronous postbacks. Since a custom tool implementing IMapServerToolAction actually triggers an asynchronous postback (or callback if the callback framework is being used, but that doesn't apply here), we can retrieve the value of the drop-down from the page's request parameters. The code below illustrates this.  
       
    [C#]
    // Get the name of the resource on which to perform the selection
    string resourceName = (string)adfMap.Page.Request.Params["DropDownList1"];
    
    // Get a reference to the resource
    ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
    	adfMap.GetFunctionality(resourceName);
    ESRI.ArcGIS.ADF.Web.DataSources.IGISResource gisResource = 
    	commonMapFunctionality.Resource;
    
    // Create a query functionality to use in querying the resource
    ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality commonQueryFunctionality =
    	(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)
    	gisResource.CreateFunctionality(
    	typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), null);

    [VB]
    ' Get the name of the resource on which to perform the selection
    Dim resourceName As String = adfMap.Page.Request.Params("DropDownList1")
    
    ' Get a reference to the resource
    Dim commonMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = _
    	adfMap.GetFunctionality(resourceName)
    Dim gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = _
    	commonMapFunctionality.Resource
    
    ' Create a query functionality to use in querying the resource
    Dim commonQueryFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = _
    	CType(gisResource.CreateFunctionality(GetType( _
    	ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), Nothing),  _
    	ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)
  6. The QueryFunctionality object's Query function requires the ID of the layer to be queried as a parameter. We can get arrays containing the names and IDs of a resource's queryable layers from that resource's QueryFunctionality. These arrays are constructed such that the items at the same index of the two arrays correspond to the same layer (e.g. the name at index 0 of the names array and the ID at index 0 of the IDs array are the name and ID of the same layer). So, given the name of a layer, we can determine that layer's ID by (1) determining the index at which that layer name is stored in the names array and (2) retrieving the ID at the same index from the IDs array. This can be done as shown in the code below. Note that we retrieve the name of the layer to query from the page's request parameters in the same way we retrieved the resource name.

    [C#]
    // Get the resource's queryable layers
    string[] layerIDs = null;
    string[] layerNames = null;
    commonQueryFunctionality.GetQueryableLayers(null, out layerIDs, out layerNames);
    
    // Get the name of the selection layer from the page's request parameters
    string selectionLayerName = (string)adfMap.Page.Request.Params["DropDownList2"];
    string selectionLayerID = null;
    
    // Get the ID of the selection layer from the layer IDs array
    for (int i = 0; i < layerNames.Length; i++)
    {
    	if (layerNames[i] == selectionLayerName)
    	{
    		selectionLayerID = layerIDs[i];
    		break;
    	}
    }
    

    [VB]
    ' Get the resource's queryable layers
    Dim layerIDs() As String = Nothing
    Dim layerNames() As String = Nothing
    commonQueryFunctionality.GetQueryableLayers(Nothing, layerIDs, layerNames)
    
    ' Get the name of the selection layer from the page's request parameters
    Dim selectionLayerName As String = adfMap.Page.Request.Params("DropDownList2")
    Dim selectionLayerID As String = Nothing
    
    ' Get the ID of the selection layer from the layer IDs array
    For i As Integer = 0 To layerNames.Length - 1
    	If layerNames(i) = selectionLayerName Then
    		selectionLayerID = layerIDs(i)
    		Exit For
    	End If
    Next
    
  7. The Query function also requires a Web ADF QueryFilter as a parameter. If, as in this case, the query has a spatial component, then a SpatialFilter object, which extends QueryFilter to allow specification of query geometry and other spatial properties, should be used. Here we add code to initialize a SpatialFilter with the user-defined envelope.
     
    [C#]
    // Set-up a spatial filter to use in querying the resource
    ESRI.ArcGIS.ADF.Web.SpatialFilter adfSpatialFilter =
    new ESRI.ArcGIS.ADF.Web.SpatialFilter();
    adfSpatialFilter.ReturnADFGeometries = true;
    adfSpatialFilter.MaxRecords = 100;
    adfSpatialFilter.Geometry = adfEnvelope;

    [VB]
    ' Set-up a spatial filter to use in querying the resource
    Dim adfSpatialFilter As ESRI.ArcGIS.ADF.Web.SpatialFilter = _
    New ESRI.ArcGIS.ADF.Web.SpatialFilter()
    adfSpatialFilter.ReturnADFGeometries = True
    adfSpatialFilter.MaxRecords = 100
    adfSpatialFilter.Geometry = adfEnvelope
  8. Now that we have the ID of the selection layer and a SpatialFilter that references the user-defined selection rectangle, we can execute the query.
     
    [C#]
    // Query the selection layer with the user-drawn rectangle
    System.Data.DataTable resultsDataTable = commonQueryFunctionality.Query(
    commonMapFunctionality.Name, selectionLayerID, adfSpatialFilter);

    [VB]
    ' Query the selection layer with the user-drawn rectangle
    Dim resultsDataTable As System.Data.DataTable = commonQueryFunctionality.Query( _
    commonMapFunctionality.Name, selectionLayerID, adfSpatialFilter)
  9. The Query function returns results contained in a DataTable object. While this class provides easy access to the results data, it is not well suited to tailoring the appearance of results. For this reason, we add code to convert the results from a DataTable to a Web ADF GraphicsLayer.  
     
    [C#]
    // Convert the results data table to a graphics layer
    ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer resultsGraphicsLayer =
    ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(resultsDataTable);

    [VB]
    ' Convert the results data table to a graphics layer
    Dim resultsGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = _
    ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(resultsDataTable)
  10. Now that we have the query results as a Web ADF GraphicsLayer, we will configure this GraphicsLayer for display in a TaskResults control. While we could add the GraphicsLayer to the map independently of a TaskResults control, displaying the GraphicsLayer in this way takes full advantage of the Web ADF's attribute display and client-side rendering capabilities. First, add code to set the selected field of each result feature to true. We do this so that the task result node for each feature is checked, making the corresponding feature on the map visible.

    [C#]
    // Select each result so that its node is checked in the TaskResults control
    foreach (System.Data.DataRow dataRow in resultsGraphicsLayer.Rows)
    	dataRow[resultsGraphicsLayer.IsSelectedColumn] = true;

    [VB]
    ' Select each result so that its node is checked in the TaskResults control
    For Each dataRow As System.Data.DataRow In resultsGraphicsLayer.Rows
      	dataRow(resultsGraphicsLayer.IsSelectedColumn) = True
    Next
       
  11. Next, we retrieve the selection layer's LayerFormat and apply it to the results GraphicsLayer. The LayerFormat object specifies a GraphicsLayer's renderers and the format and contents of its attribute displays (i.e. callouts and task results nodes). For feature layers, the LayerFormat applies to results GraphicsLayers and can be specified at design time in either Visual Studio or ArcGIS Server Manager via the Layer Properties dialog (see Using the MapResourceManager control and Selecting layers to display). LayerFormats can also be modified at run time programmatically (see Working with LayerFormats). In this case, we will simply apply the selection layer's LayerFormat to our results GraphicsLayer as-is, causing the GraphicsLayer to appear according to the Layer Properties specified at design-time.

    [C#]
    // Retrieve the selection layer's layerFormat and apply it to the results
    ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat layerFormat =
    ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(
    adfMap.MapResourceManagerInstance, resourceName, selectionLayerID);
    layerFormat.Apply(resultsGraphicsLayer);

    [VB]
    ' Retrieve the selection layer's layerFormat and apply it to the results
    Dim layerFormat As ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat = _
    ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager( _
    adfMap.MapResourceManagerInstance, resourceName, selectionLayerID)
    layerFormat.Apply(resultsGraphicsLayer)
  12. To enable functionality that relies on client-side events (e.g. mouseOver, mouseOut, etc.), we specify that the graphics layer be rendered on the client. This will turn on effects such as showing callouts when a result feature is moused over and displaying a feature's highlight renderer when the corresponding node is moused over in the TaskResults control.

    [C#]
    // Render the results on the client to enable callouts and highlighting
    resultsGraphicsLayer.RenderOnClient = true;

    [VB]
    ' Render the results on the client to enable callouts and highlighting
    resultsGraphicsLayer.RenderOnClient = True
  13. The final step in readying the GraphicsLayer for display via the TaskResults control is placing it in a DataSet. The TaskResults control is designed to display results contained in a DataSet, where the root node represents the DataSet level, child nodes are created for each DataTable (i.e. GraphicsLayer) within the DataSet, and child nodes are created on each DataTable node for each row (i.e. feature) in each DataTable. The DataSet's name determines the text rendered on the DataSet nodes, DataTable names determine the text rendered on DataTable nodes, and the GraphicsLayer's LayerFormat specifies the appearance of feature nodes. Here we give the DataSet a name that includes the GraphicLayer's TableName, which is inherited from the name of the map layer on which the query was executed.

    [C#]
    // Create a DataSet and add the results to it
    string resultsDataSetName = string.Format("Selected Features - {0}", 
    resultsGraphicsLayer.TableName);
    System.Data.DataSet resultsDataSet = new System.Data.DataSet(resultsDataSetName);
    resultsDataSet.Tables.Add(resultsGraphicsLayer);

    [VB]
    ' Create a DataSet and add the results to it
    Dim resultsDataSetName As String = String.Format("Selected Features - {0}", _
    resultsGraphicsLayer.TableName)
    Dim resultsDataSet As System.Data.DataSet = New _
    System.Data.DataSet(resultsDataSetName)
    resultsDataSet.Tables.Add(resultsGraphicsLayer)
  14. Now we are ready to add the results to the TaskResults control. This is done by passing the results DataSet to the TaskResults' DisplayResults method. Here the TaskResults control is retrieved from session, where it was stored in the Load event of the Default.aspx page.

    [C#]
    // Retrieve the TaskResults control from session and add the results to it
    ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults taskResults =             
    (ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults)
    adfMap.Page.Session["TaskResultsControl"];
    taskResults.DisplayResults(null, null, null, resultsDataSet);

    [VB]
    ' Retrieve the TaskResults control from session and add the results to it
    Dim taskResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults = _
    adfMap.Page.Session("TaskResultsControl")
    taskResults.DisplayResults(Nothing, Nothing, Nothing, resultsDataSet)
  15. All that remains to complete our custom selection tool implementation is to return the necessary callback results. When a MapServerToolAction is executed, a callback (or partial postback) is initiated by the Map control, so that control's callback results will automatically be processed on the client. In this implementation, we have modified the TaskResults control, causing this control to have callback results. So in order for the TaskResults control's callback results to be processed on the client – and the TaskResults control and Map to be updated accordingly – we add code to copy the TaskResults' callback results to the Map.

    [C#]
    // Copy the TaskResults' callback results to the Map so the results show up
    adfMap.CallbackResults.CopyFrom(taskResults.CallbackResults);

    [VB]
    ' Copy the TaskResults' callback results to the Map so the results show up
    adfMap.CallbackResults.CopyFrom(taskResults.CallbackResults)
Adding the custom tool to the application

Now that the implementation of our custom selection tool is complete, we need to add it tool to the application's Toolbar. To do this, we need to add a Tool item to the Toolbar's collection, wire the Tool to our custom tool class, specify the appropriate ClientAction, and define other properties as desired.

  1. Open Default.aspx in design view and select the Toolbar. In the Properties window, click the ellipsis for the ToolbarItems property. In the ToolbarItems Collection Editor dialog, add a new custom tool item by selecting “Tool” from the left-hand side of the dialog and clicking the Add button. The new Tool will appear under the Current Toolbar Contents section of the dialog.
  2. Select the new Tool item in the Current Toolbar Contents section of the dialog and examine its properties. Note that the EnablePostBack property is set to false by default. If false, using the tool at runtime will trigger an asynchronous callback or partial postback. If true, using the tool will trigger a synchronous (i.e full page) postback. Since we want the tool to execute asynchronously, keep this property set to false. Set the Tool's other properties as follows, then click OK.

    Property Value Description
     Text

     Select Tool

     Label for the tool in the Toolbar
     ClientAction

     DragRectangle

     Client event passed to the server
     Name  SelectTool  Object name of the tool, if used in code
     ServerActionAssembly

     App_Code

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

     SelectTool

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

  3. In design view, expand the width of the Toolbar so the text of each tool is clearly visible.
  4. Run the application. Activate the custom tool by clicking “Select Tool” on the toolbar. Use the left-most drop-down list to specify the resource to select features from, then specify the layer to select features from using the drop-down list on the right. Use the tool by clicking on the map and dragging the rectangle that appears around the features you want to select. The selected features will be highlighted according to the selection layer's LayerFormat, and the attributes of the selected features will be displayed in the TaskResults control.