Common Custom tasks
Common_CustomTasks_VBNet\FindNearTask_VBNet\FindNearTask.vb
' Copyright 2010 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

' Embedded images

Imports Microsoft.VisualBasic
Imports System
<Assembly: System.Web.UI.WebResource("point.bmp", "image/bmp")> 
<Assembly: System.Web.UI.WebResource("pointD.bmp", "image/bmp")> 
<Assembly: System.Web.UI.WebResource("pointU.bmp", "image/bmp")> 
<Assembly: System.Web.UI.WebResource("point.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("point_selected.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("point_hover.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("polyline.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("polyline_selected.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("polyline_hover.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("polygon.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("polygon_selected.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("polygon_hover.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("clearInput.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("clearInput_selected.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("clearInput_hover.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("selectFeature.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("selectFeature_selected.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("selectFeature_hover.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("selectTaskResult.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("selectTaskResult_selected.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("selectTaskResult_hover.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("preview.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("contextMenuRemove.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("contextMenuZoomTo.gif", "image/gif")> 
<Assembly: System.Web.UI.WebResource("contextMenuViewTable.gif", "image/gif")> 

' Embedded JavaScript
<Assembly: System.Web.UI.WebResource("FindNearTask.js", "text/javascript")> 

Namespace FindNearTask_VBNet
  ' Task that allows searching for features within a specified distance of user-drawn graphics, selected 
  ' features, or task results
    <System.ComponentModel.DefaultProperty("TabularResultOptions"), System.Drawing.ToolboxBitmap(GetType(FindNearTask_VBNet.FindNearTask)), System.Web.UI.ToolboxData("<{0}:FindNearTask runat=""server""  Width=""400px"" BackColor=""White"" " & ControlChars.CrLf & "        BorderColor=""#999999"" BorderStyle=""Solid"" BorderWidth=""1px"" Font-Names=""Verdana"" " & ControlChars.CrLf & "        Font-Size=""8pt"" ForeColor=""Black"" TitleBarColor=""#2F5675"" TitleBarHeight=""24px"" " & ControlChars.CrLf & "        TitleBarSeparatorLine=""True"" Transparency=""0"" Font-Bold=""True"" TitleBarForeColor=""White"" " & ControlChars.CrLf & "        Visible=""False"" DockingContainerElementID=""LeftPanelCellDiv"" ShowDockedContextMenu=""True"" " & ControlChars.CrLf & "        GeometryServiceUrl=""http://tasks.arcgisonline.com/arcgis/services/Geometry/GeometryServer""" & ControlChars.CrLf & "        ShowDockButton=""True"" Docked=""False"" > </{0}:FindNearTask>")> _
  Public Class FindNearTask
        Inherits ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanelTask

#Region "Instance Varables"

        ' UI Controls
        Private _taskToolbar As ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar
        Private _selectionLayerDropDownList As System.Web.UI.WebControls.DropDownList
        Private _selectionLayerLabel As System.Web.UI.WebControls.Label
        Private _searchDistanceTextBox As System.Web.UI.WebControls.TextBox
        Private _unitsDropDownList As System.Web.UI.WebControls.DropDownList
        Private _searchLayerDropDownList As System.Web.UI.WebControls.DropDownList
        Private _activityIndicatorDiv As System.Web.UI.HtmlControls.HtmlGenericControl
        Private _findButton As System.Web.UI.HtmlControls.HtmlInputButton
        Private _graphicsLayerContextMenu As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenu

        ' Tracking variables shared internally
        Private _taskFrameworkJobID As String = Nothing
        Private _newTaskResultsPanel As Boolean = False
        Private _originalDataSetName As String = Nothing

        ' Internal references to buddied Map and TaskResults controls
        Private _map As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map = Nothing
        Private _taskResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults = Nothing

#End Region

#Region "Properties"

#Region "Internal Properties"

        ' The task's input parameters.  See InputParameters.cs for the parameters' class definition.
        Friend Property TaskInput() As FindNearTask_VBNet.InputParameters
            Get
                ' Attempt to retrieve the input parameters from state
                Dim inputParameters As InputParameters = TryCast(StateManager.GetProperty("FindNearTaskInputParameters"), InputParameters)
                If inputParameters Is Nothing Then
                    ' Since no parameters were stored in state, create a new InputParameters object
                    inputParameters = New InputParameters()
                    ' Initialize the TaskInput property so the InputParameters object is stored in state
                    Me.TaskInput = inputParameters
                End If
                ' Return the parameters
                Return inputParameters
            End Get
            Set(ByVal value As FindNearTask_VBNet.InputParameters)
                ' Store the passed-in parameters in state
                StateManager.SetProperty("FindNearTaskInputParameters", value)
            End Set
        End Property

        ' Graphics resource used to store user input and buffer graphics layers.  The geometries of the features
        ' in this resource are used as input for the task.
        Friend ReadOnly Property TaskInputGraphicsResource() As FindNearTask_VBNet.GraphicsResource
            Get
                ' Attempt to retrieve the task's graphics resource from state
                Dim taskGraphicsResource As FindNearTask_VBNet.GraphicsResource = TryCast(Me.StateManager.GetProperty("TaskInputGraphicsResource"), FindNearTask_VBNet.GraphicsResource)
                If taskGraphicsResource Is Nothing Then
                    ' Since the task's graphics resource is not yet in state, create it and store it
                    Dim resourceName As String = String.Format("{0} Input Graphics Resource", Me.ClientID)
                    taskGraphicsResource = New FindNearTask_VBNet.GraphicsResource(resourceName, Me.MapInstance)
                    Me.StateManager.SetProperty("TaskInputGraphicsResource", taskGraphicsResource)
                End If

                ' Return the resource
                Return taskGraphicsResource
            End Get
        End Property

#End Region

#Region "Private Properties"

        ' Whether to define the search area by selecting a task result
        Private Property SearchAreaByTaskResult() As Boolean
            Get
                ' Check whether the property is stored in state.  If not, return false.  If so, return
                ' the value from state.
                If StateManager.GetProperty("SearchAreaByTaskResult") Is Nothing Then
                    Return False
                Else
                    Return CBool(StateManager.GetProperty("SearchAreaByTaskResult"))
                End If
            End Get
            Set(ByVal value As Boolean)
                ' Store the passed-in value in state
                StateManager.SetProperty("SearchAreaByTaskResult", value)
            End Set
        End Property

        ' Returns the buddied Map
        Private ReadOnly Property MapInstance() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Map
            Get
                Return Me.TaskResultsInstance.MapInstance
            End Get
        End Property

        ' Returns the first buddied TaskResults container that has a buddied Map
        Private ReadOnly Property TaskResultsInstance() As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults
            Get
                ' Check whether the internal task results variable is null
                If _taskResults Is Nothing Then
                    ' Iterate through the the task's buddied TaskResults containers.  Exit the loop when the first one wtih
                    ' a buddied Map control is found.
                    For i As Integer = 0 To Me.TaskResultsContainers.Count - 1
                        _taskResults = TryCast(ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(Me.TaskResultsContainers(i).Name, Me.Page), ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults)
                        If _taskResults IsNot Nothing AndAlso _taskResults.MapInstance IsNot Nothing Then
                            Exit For
                        End If
                    Next i
                End If
                ' Return the internal variable storing the reference to the TaskResults
                Return _taskResults
            End Get
        End Property

        ' Counts the number of times the task has executed.  Used to apply a unique ID to new TaskResultsPanels displaying resutls.
        Private Property SearchCount() As Integer
            Get
                If Me.StateManager.GetProperty("OperationCount") Is Nothing Then
                    Return 1
                Else
                    Return CInt(Fix(Me.StateManager.GetProperty("OperationCount")))
                End If
            End Get
            Set(ByVal value As Integer)
                Me.StateManager.SetProperty("OperationCount", value)
            End Set
        End Property

#End Region

#Region "Public Properties"

        ''' <summary>
        ''' How tabular results will be displayed
        ''' </summary>
        <System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.Browsable(True), System.ComponentModel.NotifyParentProperty(True), System.ComponentModel.DefaultValue(TabularResultOptions.TableInFloatingPanel), System.ComponentModel.Description("How tabular results will be displayed"), System.ComponentModel.Category("Task")> _
        Public Property TabularResultOptions() As TabularResultOptions
            Get
                ' Attempt retrieving the tabular result options from state.  If not found, return the default
                ' option.  Otherwise, return the value from state.
                Dim obj As Object = StateManager.GetProperty("TabularResultOptions")
                Return If((obj Is Nothing), TabularResultOptions.TableInFloatingPanel, CType(obj, TabularResultOptions))
            End Get
            ' Store the passed-in value in state
            Set(ByVal value As TabularResultOptions)
                StateManager.SetProperty("TabularResultOptions", value)
            End Set
        End Property

        ''' <summary>
        ''' The URL of an ArcGIS Server Geometry Service.  Required for calculating the search area.
        ''' </summary>
        <System.Web.UI.PersistenceMode(System.Web.UI.PersistenceMode.Attribute), System.ComponentModel.Browsable(True), System.ComponentModel.NotifyParentProperty(True), System.ComponentModel.DefaultValue("http://tasks.arcgisonline.com/arcgis/services/Geometry/GeometryServer"), System.ComponentModel.Description("URL for an ArcGIS Server geometry service"), System.ComponentModel.Category("Task")> _
        Public Property GeometryServiceUrl() As String
            Get
                ' Attempt retrieving the geometry service URL from state.  If not found, return the default
                ' URL.  Otherwise, return the value from state.
                Dim obj As Object = StateManager.GetProperty("GeometryServiceUrl")
                Return If((obj Is Nothing), "http://tasks.arcgisonline.com/arcgis/services/Geometry/GeometryServer", CStr(obj))
            End Get
            ' Store the passed-in value in state
            Set(ByVal value As String)
                StateManager.SetProperty("GeometryServiceUrl", value)
            End Set
        End Property

#End Region

#End Region

#Region "ASP.NET WebControl Life Cycle Event Handlers"

        Protected Overrides Sub CreateChildControls()
            MyBase.CreateChildControls()

            ' Do not create child controls if the control is being rendered at design-time
            If Me.DesignMode Then
                Return
            End If

            ' Table to format the task interface and add it to the task's controls
            Dim taskInterfaceTable As New System.Web.UI.WebControls.Table()
            Controls.Add(taskInterfaceTable)

            ' Row for the distance label, distance textbox, and units label
            Dim tableRow As New System.Web.UI.WebControls.TableRow()
            taskInterfaceTable.Rows.Add(tableRow)

            ' Cell for the search area tools label
            Dim tableCell As New System.Web.UI.WebControls.TableCell()
            tableCell.ColumnSpan = 2
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Create the search area tools label and add it to the container cell
            Dim label As New System.Web.UI.WebControls.Label()
            label.Text = "Search Area Definition Tools"
            tableCell.Controls.Add(label)

            ' Create the toolbar containing the task's tools and add it to the interface table
            CreateTaskToolbar()
            tableCell.Controls.Add(_taskToolbar)

            ' Table row and cell for the selection layer controls
            tableRow = New System.Web.UI.WebControls.TableRow()
            taskInterfaceTable.Rows.Add(tableRow)
            tableCell = New System.Web.UI.WebControls.TableCell()
            tableCell.ColumnSpan = 2
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Create selection layer label and drop-down list and add them to the table
            CreateSelectionLayerControls()
            tableCell.Controls.Add(_selectionLayerLabel)
            tableCell.Controls.Add(_selectionLayerDropDownList)

            ' Table row and cell for the Add New Search Areas to Previous checkbox
            tableRow = New System.Web.UI.WebControls.TableRow()
            taskInterfaceTable.Rows.Add(tableRow)
            tableCell = New System.Web.UI.WebControls.TableCell()
            tableCell.ColumnSpan = 2
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Create a checkbox to give user the option of overwriting or adding to task input geometries and add
            ' it to the UI table
            tableCell.Controls.Add(CreateAddToInputCheckbox())

            ' Row for the search distance controls
            tableRow = New System.Web.UI.WebControls.TableRow()
            tableRow.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            taskInterfaceTable.Rows.Add(tableRow)

            ' Cell for the search distance label and textbox
            tableCell = New System.Web.UI.WebControls.TableCell()
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Create the search distance label and add it to the cell
            tableCell.Controls.Add(CreateSearchDistanceLabel())

            ' Add the search distance textbox to the table            
            CreateSearchDistanceControls()
            tableCell.Controls.Add(_searchDistanceTextBox)

            ' Cell for the units drop-down list
            tableCell = New System.Web.UI.WebControls.TableCell()
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Add the units drop-down list to the table
            tableCell.Controls.Add(_unitsDropDownList)

            ' Table row and cell for the search layer label and drop-down list
            tableRow = New System.Web.UI.WebControls.TableRow()
            taskInterfaceTable.Rows.Add(tableRow)
            tableCell = New System.Web.UI.WebControls.TableCell()
            tableCell.ColumnSpan = 2
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Create search layer label and add it to the table
            tableCell.Controls.Add(CreateSearchLayerLabel())

            ' Create selection layer drop-down list and add it to the table
            CreateSearchLayerDropDownList()
            tableCell.Controls.Add(_searchLayerDropDownList)

            ' Row and cell for the Find button
            tableRow = New System.Web.UI.WebControls.TableRow()
            taskInterfaceTable.Rows.Add(tableRow)
            tableCell = New System.Web.UI.WebControls.TableCell()
            tableCell.ColumnSpan = 2
            tableCell.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            tableRow.Cells.Add(tableCell)

            ' Create the Find button to the container table cell
            CreateFindButton()
            tableCell.Controls.Add(_findButton)

            ' Create and add the activity indicator div to the table
            CreateActivityIndicatorDiv()
            tableCell.Controls.Add(_activityIndicatorDiv)

            ' If tabular results are to appear in a floating panel, call the function to create the task results
            ' context menu containing an item to show the panel
            If TabularResultOptions = TabularResultOptions.TableInFloatingPanel Then
                Me.CreateTaskResultsPanelContextMenu()
            End If

            ' Add handlers for the task results container's node events.  The NodeAdded and NodeRemoved hanlders
            ' manage creating and destroying TaskResultsPanels for the associated task results, while the NodeClicked
            ' handler incorporates adding the clicked task result's search geometry if the tool to specify 
            ' input by task result is selected
            AddHandler TaskResultsInstance.NodeAdded, AddressOf TaskResults_NodeAdded
            AddHandler TaskResultsInstance.NodeRemoved, AddressOf TaskResults_NodeRemoved
            AddHandler TaskResultsInstance.NodeClicked, AddressOf TaskResults_NodeClicked
        End Sub

        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            MyBase.Render(writer)

            If DesignMode Then
                Return
            End If

            ' Register the JavaScript file containing the functions used by the task
            If (Not Me.Page.ClientScript.IsStartupScriptRegistered("FindNearTaskScript")) Then
                Me.Page.ClientScript.RegisterStartupScript(GetType(FindNearTask_VBNet.FindNearTask), "FindNearTaskScript", FindNearTask_VBNet.ResourceUtility.GetJavascript(Me, "FindNearTask.js", GetType(FindNearTask_VBNet.FindNearTask)))
            End If

            ' Registered the TaskResultsPanel's scripts
            FindNearTask_VBNet.TaskResultsPanel.RegisterScripts(Me)

            ' Find all the toolbars on the page and construct a string containing their IDs
            Dim toolbarList As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar) = ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControls(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar)(Me.Page.Controls)
            Dim toolbarIDs As String = ""
            For Each toolbar As ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar In toolbarList
                toolbarIDs &= toolbar.ClientID & ","
            Next toolbar
            If toolbarIDs.Length > 0 Then
                toolbarIDs = toolbarIDs.Substring(0, toolbarIDs.Length - 1)
            End If

            ' Construct JavaScript to pass server-side task initialization data to the client
            Dim initializationScriptKey As String = "FindNearTaskInitializationScript"
            If (Not Page.ClientScript.IsStartupScriptRegistered(initializationScriptKey)) Then
                Dim initializationScript As String = "" & ControlChars.CrLf & "                    function findNearTaskInit() {{" & ControlChars.CrLf & "                        _callbackFunctionString = ""{0}"";" & ControlChars.CrLf & "                    " & ControlChars.CrLf & "                        var toolbarIDs = '{1}';" & ControlChars.CrLf & "                        toolbarIDs = toolbarIDs.split(',');" & ControlChars.CrLf & "                        for (var i = 0; i < toolbarIDs.length; i++)" & ControlChars.CrLf & "                        {{" & ControlChars.CrLf & "                            var toolbar = $find(toolbarIDs[i]);" & ControlChars.CrLf & "                            if (toolbar)" & ControlChars.CrLf & "                                toolbar.add_onToolSelected(toolSelected);" & ControlChars.CrLf & "                        }}" & ControlChars.CrLf & ControlChars.CrLf & "                        _selectionLayerLabel = $get('{2}');                    " & ControlChars.CrLf & "                        _selectionLayerDropDownList = $get('{3}');" & ControlChars.CrLf & "                        _searchDistanceTextBox = $get('{4}');" & ControlChars.CrLf & ControlChars.CrLf & "                        _unitsDropDownList = $get('{5}');" & ControlChars.CrLf & "                        _buddyMapID = '{6}';" & ControlChars.CrLf & "                        _findButton = $get('{7}');" & ControlChars.CrLf & "                        _activityIndicatorDiv = $get('{8}');" & ControlChars.CrLf & "                        _buddyTaskResultsID = '{9}';" & ControlChars.CrLf & ControlChars.CrLf & "                        initAsyncRequestHandler();" & ControlChars.CrLf & "                    }}" & ControlChars.CrLf & ControlChars.CrLf & "                    Sys.Application.add_init(findNearTaskInit);"
                initializationScript = String.Format(initializationScript, Me.CallbackFunctionString, toolbarIDs, _selectionLayerLabel.ClientID, _selectionLayerDropDownList.ClientID, _searchDistanceTextBox.ClientID, _unitsDropDownList.ClientID, Me.MapInstance.ClientID, _findButton.ClientID, _activityIndicatorDiv.ClientID, Me.TaskResultsInstance.ClientID)

                ' Register the script on the page
                Page.ClientScript.RegisterStartupScript(Me.GetType(), initializationScriptKey, initializationScript, True)
            End If

            Me.TaskResultsInstance.ShowRowCount = False
        End Sub

#End Region

#Region "Web ADF Control Event Handlers"

        ' Fires when a node is removed from the TaskResults control buddied to the task.  If the removed node corresponds
        ' to a TaskResultsPanel, then that panel is detroyed here.
        Private Sub TaskResults_NodeRemoved(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeRemovedEventArgs)
            ' Check whether results are being shown in a TaskResultsPanel
            If Me.TabularResultOptions = TabularResultOptions.TableInFloatingPanel Then
                ' Call method to retrieve a GraphicsLayerNode that is the child of the current node.  Note this method
                ' will also check whether the current node is a GraphicsLayerNode
                Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = Me.FindChildGraphicsLayerNode(args.Node)

                ' Check whether a GraphicsLayerNode was found
                If graphicsLayerNode IsNot Nothing Then
                    ' Construct JavaScript to find the TaskResultsPanel corresponding to the GraphicsLayerNode and 
                    ' destroy it.
                    Dim taskResultsPanelClientID As String = String.Format("{0}_{1}_TaskResultsPanel", Me.ClientID, graphicsLayerNode.NodeID)
                    Dim disposeResultsPanelJavaScript As String = "" & ControlChars.CrLf & "                    var taskResultsPanel = $find('{0}');" & ControlChars.CrLf & "                    if (taskResultsPanel)" & ControlChars.CrLf & "                    {{" & ControlChars.CrLf & "                        taskResultsPanel.hide(false);" & ControlChars.CrLf & "                        taskResultsPanel.dispose();" & ControlChars.CrLf & "                    }}" & ControlChars.CrLf & "                    var element = $get('{0}');" & ControlChars.CrLf & "                    if (element)" & ControlChars.CrLf & "                        element.parentNode.removeChild(element);"
                    disposeResultsPanelJavaScript = String.Format(disposeResultsPanelJavaScript, taskResultsPanelClientID)

                    ' Encapsulate the JavaScript in a callback result and add it to the task's collection of CallbackResults
                    Dim disposeResultsPanelCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(disposeResultsPanelJavaScript)
                    Me.CallbackResults.Add(disposeResultsPanelCallbackResult)
                End If
            End If

            ' Retrieve any FeatureNodes that are children of the removed node.  Note this call will also check whether
            ' the current node is a FeatureNode.
            Dim featureNodes As New System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)()
            Me.FindChildFeatureNodes(args.Node, featureNodes)

            ' Check whether any FeatureNodes were found
            If featureNodes.Count > 0 Then
                ' For each FeatureNode, construct JavaScript to reset the corresponding entry in the client-side array 
                ' storing FeatureNode IDs to null
                Dim removeNodeIDsJavaScript As String = ""
                For Each featureNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode In featureNodes
                    removeNodeIDsJavaScript &= String.Format("_featureNodeIDs['{0}'] = null;", featureNode.NodeID)
                Next featureNode

                ' Wrap the JavaScript in a CallbackResult and add it to the task's collection
                Dim removeNodeIDsCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(removeNodeIDsJavaScript)
                Me.CallbackResults.Add(removeNodeIDsCallbackResult)
            End If

            ' Add any CallbackResults created by the task to the calling controls results so they are processed on the client
            Me.CopyCallbackResultsToCaller(Me.CallbackResults)
        End Sub

        ' Fires when a node is added to the TaskResults control buddied to the Task.  
        Private Sub TaskResults_NodeAdded(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs)
            ' Check whether the internal flag indicating that a new TaskResultsPanel needs to be created is true and 
            ' results are to be shown in a TaskResultsPanel
            If Me._newTaskResultsPanel AndAlso (Me.TabularResultOptions = TabularResultOptions.TableInFloatingPanel) Then
                ' Get the GraphicsLayerNode that is an ancestor or descendant of the current node
                Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = Me.GetRelatedGraphicsLayerNode(args.Node)

                ' Make sure a GraphicsLayerNode was found and that the name of the dataset containing the GraphicsLayer it
                ' references does not match the dataset name that was assigned in ExecuteTask.  We do this because the Web
                ' ADF replaces the original node with one that has IDs that are used internally by other ADF components.  
                ' The DataSetName in particular holds the name of the resource that contains the results GraphicsLayer.
                If graphicsLayerNode IsNot Nothing AndAlso graphicsLayerNode.Layer.DataSet.DataSetName <> Me._originalDataSetName Then
                    ' Create the TaskResultsPanel that will be used to display results
                    Dim taskResultsPanelID As String = String.Format("{0}_TaskResultsPanel", graphicsLayerNode.NodeID)
                    Dim taskResultsPanelTitle As String = String.Format("{0} Results - {1}", Me.Title, graphicsLayerNode.Layer.TableName)
                    Dim taskResultsPanel As FindNearTask_VBNet.TaskResultsPanel = Me.CreateTaskResultsPanel(taskResultsPanelID, taskResultsPanelTitle)

                    ' When a Web ADF FloatingPanel is rendered during an asynchronous request, the ADF automatically creates
                    ' a callback result that includes a call to the private client-side function _checkDock.  In cases where the
                    ' FloatingPanel does not have a docking container, this interferes with the FloatingPanel's initialization.
                    ' So we remove that callback result here.                    
                    Dim checkDockJavaScript As String = String.Format("$find('{0}')._checkDock();", taskResultsPanel.ClientID)
                    Dim callbackResultToRemove As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = Nothing
                    For Each callbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult In taskResultsPanel.CallbackResults
                        If TryCast(callbackResult.Parameters(0), String) = checkDockJavaScript Then
                            callbackResultToRemove = callbackResult
                            Exit For
                        End If
                    Next callbackResult
                    If callbackResultToRemove IsNot Nothing Then
                        taskResultsPanel.CallbackResults.Remove(callbackResultToRemove)
                    End If

                    ' Get the name of the resource containing the results GraphicsLayer and call SetLayer to associate the 
                    ' TaskResultsPanel with the GraphicsLayer
                    Dim resourceName As String = graphicsLayerNode.Layer.DataSet.DataSetName
                    taskResultsPanel.SetLayer(TryCast(graphicsLayerNode.Layer, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer), resourceName, Me.TaskResultsInstance.Map)

                    ' Call ShowFloatingPanel to display the results panel
                    taskResultsPanel.ShowFloatingPanel()

                    ' Copy the results panel's callback results to the task's results collection so changes made to the
                    ' panel requiring client-side handling are processed
                    Me.CallbackResults.CopyFrom(taskResultsPanel.CallbackResults)

                    ' Reset the flag indicating whether a new TaskResultsPanel needs to be created
                    Me._newTaskResultsPanel = False
                End If
            End If

            ' Find any FeatureNodes that are descendants of the added node
            Dim featureNodes As New System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)()
            Me.FindChildFeatureNodes(args.Node, featureNodes)
            ' Check whether any FeatureNodes were found
            If featureNodes.Count > 0 Then
                ' Create JavaScript to add each FeatureNode's ID to a client-side array.  This allows the tool to
                ' specify search area by clicking on a task result to determine if a clicked node is a FeatureNode
                ' and show a progress indicator accordingly.
                Dim defineNodeIDsJavaScript As String = ""
                For Each featureNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode In featureNodes
                    defineNodeIDsJavaScript &= String.Format("_featureNodeIDs['{0}'] = true;", featureNode.NodeID)
                Next featureNode

                ' Put the JavaScript in a CallbackResult and add it to the task's collection
                Dim defineNodeIDsCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(defineNodeIDsJavaScript)
                Me.CallbackResults.Add(defineNodeIDsCallbackResult)
            End If

            ' Check whether the control that initiated the asynchronous request is the task.  If not, copy the task's
            ' callback results to the control that did.
            If Me.GetCallingControl(Me.Page).UniqueID <> Me.UniqueID Then
                Me.CopyCallbackResultsToCaller(Me.CallbackResults)
            End If
        End Sub

        ' Fires when a node on the buddied TaskResults control is clicked.  Updates the search area geometry with that of 
        ' the feature corresponding to the clicked node if the node is a FeatureNode and the tool to specify search area by 
        ' task result is active, 
        Private Sub TaskResults_NodeClicked(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs)
            ' Check whether the passed-in node is a feature node and the define search area by task result tool is active
            If Me.SearchAreaByTaskResult AndAlso (TypeOf args.Node Is ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode) Then
                ' Get a reference to the clicked node as a FeatureNode
                Dim clickedFeatureNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode = TryCast(args.Node, ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)

                ' Loop through the data of the FeatureNode until the feature geometry is found
                For Each value As Object In clickedFeatureNode.DataRow.ItemArray
                    If TypeOf value Is ESRI.ArcGIS.ADF.Web.Geometry.Geometry Then
                        ' Put the feature geometry in a geometry array
                        Dim inputGeometries(0) As ESRI.ArcGIS.ADF.Web.Geometry.Geometry
                        inputGeometries(0) = TryCast(value, ESRI.ArcGIS.ADF.Web.Geometry.Geometry)

                        ' Update the task's input geometries with the feature geometry
                        Me.TaskInput.SetUserInputGeometries(inputGeometries)

                        ' Update the task's user input and buffer graphics layer
                        Me.UpdateInputGraphicsLayers()

                        ' Create a callback result to call the client-side function that hides the activity indicator
                        Dim hideActivityIndicatorJavaScript As String = "hideUpdateAreaIndicator();"
                        Dim hideIndicatorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(hideActivityIndicatorJavaScript)
                        Me.CallbackResults.Add(hideIndicatorCallbackResult)

                        ' Check whether the task initiated the request.  If not, copy the callback results to the initiating
                        ' control so the updates to the GraphicsLayer are processed on the client.
                        If Me.GetCallingControl(Me.Page).UniqueID <> Me.UniqueID Then
                            Me.CopyCallbackResultsToCaller(Me.CallbackResults)
                        End If
                        Exit For
                    End If
                Next value
            End If
        End Sub

        ' Fires when the custom GraphicLayer context menu is closed.
        Private Sub GraphicsLayerContextMenu_Dismissed(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuDismissedEventArgs)
            Me.TaskResultsInstance.ContextMenuDismissed(_graphicsLayerContextMenu, args)
        End Sub

        ' Fires when an item on the custom GraphicsLayer context menu is clicked
        Private Sub GraphicsLayerContextMenu_ItemClicked(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItemEventArgs)
            ' Get the node on which the context menu was displayed
            Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(TaskResultsInstance.Nodes.FindByNodeID(args.Context), ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)

            ' Check the text of the item clicked
            Select Case args.Item.Text
                Case "Zoom To Selected Features"
                    If graphicsLayerNode Is Nothing OrElse Me.MapInstance Is Nothing Then
                        Return
                    End If
                    Dim hasFeaturesSelected As Boolean = False

                    ' Get the GraphicsLayer associated with the node
                    Dim graphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = graphicsLayerNode.Layer

                    ' Declare an envelope to store the combined extent of all features in the layer
                    Dim adfEnvelope As New ESRI.ArcGIS.ADF.Web.Geometry.Envelope()

                    ' Loop through the rows (i.e. features) of the graphics layer, adding the envelope of each to the
                    ' combined extent envelope
                    For i As Integer = 0 To graphicsLayer.Rows.Count - 1
                        If CBool(graphicsLayer.Rows(i)(graphicsLayer.IsSelectedColumn)) Then
                            hasFeaturesSelected = True

                            Dim rowGeometry As ESRI.ArcGIS.ADF.Web.Geometry.Geometry = graphicsLayer.GeometryFromRow(graphicsLayer.Rows(i))
                            adfEnvelope.Union(rowGeometry)
                        End If
                    Next i

                    If (Not hasFeaturesSelected) Then
                        Return
                    End If

                    ' If combined envelope width or height is zero, zoom in the amount specified by the 
                    ' ZoomToPointFactor property
                    If adfEnvelope.Width = 0 OrElse adfEnvelope.Height = 0 Then
                        Dim adfPoint As New ESRI.ArcGIS.ADF.Web.Geometry.Point(adfEnvelope.XMax, adfEnvelope.YMax)
                        Dim fullExtentEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = Me.MapInstance.GetFullExtent()

                        Dim widthMargin As Double = (fullExtentEnvelope.Width / TaskResultsInstance.ZoomToPointFactor) / 2
                        Dim heightMargin As Double = (fullExtentEnvelope.Height / TaskResultsInstance.ZoomToPointFactor) / 2

                        Dim zoomToEnvelope As New ESRI.ArcGIS.ADF.Web.Geometry.Envelope()

                        zoomToEnvelope.XMax = adfPoint.X + widthMargin
                        zoomToEnvelope.XMin = adfPoint.X - widthMargin
                        zoomToEnvelope.YMax = adfPoint.Y + heightMargin
                        zoomToEnvelope.YMin = adfPoint.Y - heightMargin

                        Me.MapInstance.Extent = zoomToEnvelope
                    Else
                        ' Apply the combined feature extent to the map
                        Me.MapInstance.Extent = adfEnvelope
                    End If

                    ' Copy the map's callback results to the context menu so the extent change is processed on the client
                    _graphicsLayerContextMenu.CallbackResults.CopyFrom(Me.MapInstance.CallbackResults)
                Case "Remove"
                    If Me.MapInstance Is Nothing OrElse graphicsLayerNode Is Nothing Then
                        Return
                    End If

                    ' Check whether there is a GraphicsLayer associated with the node
                    If graphicsLayerNode.Layer IsNot Nothing Then
                        ' Remove the GraphicsLayer associated with the node from the map and refresh the layer's
                        ' parent resource
                        Dim graphicsResourceName As String = graphicsLayerNode.RemoveFromMap(Me.TaskResultsInstance)
                        Me.MapInstance.RefreshResource(graphicsResourceName)

                        ' Copy the map's callback results to the context menu so the map is updated on the client
                        _graphicsLayerContextMenu.CallbackResults.CopyFrom(Me.MapInstance.CallbackResults)
                    End If

                    ' Remove the node and refresh the buddied TaskResults control
                    graphicsLayerNode.Remove()
                    Me.TaskResultsInstance.Refresh()

                    ' Copy the buddied TaskResults control's callback results to the context menu so the node removal
                    ' is processed on the client
                    _graphicsLayerContextMenu.CallbackResults.CopyFrom(TaskResultsInstance.CallbackResults)
                Case "View Attribute Table"
                    ' Construct JavaScript to call the client-side Web ADF function to display the TaskResultsPanel
                    Dim taskResultsPanelClientID As String = String.Format("{0}_{1}_TaskResultsPanel", Me.ClientID, args.Context)
                    Dim showTaskResultsPanelJavaScript As String = String.Format("showFloatingPanel('{0}', false);", taskResultsPanelClientID)
                    Dim showTaskResultsPanelCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(showTaskResultsPanelJavaScript)
                    _graphicsLayerContextMenu.CallbackResults.Add(showTaskResultsPanelCallbackResult)
            End Select
        End Sub

#End Region

#Region "ICallbackEventHandler Overrides - GetCallbackResults"

        Public Overrides Function GetCallbackResult() As String
            ' Convert the callback argument string into a name-value collection using the Web ADF's callback 
            ' argument parsing utility
            Dim callbackArgsCollection As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(Me.CallbackEventArgument)

            ' Get the task's job ID
            _taskFrameworkJobID = callbackArgsCollection("taskJobID")

            ' Get the callback event argument
            Dim eventArg As String = callbackArgsCollection("EventArg")

            Select Case eventArg
                ' Check whether the event argument indicates that the user has modified the search distance
                Case "updateSearchArea"
                    ' Get the updated search distance and make sure it is valid
                    Dim searchDistance As String = callbackArgsCollection("searchDistance")
                    Dim units As String = callbackArgsCollection("units")

                    ' Make sure a search distance was entered
                    If (Not String.IsNullOrEmpty(searchDistance)) Then
                        Dim newSearchDistance As Single
                        If Single.TryParse(searchDistance, newSearchDistance) Then
                            Me.TaskInput.SearchDistance = newSearchDistance
                        End If

                        Me.TaskInput.Units = units

                        ' Update the graphics layer showing the search area
                        Me.UpdateInputGraphicsLayers()

                        ' Copy the callback results from the map so the graphics layer update is processed
                        ' on the client
                        Me.CallbackResults.CopyFrom(MapInstance.CallbackResults)
                    End If

                    ' Create callback result to hide the task's activity indicator
                    Dim hideActivityIndicatorCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript("hideUpdateAreaIndicator();")
                    Me.CallbackResults.Add(hideActivityIndicatorCallbackResult)
                Case "toggleSelectByTaskResult"
                    ' Set the flag indicating wheteher the define search area by task result tool
                    Me.SearchAreaByTaskResult = Boolean.Parse(callbackArgsCollection("Value"))
                Case "toggleAddToInput"
                    ' Set the flag indicating whether newly defined geometries are to be added to those already defined
                    Me.TaskInput.AddToInputGeometry = Not Me.TaskInput.AddToInputGeometry
                Case "updateSearchLayer"
                    ' Get the map resource and layer ID for the layer clicked.  These values are stored as the value of
                    ' the tree view plus node and are comma delimited.
                    Dim searchLayerParameters() As String = callbackArgsCollection("searchLayerParameters").Split(New Char() {","c})
                    ' Update the task's input parameters with the node's information
                    Me.TaskInput.SearchResource = searchLayerParameters(0)
                    Me.TaskInput.SearchLayer = searchLayerParameters(1)
            End Select
            Return MyBase.GetCallbackResult()
        End Function

        ' Updates the geometries of the user input and buffer graphics layers
        Friend Sub UpdateInputGraphicsLayers()
            ' If there are not defined input geometries, exit the function
            If Me.TaskInput.UserInputGeometries Is Nothing OrElse Me.TaskInput.UserInputGeometries.Length = 0 Then
                Return
            End If

            ' Get the graphics layer displaying the user defined geometries and clear it
            Dim inputGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = TryCast(Me.TaskInputGraphicsResource.GetUserInputLayer(Me.MapInstance.MapResourceManagerInstance), ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer)
            Dim adfSearchGeometries() As ESRI.ArcGIS.ADF.Web.Geometry.Geometry = Me.TaskInput.UserInputGeometries
            inputGraphicsLayer.Clear()

            ' Get the search distance
            Dim searchDistance As Single = TaskInput.SearchDistance

            ' Make sure the user specified geometries and distance are valid
            If adfSearchGeometries IsNot Nothing AndAlso (Not Single.IsNaN(searchDistance)) AndAlso searchDistance > 0.0 Then
                Dim inputGraphicElement As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement = Nothing

                ' Create a symbol for the user specified geometry
                For i As Integer = 0 To adfSearchGeometries.Length - 1
                    If TypeOf adfSearchGeometries(i) Is ESRI.ArcGIS.ADF.Web.Geometry.Point Then
                        Dim adfSearchPointSymbol As New ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol(System.Drawing.Color.Green, 10, ESRI.ArcGIS.ADF.Web.Display.Symbol.MarkerSymbolType.Star)

                        ' Create a graphic element for the search point and add it to the graphics layer
                        inputGraphicElement = New ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement(adfSearchGeometries(i), adfSearchPointSymbol)
                    ElseIf TypeOf adfSearchGeometries(i) Is ESRI.ArcGIS.ADF.Web.Geometry.Polyline Then
                        Dim adfSearchLineSymbol As New ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleLineSymbol(System.Drawing.Color.Green, 4, ESRI.ArcGIS.ADF.Web.Display.Symbol.LineType.Solid)

                        inputGraphicElement = New ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement(adfSearchGeometries(i), adfSearchLineSymbol)
                    ElseIf TypeOf adfSearchGeometries(i) Is ESRI.ArcGIS.ADF.Web.Geometry.Polygon Then
                        Dim adfSearchFillSymbol As New ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleFillSymbol(System.Drawing.Color.Green, System.Drawing.Color.Black, 50, 20, ESRI.ArcGIS.ADF.Web.Display.Symbol.PolygonFillType.Solid)

                        inputGraphicElement = New ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement(adfSearchGeometries(i), adfSearchFillSymbol)
                    End If

                    inputGraphicsLayer.Add(inputGraphicElement)
                Next i

                ' Get the buffer geometry for the search geometry and distance
                Me.TaskInput.BufferGeometry = Me.BufferGeometries(adfSearchGeometries, searchDistance, Me.TaskInput.Units)

                ' Create a graphic element for the buffer and add it to the graphics layer
                Dim bufferElement As New ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement(Me.TaskInput.BufferGeometry, System.Drawing.Color.Red, 75)
                Dim bufferGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = Me.TaskInputGraphicsResource.GetBufferLayer(Me.MapInstance.MapResourceManagerInstance)
                bufferGraphicsLayer.Clear()
                bufferGraphicsLayer.Add(bufferElement)
            End If

            ' Refresh the resource containing the input graphics layer
            MapInstance.RefreshResource(Me.TaskInputGraphicsResource.ResourceName)

            ' Check whether the map is the control that initiated the request.  If not, copy the Map's callback results to the
            ' initiating control so the map's updates are processed on the client.
            If Me.GetCallingControl(Me.Page).UniqueID <> Me.MapInstance.UniqueID Then
                Me.CopyCallbackResultsToCaller(Me.MapInstance.CallbackResults)
            End If
        End Sub

        ' Clears the user input and buffer graphics layers
        Friend Sub ClearTaskInput()
            ' Temporarily store AddToInputGeometry
            Dim addToInputGeometry As Boolean = Me.TaskInput.AddToInputGeometry

            ' Set AddToInputGeometry to false and pass null to SetUserInputGeometries to clear the input
            ' geometries
            Me.TaskInput.AddToInputGeometry = False
            Me.TaskInput.SetUserInputGeometries(Nothing)

            ' Revert the value of AddToInputGeometry
            Me.TaskInput.AddToInputGeometry = addToInputGeometry

            ' Set the buffer geometry to null
            Me.TaskInput.BufferGeometry = Nothing

            ' Retrieve and clear the user input and buffer graphics layers
            Dim elementGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer = Me.TaskInputGraphicsResource.GetUserInputLayer(Me.MapInstance.MapResourceManagerInstance)
            elementGraphicsLayer.Clear()

            elementGraphicsLayer = Me.TaskInputGraphicsResource.GetBufferLayer(Me.MapInstance.MapResourceManagerInstance)
            elementGraphicsLayer.Clear()

            ' Update the resource containing the input graphics layers
            Me.MapInstance.RefreshResource(Me.TaskInputGraphicsResource.ResourceName)
        End Sub

#End Region

#Region "Web ADF Task Overrides - ExecuteTask, GetGISResourceItemDependencies"

        ' Called when the Find button is clicked
        Public Overrides Sub ExecuteTask()
            If Me.TaskInput.BufferGeometry Is Nothing Then
                Return
            End If

            ' Get the buddied map resource manager
            Dim mapResourceManager As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager = Me.MapInstance.MapResourceManagerInstance
            ' Get the map resource item containing the resource to be searched
            Dim mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem = mapResourceManager.ResourceItems.Find(Me.TaskInput.SearchResource)
            ' Make sure the search resource is initialized
            If mapResourceItem.Resource Is Nothing OrElse (Not mapResourceItem.Resource.Initialized) Then
                mapResourceItem.InitializeResource()
            End If

            ' Get a reference to the resource item's map resource
            Dim commonMapResource As ESRI.ArcGIS.ADF.Web.DataSources.IMapResource = TryCast(mapResourceItem.Resource, ESRI.ArcGIS.ADF.Web.DataSources.IMapResource)

            ' Use the resource to create a query functionality
            Dim queryFunctionalityName As String = String.Format("{0}_QueryFunctionality", Me.ID)
            Dim queryFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = TryCast(commonMapResource.CreateFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), queryFunctionalityName), ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)

            ' Instantiate a spatial filter with the buffer geometry
            Dim adfSpatialFilter As New ESRI.ArcGIS.ADF.Web.SpatialFilter()
            adfSpatialFilter.Geometry = Me.TaskInput.BufferGeometry
            ' Specify that the query return the geometries of result features
            adfSpatialFilter.ReturnADFGeometries = True
            ' Set the spatial reference of the query results to match that of the map
            adfSpatialFilter.OutputSpatialReference = MapInstance.SpatialReference

            ' Execute the query
            Dim resultsDataTable As System.Data.DataTable = queryFunctionality.Query(Nothing, TaskInput.SearchLayer, adfSpatialFilter)

            ' Make sure results were found and they can be shown as a graphics layer
            If resultsDataTable Is Nothing OrElse Not (TypeOf resultsDataTable Is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer) Then
                Return
            End If

            ' Convert query results to a graphics layer
            Dim resultsGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(resultsDataTable)

            ' Retrieve and apply the layer format of the search layer to the results
            Dim layerFormat As ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat = ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(mapResourceManager, Me.TaskInput.SearchResource, Me.TaskInput.SearchLayer)
            layerFormat.Apply(resultsGraphicsLayer)

            ' Set RenderOnClient to true so highlighting and maptips are enabled on the results
            resultsGraphicsLayer.RenderOnClient = True

            ' Select all features so that they are visible by default
            For Each dataRow As System.Data.DataRow In resultsGraphicsLayer.Rows
                dataRow(resultsGraphicsLayer.IsSelectedColumn) = True
            Next dataRow

            ' Create a graphics dataset and add the results graphics layer to it
            Dim resultsGraphicsDataSet As New ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet()
            resultsGraphicsDataSet.DataSetName = String.Format("{0} Results ({1})", Me.Title, resultsGraphicsLayer.Rows.Count)
            resultsGraphicsDataSet.Tables.Add(resultsGraphicsLayer)

            ' Get the number of searches performed and append the ordinal suffix 
            Dim searchCount As String = Me.SearchCount.ToString()
            Dim searchCountDescription As String
            Select Case searchCount.Substring(searchCount.Length - 1)
                Case "1"
                    searchCount &= "st"
                Case "2"
                    searchCount &= "nd"
                Case "3"
                    searchCount &= "rd"
                Case Else
                    searchCount &= "th"
            End Select
            ' Create a count description with the search count
            searchCountDescription = String.Format("{0} search", searchCount)
            ' Create the results GraphicsLayer's table name from the ADF generated table name, the number of
            ' rows, and the search count string
            resultsGraphicsLayer.TableName = String.Format("{0} ({1} found, {2})", resultsGraphicsLayer.TableName, resultsGraphicsLayer.Rows.Count, searchCountDescription)
            ' increment the search count
            Me.SearchCount += 1

            ' If the tabular results are to be formatted in a tree view in the task results container,
            ' assign the graphics dataset to the task's results.  Otherwise, call the method to create
            ' a custom task results node and assign that method's return value as the task's results.
            If TabularResultOptions = FindNearTask_VBNet.TabularResultOptions.TreeViewInTaskResults Then
                Me.Results = resultsGraphicsDataSet
            ElseIf Me.TabularResultOptions = FindNearTask_VBNet.TabularResultOptions.TableInFloatingPanel Then
                Me.Results = Me.CreateCustomTaskResultsNode(resultsGraphicsDataSet)

                If resultsGraphicsLayer.Rows.Count > 0 Then
                    Me._newTaskResultsPanel = True
                    Me._originalDataSetName = resultsGraphicsDataSet.DataSetName
                End If
            End If
        End Sub

        ' Used by Manager to see if task requires a particular resource item in a resource manager.  Since
        ' this task has no dependencies, the implementation here returns null.
        Public Overrides Function GetGISResourceItemDependencies() As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency)
            Return Nothing
        End Function

#End Region

#Region "Private Methods"

        ' Retrieves queryable layers from all resources in the passed-in MapResourceManager
        Private Sub GetQueryableLayers(ByVal mapResourceManager As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager, <System.Runtime.InteropServices.Out()> ByRef layerNamesByResources As System.Collections.Generic.Dictionary(Of String, String()), <System.Runtime.InteropServices.Out()> ByRef layerIDsByResources As System.Collections.Generic.Dictionary(Of String, String()))
            layerNamesByResources = New System.Collections.Generic.Dictionary(Of String, String())()
            layerIDsByResources = New System.Collections.Generic.Dictionary(Of String, String())()

            Dim initializedResourceManager As Boolean = False
            Try
                ' If the MapResourceManager is not initialize, do so here
                If (Not mapResourceManager.Initialized) Then
                    mapResourceManager.Initialize()
                    initializedResourceManager = True
                End If

                Dim mapResourceItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem

                ' Loop through the resources in the MapResourceManager
                For i As Integer = 0 To mapResourceManager.ResourceItems.Count - 1
                    mapResourceItem = mapResourceManager.ResourceItems(i)

                    ' If the current resource is not displayed in the Toc, skip to the next
                    If (Not mapResourceItem.DisplaySettings.DisplayInTableOfContents) Then
                        Continue For
                    End If

                    ' Ensure the resource is initialized
                    mapResourceItem.InitializeResource()

                    ' Get the map resource from the resource item
                    Dim commonMapResource As ESRI.ArcGIS.ADF.Web.DataSources.IMapResource = TryCast(mapResourceItem.Resource, ESRI.ArcGIS.ADF.Web.DataSources.IMapResource)
                    If commonMapResource Is Nothing Then
                        Continue For
                    End If

                    ' If the resource does not support querying, skip to the next
                    If (Not commonMapResource.SupportsFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality))) Then
                        Continue For
                    End If

                    ' Create a query functoinality form the resource
                    Dim commonQueryFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = TryCast(commonMapResource.CreateFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), System.Guid.NewGuid().ToString("N")), ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)

                    ' Get arrays containing the resources queryable layer names and IDs
                    Dim layerIDs() As String = Nothing
                    Dim layerNames() As String = Nothing
                    commonQueryFunctionality.GetQueryableLayers(Nothing, layerIDs, layerNames)

                    ' Add the arrays to the layer names and IDs dictionaries, along with the name of the current resource item
                    layerNamesByResources.Add(mapResourceItem.Name, layerNames)
                    layerIDsByResources.Add(mapResourceItem.Name, layerIDs)
                Next i
            Catch
            Finally
                ' If the MapResourceManager was initialized in this method, call Dispose to restore it to its initial state
                If initializedResourceManager Then
                    mapResourceManager.Dispose()
                End If
            End Try

            Return
        End Sub

#Region "Task Results Setup Methods"

        ' Creates a TaskResultNode customized for use with a TaskResultsPanel
        Private Function CreateCustomTaskResultsNode(ByVal graphicsDataSet As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet) As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode
            ' Instantiate a TaskResultNode
            Dim taskResultNode As New ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode()

            ' Set the node's text to be the name of the passed-in dataset
            taskResultNode.Text = graphicsDataSet.DataSetName

            ' Iterate through the tables in the dataset, creating a node for each
            For i As Integer = 0 To graphicsDataSet.Tables.Count - 1
                ' Check whether the current table contains data for a GraphicsLayer
                If TypeOf graphicsDataSet.Tables(i) Is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer Then
                    ' Create a GraphicsLayerNode with the data from the current table.  Make it so the node only
                    ' contains the legend swatch.
                    Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(Me.TaskResultsInstance.CreateDataTableNode(graphicsDataSet.Tables(i), False, True, False, graphicsDataSet.Tables(i).TableName, graphicsDataSet.Tables(i).TableName), ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)

                    ' Associate a custom context menu with the node.  This allows opening the associated TaskResultsPanel.
                    Me.SetupContextMenu(graphicsLayerNode)

                    ' Add the node to the buddied TaskResults control
                    taskResultNode.Nodes.Add(graphicsLayerNode)
                Else
                    ' Since the table does not represent a GraphicsLayer, create a node that includes the table's data
                    Dim treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = TaskResultsInstance.CreateDataTableNode(graphicsDataSet.Tables(i), True, True, True, graphicsDataSet.Tables(i).TableName, graphicsDataSet.Tables(i).TableName)

                    ' Add the node to the TaskResults control
                    taskResultNode.Nodes.Add(treeViewPlusNode)
                End If
            Next i

            ' Call SetupTaskResultNode to initialize the functionality of the parent node
            TaskResultsInstance.SetupTaskResultNode(Me, _taskFrameworkJobID, Me.TaskInput, taskResultNode)

            Return taskResultNode
        End Function

        ' Creates a TaskResultsPanel with the passed-in ID and title
        Private Function CreateTaskResultsPanel(ByVal ID As String, ByVal title As String) As FindNearTask_VBNet.TaskResultsPanel
            ' Initialize the TaskResultsPanel
            Dim taskResultsPanel As New FindNearTask_VBNet.TaskResultsPanel()
            taskResultsPanel.ID = ID
            taskResultsPanel.Visible = False
            taskResultsPanel.CopyAppearance(Me)
            taskResultsPanel.Style(System.Web.UI.HtmlTextWriterStyle.Position) = "absolute"
            taskResultsPanel.Style(System.Web.UI.HtmlTextWriterStyle.Left) = "200px"
            taskResultsPanel.Style(System.Web.UI.HtmlTextWriterStyle.Top) = "200px"
            taskResultsPanel.ExpandCollapseButton = True
            taskResultsPanel.WidthResizable = True
            taskResultsPanel.HeightResizable = True
            taskResultsPanel.Title = title
            taskResultsPanel.Docked = False
            taskResultsPanel.InitialMaxHeight = New System.Web.UI.WebControls.Unit(300, System.Web.UI.WebControls.UnitType.Pixel)
            taskResultsPanel.InitialMaxWidth = New System.Web.UI.WebControls.Unit(500, System.Web.UI.WebControls.UnitType.Pixel)

            ' Add the panel to the task's controls collection
            Me.Controls.Add(taskResultsPanel)

            ' Since we are adding the taskResultsPanel dynamically at run time, script must be created and
            ' returned to the client that initializes the panel client-side.  InitializeOnClient creates
            ' this script and adds it to the panel as a callback result.
            taskResultsPanel.InitializeOnClient(Me, Me.CallbackFunctionString)
            Return taskResultsPanel
        End Function

        ' Instantiates and initializes the context menu to show on task results if results are being displayed in a 
        ' TaskResultsPanel.
        Private Sub CreateTaskResultsPanelContextMenu()
            ' Instantiate and initialize the context menu
            _graphicsLayerContextMenu = New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenu()
            _graphicsLayerContextMenu.ID = "graphicsLayerContextMenu"
            _graphicsLayerContextMenu.BorderColor = System.Drawing.Color.Silver
            _graphicsLayerContextMenu.BorderStyle = System.Web.UI.WebControls.BorderStyle.Solid
            _graphicsLayerContextMenu.BorderWidth = New System.Web.UI.WebControls.Unit(1, System.Web.UI.WebControls.UnitType.Pixel)
            _graphicsLayerContextMenu.HoverColor = System.Drawing.Color.Gainsboro
            _graphicsLayerContextMenu.BackColor = System.Drawing.Color.White
            _graphicsLayerContextMenu.ForeColor = ForeColor
            _graphicsLayerContextMenu.Font.CopyFrom(Me.Font)
            _graphicsLayerContextMenu.UseDefaultWebResources = Me.UseDefaultWebResources

            ' Wire item clicked and menu dismissed event handlers
            AddHandler _graphicsLayerContextMenu.ItemClicked, AddressOf GraphicsLayerContextMenu_ItemClicked
            AddHandler _graphicsLayerContextMenu.Dismissed, AddressOf GraphicsLayerContextMenu_Dismissed

            ' Add a menu item to zoom to selected features
            Dim contextMenuItem As New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem()
            contextMenuItem.ImageUrl = ResourceUtility.GetImage("contextMenuZoomTo.gif", Me, GetType(FindNearTask))
            contextMenuItem.Text = "Zoom To Selected Features"
            _graphicsLayerContextMenu.Items.Add(contextMenuItem)

            ' Add a menu item to remove the GraphicsLayer corresponding to the node on which the context menu was shown
            contextMenuItem = New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem()
            contextMenuItem.ImageUrl = ResourceUtility.GetImage("contextMenuRemove.gif", Me, GetType(FindNearTask))
            contextMenuItem.Text = "Remove"
            _graphicsLayerContextMenu.Items.Add(contextMenuItem)

            ' Add a menu item to show the corresponding TaskResultsPanel
            contextMenuItem = New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem()
            contextMenuItem.ImageUrl = ResourceUtility.GetImage("contextMenuViewTable.gif", Me, GetType(FindNearTask))
            contextMenuItem.Text = "View Attribute Table"
            _graphicsLayerContextMenu.Items.Add(contextMenuItem)

            ' Add the context menu to the task's controls collection
            Me.Controls.Add(_graphicsLayerContextMenu)
        End Sub

        ' Method that hooks up the context menu to graphics layer node.
        Private Sub SetupContextMenu(ByVal graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)
            TaskResultsInstance.SetupContextMenu(_graphicsLayerContextMenu, graphicsLayerNode)
        End Sub

#End Region

#Region "User Interface Creation Methods"

        ' Instantiates and populates FindNearTask's toolbar
        Private Sub CreateTaskToolbar()
            ' Instantiate the toolbar and add it to the cell
            _taskToolbar = New ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar()

            ' Initialize toolbar properties
            _taskToolbar.UseDefaultWebResources = Me.UseDefaultWebResources
            _taskToolbar.ID = "FindNearTaskToolbar"
            _taskToolbar.WebResourceLocation = Me.WebResourceLocation
            _taskToolbar.BuddyControlType = ESRI.ArcGIS.ADF.Web.UI.WebControls.BuddyControlType.Map
            _taskToolbar.ToolbarStyle = ESRI.ArcGIS.ADF.Web.UI.WebControls.ToolbarStyle.ImageOnly
            _taskToolbar.Height = New System.Web.UI.WebControls.Unit("32px")
            _taskToolbar.Width = New System.Web.UI.WebControls.Unit("192px")

            ' Create the add search point tool and add it to the toolbar
            _taskToolbar.ToolbarItems.Add(CreateSearchPointTool())

            ' Create the add search polyline tool and add it to the toolbar
            _taskToolbar.ToolbarItems.Add(CreateSearchPolylineTool())

            ' Create the add search polygon tool and add it to the toolbar
            _taskToolbar.ToolbarItems.Add(CreateSearchPolygonTool())

            ' Create the select input by task results tool and add it to the toolbar
            _taskToolbar.ToolbarItems.Add(CreateInputByTaskResultTool())

            ' Create the select input by map tool and add it to the toolbar
            _taskToolbar.ToolbarItems.Add(CreateInputByMapTool())

            ' Create the clear input tool and add it to the toolbar
            _taskToolbar.ToolbarItems.Add(CreateClearInputCommand())

            ' Get the names of the toolbar groups buddied to the map
            Dim toolbarGroupNamesArray() As String = ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar.FindToolbarGroupsBuddiedTo(MapInstance.UniqueID, Page)

            ' If the map is not buddied to any toolbar groups, create a new toolbar group for it.  Otherwise, use an
            ' existing one.
            If toolbarGroupNamesArray Is Nothing OrElse toolbarGroupNamesArray.Length < 1 Then
                _taskToolbar.Group = String.Format("{0}_FindNearTaskToolbarGroup", MapInstance.UniqueID)
            Else
                _taskToolbar.Group = toolbarGroupNamesArray(0)
            End If

            ' Encapsulate the map in a BuddyControl and add it to the toolbar's buddy control collection
            Dim mapBuddyControl As New ESRI.ArcGIS.ADF.Web.UI.WebControls.BuddyControl(MapInstance.UniqueID)
            _taskToolbar.BuddyControls.Add(mapBuddyControl)
        End Sub

        ' Creates the tool to define search area by drawing a point
        Private Function CreateSearchPointTool() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool
            Dim addSearchPointTool As New ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool()
            addSearchPointTool.Name = "SearchAreaByPoint"
            addSearchPointTool.ServerActionAssembly = "FindNearTask_VBNet"
            addSearchPointTool.ServerActionClass = "FindNearTask_VBNet.SearchAreaByPoint"
            addSearchPointTool.ToolTip = "Draw a point to search near"
            addSearchPointTool.ClientAction = ESRI.ArcGIS.ADF.Web.UI.WebControls.MapClientToolAction.Point.ToString()
            addSearchPointTool.DefaultImage = ResourceUtility.GetImage("point.gif", Me, GetType(FindNearTask))
            addSearchPointTool.SelectedImage = ResourceUtility.GetImage("point_selected.gif", Me, GetType(FindNearTask))
            addSearchPointTool.HoverImage = ResourceUtility.GetImage("point_hover.gif", Me, GetType(FindNearTask))
            Return addSearchPointTool
        End Function

        ' Creates the tool to define search area by drawing a polyline
        Private Function CreateSearchPolylineTool() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool
            Dim addSearchPolylineTool As New ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool()
            addSearchPolylineTool.Name = "SearchAreaByLine"
            addSearchPolylineTool.ServerActionAssembly = "FindNearTask_VBNet"
            addSearchPolylineTool.ServerActionClass = "FindNearTask_VBNet.AddSearchPolyline"
            addSearchPolylineTool.ToolTip = "Draw a polyline to search near"
            addSearchPolylineTool.ClientAction = ESRI.ArcGIS.ADF.Web.UI.WebControls.MapClientToolAction.Polyline.ToString()
            addSearchPolylineTool.DefaultImage = ResourceUtility.GetImage("polyline.gif", Me, GetType(FindNearTask))
            addSearchPolylineTool.SelectedImage = ResourceUtility.GetImage("polyline_selected.gif", Me, GetType(FindNearTask))
            addSearchPolylineTool.HoverImage = ResourceUtility.GetImage("polyline_hover.gif", Me, GetType(FindNearTask))
            Return addSearchPolylineTool
        End Function

        ' Creates the tool to define search area by drawing a polygon
        Private Function CreateSearchPolygonTool() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool
            Dim addSearchPolygonTool As New ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool()
            addSearchPolygonTool.Name = "SearchAreaByPolygon"
            addSearchPolygonTool.ServerActionAssembly = "FindNearTask_VBNet"
            addSearchPolygonTool.ServerActionClass = "FindNearTask_VBNet.AddSearchPolygon"
            addSearchPolygonTool.ToolTip = "Draw a polygon to search near"
            addSearchPolygonTool.ClientAction = ESRI.ArcGIS.ADF.Web.UI.WebControls.MapClientToolAction.Polygon.ToString()
            addSearchPolygonTool.DefaultImage = ResourceUtility.GetImage("polygon.gif", Me, GetType(FindNearTask))
            addSearchPolygonTool.SelectedImage = ResourceUtility.GetImage("polygon_selected.gif", Me, GetType(FindNearTask))
            addSearchPolygonTool.HoverImage = ResourceUtility.GetImage("polygon_hover.gif", Me, GetType(FindNearTask))
            Return addSearchPolygonTool
        End Function

        ' Creates the tool to define search area by selecting a task result
        Private Function CreateInputByTaskResultTool() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool
            Dim selectInputByTaskResultTool As New ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool()
            selectInputByTaskResultTool.Name = "SearchAreaByTaskResult"
            selectInputByTaskResultTool.ServerActionAssembly = "FindNearTask_VBNet"
            selectInputByTaskResultTool.ServerActionClass = "FindNearTask_VBNet.SearchAreaByTaskResult"
            selectInputByTaskResultTool.ToolTip = "Select a task result to search near"
            selectInputByTaskResultTool.ClientAction = ESRI.ArcGIS.ADF.Web.UI.WebControls.MapClientToolAction.Point.ToString()
            selectInputByTaskResultTool.DefaultImage = ResourceUtility.GetImage("selectTaskResult.gif", Me, GetType(FindNearTask))
            selectInputByTaskResultTool.SelectedImage = ResourceUtility.GetImage("selectTaskResult_selected.gif", Me, GetType(FindNearTask))
            selectInputByTaskResultTool.HoverImage = ResourceUtility.GetImage("selectTaskResult_hover.gif", Me, GetType(FindNearTask))
            Return selectInputByTaskResultTool
        End Function

        ' Creates the tool to define search area by selecting features from the map
        Private Function CreateInputByMapTool() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool
            Dim selectInputByMapTool As New ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool()
            selectInputByMapTool.Name = "SearchAreaBySelection"
            selectInputByMapTool.ServerActionAssembly = "FindNearTask_VBNet"
            selectInputByMapTool.ServerActionClass = "FindNearTask_VBNet.SearchAreaBySelection"
            selectInputByMapTool.ToolTip = "Select features to search near"
            selectInputByMapTool.ClientAction = ESRI.ArcGIS.ADF.Web.UI.WebControls.MapClientToolAction.DragRectangle.ToString()
            selectInputByMapTool.DefaultImage = ResourceUtility.GetImage("selectFeature.gif", Me, GetType(FindNearTask))
            selectInputByMapTool.SelectedImage = ResourceUtility.GetImage("selectFeature_selected.gif", Me, GetType(FindNearTask))
            selectInputByMapTool.HoverImage = ResourceUtility.GetImage("selectFeature_hover.gif", Me, GetType(FindNearTask))
            Return selectInputByMapTool
        End Function

        ' Creates the command for clearing task input geometry from the map
        Private Function CreateClearInputCommand() As ESRI.ArcGIS.ADF.Web.UI.WebControls.Command
            Dim clearInputCommand As New ESRI.ArcGIS.ADF.Web.UI.WebControls.Command()
            clearInputCommand.Name = "ClearInput"
            clearInputCommand.ServerActionAssembly = "FindNearTask_VBNet"
            clearInputCommand.ServerActionClass = "FindNearTask_VBNet.ClearInput"
            clearInputCommand.ToolTip = "Clear search area features and buffers from the map"
            clearInputCommand.DefaultImage = ResourceUtility.GetImage("clearInput.gif", Me, GetType(FindNearTask))
            clearInputCommand.SelectedImage = ResourceUtility.GetImage("clearInput_selected.gif", Me, GetType(FindNearTask))
            clearInputCommand.HoverImage = ResourceUtility.GetImage("clearInput_hover.gif", Me, GetType(FindNearTask))
            Return clearInputCommand
        End Function

        ' Creates the controls for specifying the layer to select features from for task input
        Private Sub CreateSelectionLayerControls()
            _selectionLayerLabel = New System.Web.UI.WebControls.Label()
            _selectionLayerLabel.ID = "SelectionLayerLabel"
            _selectionLayerLabel.Text = "Selection Layer:"
            _selectionLayerLabel.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            _selectionLayerLabel.Style(System.Web.UI.HtmlTextWriterStyle.PaddingRight) = "5px"
            _selectionLayerLabel.Style(System.Web.UI.HtmlTextWriterStyle.Display) = "none"

            _selectionLayerDropDownList = New System.Web.UI.WebControls.DropDownList()
            _selectionLayerDropDownList.ID = "SelectionLayerDropDownList"
            _selectionLayerDropDownList.Style(System.Web.UI.HtmlTextWriterStyle.Display) = "none"
        End Sub

        ' Creates the checkbox determining whether newly defined search areas are merged with those previously defined
        Private Function CreateAddToInputCheckbox() As System.Web.UI.WebControls.CheckBox
            Dim addToInputCheckbox As New System.Web.UI.WebControls.CheckBox()
            addToInputCheckbox.ID = "AddToInputCheckbox"
            addToInputCheckbox.Text = "Add New Search Areas to Previous"
            addToInputCheckbox.Checked = True
            addToInputCheckbox.Font.Bold = False
            addToInputCheckbox.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            addToInputCheckbox.Attributes("onclick") = "addToInputClicked()"
            Return addToInputCheckbox
        End Function

        ' Creates the label for the search distance textbox
        Private Function CreateSearchDistanceLabel() As System.Web.UI.WebControls.Label
            Dim label As New System.Web.UI.WebControls.Label()
            label.Text = "Search Distance:"
            label.Style(System.Web.UI.HtmlTextWriterStyle.PaddingRight) = "5px"
            label.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            Return label
        End Function

        ' Creates and populates the search distance textbox and units drop-down list
        Private Sub CreateSearchDistanceControls()
            ' Instantiate the search distance textbox
            _searchDistanceTextBox = New System.Web.UI.WebControls.TextBox()
            _searchDistanceTextBox.ID = "SearchDistanceTextBox"
            _searchDistanceTextBox.Width = New System.Web.UI.WebControls.Unit(75, System.Web.UI.WebControls.UnitType.Pixel)

            ' If the search distance has been defined, use it to initialize the textbox.  Otherwise,
            ' leave it empty.
            _searchDistanceTextBox.Text = If(Single.IsNaN(TaskInput.SearchDistance), String.Empty, TaskInput.SearchDistance.ToString())
            _searchDistanceTextBox.Attributes.Add("onclick", "updateSearchArea()")
            _searchDistanceTextBox.Attributes.Add("onchange", "updateSearchArea()")

            ' Get the map functionality for the primary map resource
            Dim primaryResourceMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = Me.MapInstance.GetFunctionality(MapInstance.PrimaryMapResource)
            ' Set the task's input units to those of the primary map resource
            If Me.TaskInput.Units Is Nothing Then
                Me.TaskInput.Units = primaryResourceMapFunctionality.Units.ToString()
            End If

            _unitsDropDownList = New System.Web.UI.WebControls.DropDownList()
            _unitsDropDownList.ID = "UnitsDropDownList"

            ' Add all the ArcGIS Server unit types to the units drop-down list except for unknown, points, 
            ' and decimal degress
            Dim adfUnitArray As System.Array = System.Enum.GetValues(GetType(ESRI.ArcGIS.ADF.Web.DataSources.Units))
            Dim selectedIndex As Integer = 0
            Dim dropDownIndex As Integer = -1
            For Each i As Integer In adfUnitArray
                Dim unit As String = adfUnitArray.GetValue(i).ToString()
                If (unit <> "Unknown") AndAlso (unit <> "DecimalDegrees") AndAlso (unit <> "Points") Then
                    _unitsDropDownList.Items.Add(unit)
                    dropDownIndex += 1
                End If

                If unit = Me.TaskInput.Units Then
                    selectedIndex = dropDownIndex
                End If
            Next i
            _unitsDropDownList.SelectedIndex = selectedIndex

            ' Specify JavaScript functions to call when the drop-down list is clicked and a new item is selected
            _unitsDropDownList.Attributes.Add("onchange", "updateSearchArea()")
        End Sub

        ' Creates the label for the search layer drop-down list
        Private Function CreateSearchLayerLabel() As System.Web.UI.WebControls.Label
            Dim label As New System.Web.UI.WebControls.Label()
            label.ID = "SearchLayerLabel"
            label.Text = "Search Layer:"
            label.Style(System.Web.UI.HtmlTextWriterStyle.WhiteSpace) = "nowrap"
            label.Style(System.Web.UI.HtmlTextWriterStyle.PaddingRight) = "5px"
            Return label
        End Function

        ' Creates and populates the search layer drop-down list
        Private Sub CreateSearchLayerDropDownList()
            ' Instantiate the search layer drop-down list
            _searchLayerDropDownList = New System.Web.UI.WebControls.DropDownList()
            _searchLayerDropDownList.ID = "SearchLayerDropDownList"

            ' Wire the JavaScript function updateSearchLayer to the list's onchange event
            _searchLayerDropDownList.Attributes.Add("onchange", "updateSearchLayer(this);")

            ' Make sure the search layer hasn't already been initialized before adding layers to the drop-down list
            If Me.TaskInput.SearchLayer Is Nothing Then
                ' Dictionaries to hold the names and IDs of queryable layers
                Dim layerNamesByResourceDictionary As System.Collections.Generic.Dictionary(Of String, String())
                Dim layerIDsByResourceDictionary As System.Collections.Generic.Dictionary(Of String, String())

                ' Get the names and IDs of all the queryable layers referenced by the buddied map resource manager
                Me.GetQueryableLayers(MapInstance.MapResourceManagerInstance, layerNamesByResourceDictionary, layerIDsByResourceDictionary)

                ' Loop through all the items in the layer names dictionary, adding each to the search layer and 
                ' selection layer lists
                For Each layerNamesPair As System.Collections.Generic.KeyValuePair(Of String, String()) In layerNamesByResourceDictionary
                    ' Get the name of the resource from the current key value pair
                    Dim resourceName As String = layerNamesPair.Key

                    ' Get the array of layer names for the current resource
                    Dim layerNamesArray() As String = layerNamesPair.Value

                    ' Loop through the names, creating a tree view plus node below the current resource node for each
                    For i As Integer = 0 To layerNamesArray.Length - 1
                        ' Get the layer ID for the current layer
                        Dim layerID As String = layerIDsByResourceDictionary(resourceName)(i)

                        If _searchLayerDropDownList.Items.Count = 0 Then
                            ' Set the layer and resource input parameters based on the current node
                            TaskInput.SearchResource = resourceName
                            TaskInput.SearchLayer = layerID
                        End If

                        ' Create an item for the current layer and add it to the selection layer and search layer 
                        ' drop-down lists.  Specify the item's text as the layer name, and the item's value as the
                        ' name of the resource containing the layer and the layer ID, delimited by a comma.
                        Dim layerListItem As New System.Web.UI.WebControls.ListItem()
                        layerListItem.Text = layerNamesArray(i)
                        layerListItem.Value = String.Format("{0},{1}", resourceName, layerID)
                        _selectionLayerDropDownList.Items.Add(layerListItem)

                        _searchLayerDropDownList.Items.Add(layerListItem)
                    Next i
                Next layerNamesPair
            End If
        End Sub

        ' Creates the Find button
        Private Sub CreateFindButton()
            ' Instantiate the Find button and specify its text
            _findButton = New System.Web.UI.HtmlControls.HtmlInputButton()
            _findButton.ID = "FindButton"
            _findButton.Value = "Find"

            ' Construct JavaScript to execute the task
            Dim executeTaskJavaScript As String = String.Format("" & ControlChars.CrLf & "                executeTask('',""{0}"");", Me.CallbackFunctionString, _taskToolbar.ClientID, MapInstance.ClientID)

            ' Wire the JavaScript to execute when the Find button is clicked
            _findButton.Attributes.Add("onclick", executeTaskJavaScript)
        End Sub

        ' Creates the activity indicator div, which contains the image and label to be shown when the search 
        ' area is being updated
        Private Sub CreateActivityIndicatorDiv()
            ' Create a div to hold the activity indicator image and label
            _activityIndicatorDiv = New System.Web.UI.HtmlControls.HtmlGenericControl("div")
            _activityIndicatorDiv.ID = "ActivityIndicatorDiv"
            _activityIndicatorDiv.Style(System.Web.UI.HtmlTextWriterStyle.Display) = "none"

            ' Create an HTML image control and initialize it with the activity indicator gif
            Dim activityIndicator As New System.Web.UI.HtmlControls.HtmlImage()
            Dim activityIndicatorUrl As String = ESRI.ArcGIS.ADF.Web.UI.WebControls.ResourceUtility.GetImage("callbackActivityIndicator.gif", Me, GetType(ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanel), "Runtime")
            activityIndicator.Src = activityIndicatorUrl
            activityIndicator.Style(System.Web.UI.HtmlTextWriterStyle.PaddingRight) = "5px"
            ' Add the image to the div
            _activityIndicatorDiv.Controls.Add(activityIndicator)

            ' Create the activity label and add it to the div
            Dim label As New System.Web.UI.WebControls.Label()
            label.Font.Bold = False
            label.ForeColor = System.Drawing.Color.Gray
            label.Font.Italic = True
            label.Text = "Updating Search Area..."
            _activityIndicatorDiv.Controls.Add(label)
        End Sub

#End Region

#Region "GeoSpatial Utility Methods"

        ' Calculates the buffer of the input geometries
        Private Function BufferGeometries(ByVal adfInputGeometryArray() As ESRI.ArcGIS.ADF.Web.Geometry.Geometry, ByVal bufferDistance As Single, ByVal units As String) As ESRI.ArcGIS.ADF.Web.Geometry.Geometry
            ' Put the ArcGIS Server SOAP polygon in an ArcGIS Server SOAP geometry array to pass to the
            ' buffer operation
            Dim agsSoapInputGeometryArray(adfInputGeometryArray.Length - 1) As ESRI.ArcGIS.ADF.ArcGISServer.Geometry
            For i As Integer = 0 To adfInputGeometryArray.Length - 1
                agsSoapInputGeometryArray(i) = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.FromAdfGeometry(adfInputGeometryArray(i))
            Next i

            ' Get a reference to an ArcGIS Server Geometry service
            Dim geometryServerProxy As New ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy(Me.GeometryServiceUrl)

            ' Get a spatial reference in which to perform the buffer operation.  This spatial reference
            ' is explicitly intialized based on the user-drawn polygon so that there is minimal projection
            ' related buffer distortion
            Dim agsSoapBufferSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = Me.CreateOperationSpatialReference(agsSoapInputGeometryArray, geometryServerProxy)

            ' Use the units specified by the user to create an ArcGIS Server LinearUnit object
            Dim agsSoapBufferUnits As New ESRI.ArcGIS.ADF.ArcGISServer.LinearUnit()
            agsSoapBufferUnits.WKID = Me.GetWkidByUnitName(units)
            agsSoapBufferUnits.WKIDSpecified = True

            ' Get the user-specified buffer distance and put it in an array to pass to the buffer
            ' operation.  If the user did not specify a distance, initialize the distance to zero.
            Dim bufferDistances(0) As Double
            bufferDistances(0) = System.Convert.ToDouble(bufferDistance)

            Dim agsSoapInputSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = Me.GetSpatialReference(agsSoapInputGeometryArray(0))

            ' Execute the buffer operation via the geometry service  
            Dim agsBufferGeometryArray() As ESRI.ArcGIS.ADF.ArcGISServer.Geometry = geometryServerProxy.Buffer(agsSoapInputSpatialReference, agsSoapBufferSpatialReference, agsSoapInputSpatialReference, bufferDistances, agsSoapBufferUnits, True, agsSoapInputGeometryArray)

            ' Retrieve the buffer polygon from the array of result geometries
            Dim adfBufferGeometry As ESRI.ArcGIS.ADF.Web.Geometry.Geometry = ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.ToAdfGeometry(agsBufferGeometryArray(0))

            If adfBufferGeometry.SpatialReference Is Nothing Then
                adfBufferGeometry.SpatialReference = adfInputGeometryArray(0).SpatialReference
            End If

            Return adfBufferGeometry
        End Function

        ' Returns the well-known ID of the passed-in unit
        Private Function GetWkidByUnitName(ByVal unitName As String) As Integer
            Dim wkid As Integer = -1
            Select Case unitName
                Case "Inches"
                    wkid = 109009
                Case "Feet"
                    wkid = 9003
                Case "Yards"
                    wkid = 109002
                Case "Miles"
                    wkid = 9035
                Case "NauticalMiles"
                    wkid = 9030
                Case "Millimeters"
                    wkid = 109007
                Case "Centimeters"
                    wkid = 109006
                Case "Meters"
                    wkid = 9001
                Case "Kilometers"
                    wkid = 9036
                Case "Decimeters"
                    wkid = 109005
                Case Else
                    ' default is Kilometers
                    wkid = 9036
            End Select

            Return wkid
        End Function

        ' Creates a spatial reference that minimizes distortion in the vicinity of the passed-in geometriess
        Private Function CreateOperationSpatialReference(ByVal agsSoapGeometryArray() As ESRI.ArcGIS.ADF.ArcGISServer.Geometry, ByVal geometryServerProxy As ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy) As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference
            ' Get the polygon's minimum enclosing rectangle (MER)
            Dim agsSoapBoundingEnvelope As ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN = GetBoundingExtent(agsSoapGeometryArray)

            ' If the input polygon's spatial reference uses a projected coordinate system, project the MER to a 
            ' geographic coordinate system (WGS 1984 in this case).  We do this because the MER's coordinates 
            ' will be used to initialize the datum of the operation spatial reference
            Dim agsSoapSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = Me.GetSpatialReference(agsSoapGeometryArray(0))
            If TypeOf agsSoapSpatialReference Is ESRI.ArcGIS.ADF.ArcGISServer.ProjectedCoordinateSystem Then
                ' Create an ArcGIS Server spatial reference initalized to use the WGS 1984 coordinate system
                Dim agsSoapGeographicSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = New ESRI.ArcGIS.ADF.ArcGISServer.GeographicCoordinateSystem()
                agsSoapGeographicSpatialReference.WKID = 4326
                agsSoapGeographicSpatialReference.WKIDSpecified = True

                ' Place the input MER in an array for the project operation
                Dim agsSoapInputGeometryArray(0) As ESRI.ArcGIS.ADF.ArcGISServer.Geometry
                agsSoapInputGeometryArray(0) = agsSoapBoundingEnvelope

                ' Execute the projection
                Dim agsSoapOutputGeometryArray() As ESRI.ArcGIS.ADF.ArcGISServer.Geometry = geometryServerProxy.Project(agsSoapSpatialReference, agsSoapGeographicSpatialReference, True, Nothing, Nothing, agsSoapInputGeometryArray)

                ' Retrieve the projected MER from the results array
                agsSoapBoundingEnvelope = TryCast(agsSoapOutputGeometryArray(0), ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN)
            End If

            ' Get the latitude (Y coordinate) at the center of the MER
            Dim centerLatitude As Double = agsSoapBoundingEnvelope.YMax - (agsSoapBoundingEnvelope.YMax - agsSoapBoundingEnvelope.YMin) / 2

            ' Create the definition string for the operation spatial reference's coordinate system.  We will use 
            ' the Hotine Oblique Mercator coordinate system because it lends itself well to minimizing operational
            ' distortion anywhere on the earth
            Dim hotineObliqueMercatorDefinition As String = "" & ControlChars.CrLf & "            PROJCS[""World_Hotine""," & ControlChars.CrLf & "            GEOGCS[""GCS_WGS_1984""," & ControlChars.CrLf & "            DATUM[""D_WGS_1984""," & ControlChars.CrLf & "            SPHEROID[""WGS_1984"",6378137.0,298.257223563]]," & ControlChars.CrLf & "            PRIMEM[""Greenwich"",0.0]," & ControlChars.CrLf & "            UNIT[""Degree"",0.0174532925199433]]," & ControlChars.CrLf & "            PROJECTION[""Hotine_Oblique_Mercator_Two_Point_Natural_Origin""]," & ControlChars.CrLf & "            PARAMETER[""False_Easting"",0.0]," & ControlChars.CrLf & "            PARAMETER[""False_Northing"",0.0]," & ControlChars.CrLf & "            PARAMETER[""Latitude_Of_1st_Point"",{0}],   " & ControlChars.CrLf & "            PARAMETER[""Latitude_Of_2nd_Point"",{1}]," & ControlChars.CrLf & "            PARAMETER[""Scale_Factor"",1.0]," & ControlChars.CrLf & "            PARAMETER[""Longitude_Of_1st_Point"",{2}]," & ControlChars.CrLf & "            PARAMETER[""Longitude_Of_2nd_Point"",{3}]," & ControlChars.CrLf & "            PARAMETER[""Latitude_Of_Center"",{4}]," & ControlChars.CrLf & "            UNIT[""Meter"",1.0]]"

            ' Specify the relevant coordinates of the MER for the coordinate system's datum parameters
            Dim customHotineObliqueCylindricalMercator As String = String.Format(hotineObliqueMercatorDefinition, agsSoapBoundingEnvelope.YMin, agsSoapBoundingEnvelope.YMax, agsSoapBoundingEnvelope.XMin, agsSoapBoundingEnvelope.XMax, centerLatitude)

            ' Create the spatial reference
            Dim agsSoapBufferSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = geometryServerProxy.FindSRByWKT(customHotineObliqueCylindricalMercator, Nothing, True, True)

            Return agsSoapBufferSpatialReference
        End Function

        ' Returns the spatial reference of an ArcGIS Server SOAP Geometry
        Private Function GetSpatialReference(ByVal agsSoapGeometry As ESRI.ArcGIS.ADF.ArcGISServer.Geometry) As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference
            Dim agsSoapSpatialReference As ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference = Nothing
            If TypeOf agsSoapGeometry Is ESRI.ArcGIS.ADF.ArcGISServer.PolygonN Then
                Dim agsSoapPolygon As ESRI.ArcGIS.ADF.ArcGISServer.PolygonN = TryCast(agsSoapGeometry, ESRI.ArcGIS.ADF.ArcGISServer.PolygonN)
                agsSoapSpatialReference = agsSoapPolygon.SpatialReference
            ElseIf TypeOf agsSoapGeometry Is ESRI.ArcGIS.ADF.ArcGISServer.PolylineN Then
                Dim agsSoapPolyline As ESRI.ArcGIS.ADF.ArcGISServer.PolylineN = TryCast(agsSoapGeometry, ESRI.ArcGIS.ADF.ArcGISServer.PolylineN)
                agsSoapSpatialReference = agsSoapPolyline.SpatialReference
            ElseIf TypeOf agsSoapGeometry Is ESRI.ArcGIS.ADF.ArcGISServer.PointN Then
                Dim agsSoapPoint As ESRI.ArcGIS.ADF.ArcGISServer.PointN = TryCast(agsSoapGeometry, ESRI.ArcGIS.ADF.ArcGISServer.PointN)
                agsSoapSpatialReference = agsSoapPoint.SpatialReference
            End If

            Return agsSoapSpatialReference
        End Function

        ' Gets the bounding extent of the passed-in ArcGIS Server SOAP geometries
        Private Function GetBoundingExtent(ByVal agsSoapGeometryArray() As ESRI.ArcGIS.ADF.ArcGISServer.Geometry) As ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN
            ' Instantiate an envelope with max values minimized and min values maximized
            Dim agsSoapBoundingBox As New ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN()
            agsSoapBoundingBox.XMin = Double.MaxValue
            agsSoapBoundingBox.XMax = Double.MinValue
            agsSoapBoundingBox.YMin = Double.MaxValue
            agsSoapBoundingBox.YMax = Double.MinValue

            For i As Integer = 0 To agsSoapGeometryArray.Length - 1
                Dim agsSoapGeometry As ESRI.ArcGIS.ADF.ArcGISServer.Geometry = agsSoapGeometryArray(i)
                If TypeOf agsSoapGeometry Is ESRI.ArcGIS.ADF.ArcGISServer.PolygonN Then
                    Dim agsSoapPolygon As ESRI.ArcGIS.ADF.ArcGISServer.PolygonN = TryCast(agsSoapGeometry, ESRI.ArcGIS.ADF.ArcGISServer.PolygonN)
                    ' Iterate through all the polygon's vertices
                    For j As Integer = 0 To agsSoapPolygon.RingArray.Length - 1
                        Dim agsSoapRing As ESRI.ArcGIS.ADF.ArcGISServer.Ring = agsSoapPolygon.RingArray(j)
                        For k As Integer = 0 To agsSoapRing.PointArray.Length - 1
                            ' For each vertex, expand the bounds of the minimum enclosing rectangle with the 
                            ' vertex's coordinates if they fall outside the rectangle's current bounds
                            Dim agsSoapCurrentPoint As ESRI.ArcGIS.ADF.ArcGISServer.PointN = TryCast(agsSoapRing.PointArray(k), ESRI.ArcGIS.ADF.ArcGISServer.PointN)

                            If agsSoapCurrentPoint.X < agsSoapBoundingBox.XMin Then
                                agsSoapBoundingBox.XMin = agsSoapCurrentPoint.X
                            ElseIf agsSoapCurrentPoint.X > agsSoapBoundingBox.XMax Then
                                agsSoapBoundingBox.XMax = agsSoapCurrentPoint.X
                            End If

                            If agsSoapCurrentPoint.Y < agsSoapBoundingBox.YMin Then
                                agsSoapBoundingBox.YMin = agsSoapCurrentPoint.Y
                            ElseIf agsSoapCurrentPoint.Y > agsSoapBoundingBox.YMax Then
                                agsSoapBoundingBox.YMax = agsSoapCurrentPoint.Y
                            End If
                        Next k
                    Next j
                ElseIf TypeOf agsSoapGeometry Is ESRI.ArcGIS.ADF.ArcGISServer.PolylineN Then
                    Dim agsSoapPolyline As ESRI.ArcGIS.ADF.ArcGISServer.PolylineN = TryCast(agsSoapGeometry, ESRI.ArcGIS.ADF.ArcGISServer.PolylineN)
                    ' Iterate through all the polyline's vertices
                    For j As Integer = 0 To agsSoapPolyline.PathArray.Length - 1
                        Dim agsSoapPath As ESRI.ArcGIS.ADF.ArcGISServer.Path = agsSoapPolyline.PathArray(j)
                        For k As Integer = 0 To agsSoapPath.PointArray.Length - 1
                            ' For each vertex, expand the bounds of the minimum enclosing rectangle with the 
                            ' vertex's coordinates if they fall outside the rectangle's current bounds
                            Dim agsSoapCurrentPoint As ESRI.ArcGIS.ADF.ArcGISServer.PointN = TryCast(agsSoapPath.PointArray(k), ESRI.ArcGIS.ADF.ArcGISServer.PointN)

                            If agsSoapCurrentPoint.X < agsSoapBoundingBox.XMin Then
                                agsSoapBoundingBox.XMin = agsSoapCurrentPoint.X
                            ElseIf agsSoapCurrentPoint.X > agsSoapBoundingBox.XMax Then
                                agsSoapBoundingBox.XMax = agsSoapCurrentPoint.X
                            End If

                            If agsSoapCurrentPoint.Y < agsSoapBoundingBox.YMin Then
                                agsSoapBoundingBox.YMin = agsSoapCurrentPoint.Y
                            ElseIf agsSoapCurrentPoint.Y > agsSoapBoundingBox.YMax Then
                                agsSoapBoundingBox.YMax = agsSoapCurrentPoint.Y
                            End If
                        Next k
                    Next j
                ElseIf TypeOf agsSoapGeometry Is ESRI.ArcGIS.ADF.ArcGISServer.PointN Then
                    Dim agsSoapPoint As ESRI.ArcGIS.ADF.ArcGISServer.PointN = TryCast(agsSoapGeometry, ESRI.ArcGIS.ADF.ArcGISServer.PointN)

                    agsSoapBoundingBox.XMin = agsSoapPoint.X - 0.1
                    agsSoapBoundingBox.XMax = agsSoapPoint.X + 0.1
                    agsSoapBoundingBox.YMin = agsSoapPoint.Y - 0.1
                    agsSoapBoundingBox.YMax = agsSoapPoint.Y + 0.1
                End If
            Next i

            Return agsSoapBoundingBox
        End Function

#End Region

#Region "TaskResultNode Utility Methods"

        ' Retrieves a GraphicsLayerNode that is an ancestor or descendant of the passed-in node, if available
        Private Function GetRelatedGraphicsLayerNode(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode) As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode
            ' Check whether the passed-in node is a GraphicsLayerNode
            Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(node, ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)

            ' Check whether the passed-in node has an ancestor GraphicsLayerNode
            If graphicsLayerNode Is Nothing Then
                graphicsLayerNode = Me.FindParentGraphicsLayerNode(node)
            End If

            ' Check whether the passed-in node has a descendant GraphicsLayerNode
            If graphicsLayerNode Is Nothing Then
                graphicsLayerNode = Me.FindChildGraphicsLayerNode(node)
            End If

            Return graphicsLayerNode
        End Function

        ' Retrieves a GraphicsLayerNode that is a descendant of the passed-in node, if available
        Private Function FindChildGraphicsLayerNode(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode) As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode
            Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(node, ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)
            If graphicsLayerNode Is Nothing AndAlso node.Nodes.Count > 0 Then
                For Each childNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In node.Nodes
                    graphicsLayerNode = Me.FindChildGraphicsLayerNode(childNode)
                    If graphicsLayerNode IsNot Nothing Then
                        Exit For
                    End If
                Next childNode
            End If

            Return graphicsLayerNode
        End Function

        ' Retrieves a GraphicsLayerNode that is an ancestor of the passed-in node, if available
        Private Function FindParentGraphicsLayerNode(ByVal node As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode) As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode
            Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(node, ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)
            If graphicsLayerNode Is Nothing AndAlso TypeOf node.Parent Is ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode Then
                graphicsLayerNode = Me.FindParentGraphicsLayerNode(TryCast(node.Parent, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode))
            End If

            Return graphicsLayerNode
        End Function

        ' Retrieves FeatureNodes that are descendants of the passed-in node if available
        Private Sub FindChildFeatureNodes(ByVal treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode, ByRef featureNodes As System.Collections.Generic.List(Of ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode))
            Dim featureNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode = TryCast(treeViewPlusNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)

            If featureNode IsNot Nothing Then
                featureNodes.Add(featureNode)
            End If

            If treeViewPlusNode.Nodes.Count > 0 Then
                For Each childNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In treeViewPlusNode.Nodes
                    Me.FindChildFeatureNodes(childNode, featureNodes)
                Next childNode
            End If

            Return
        End Sub

#End Region

#Region "Asynchronous Request Utility Methods"

        ' Retrieves the control that initiated the asynchronous request
        Private Function GetCallingControl(ByVal page As System.Web.UI.Page) As System.Web.UI.Control
            If page Is Nothing Then
                Return Nothing
            End If
            If page.IsCallback Then
                Dim controlID As String = page.Request.Params("__CALLBACKID")
                Dim control As System.Web.UI.Control = page.FindControl(controlID)
                Return control
                ' For 9.3 we could be using a partial postback instead
            ElseIf page.IsPostBack AndAlso System.Web.UI.ScriptManager.GetCurrent(page) IsNot Nothing AndAlso System.Web.UI.ScriptManager.GetCurrent(page).IsInAsyncPostBack Then
                Dim controlID As String = System.Web.UI.ScriptManager.GetCurrent(page).AsyncPostBackSourceElementID
                Dim control As System.Web.UI.Control = page.FindControl(controlID)
                Return control
            Else 'Not an asyncronous request
                Return Nothing
            End If
        End Function

        ' Copies the passed-in callback results to the callback results collection of the control that initiated
        ' the asynchronous request
        Private Sub CopyCallbackResultsToCaller(ByVal callbackResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResultCollection)
            Dim callingControl As System.Web.UI.Control = Me.GetCallingControl(Me.Page)
            If TypeOf callingControl Is ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl Then
                Dim compositeControl As ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl = TryCast(callingControl, ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl)
                compositeControl.CallbackResults.CopyFrom(callbackResults)
            ElseIf TypeOf callingControl Is ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl Then
                Dim webControl As ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl = TryCast(callingControl, ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl)
                webControl.CallbackResults.CopyFrom(callbackResults)
            End If
        End Sub

#End Region

#End Region
    End Class
End Namespace