Common Extend tasks
Common_ExtendTasks_VBNet\App_Code\ExtendQueryTask.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.
' 

Imports Microsoft.VisualBasic
Imports System
Namespace ExtendedTasks
  Public Class ExtendQueryTask
    Inherits ESRI.ArcGIS.ADF.Tasks.QueryAttributesTask
    #Region "Instance Variable Declarations"

    Private m_customContextMenuSimpleResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenu
    Private m_taskResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults = Nothing

    #End Region

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

    ' Add custom context menu to the task's controls collection
    Protected Overrides Sub CreateChildControls()
      ' Create the default task controls
      MyBase.CreateChildControls()

      ' Instantiate context menu and configure appearance
      m_customContextMenuSimpleResult = New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenu()
      m_customContextMenuSimpleResult.ID = "customContextMenu"
      m_customContextMenuSimpleResult.BorderColor = System.Drawing.Color.Silver
      m_customContextMenuSimpleResult.BorderStyle = System.Web.UI.WebControls.BorderStyle.Solid
      m_customContextMenuSimpleResult.BorderWidth = New System.Web.UI.WebControls.Unit(1, System.Web.UI.WebControls.UnitType.Pixel)
      m_customContextMenuSimpleResult.HoverColor = System.Drawing.Color.Gainsboro
      m_customContextMenuSimpleResult.BackColor = System.Drawing.Color.White
      m_customContextMenuSimpleResult.ForeColor = System.Drawing.Color.Black
      m_customContextMenuSimpleResult.UseDefaultWebResources = True
      m_customContextMenuSimpleResult.Font.Name = "Verdana"
      m_customContextMenuSimpleResult.Font.Size = New System.Web.UI.WebControls.FontUnit(8)

      ' Add handlers to fire when an item is clicked and when the menu is dismissed
      AddHandler m_customContextMenuSimpleResult.ItemClicked, AddressOf customContextMenuSimpleResult_ItemClicked
      AddHandler m_customContextMenuSimpleResult.Dismissed, AddressOf customContextMenuSimpleResult_Dismissed

      ' Add a menu item with the text "Show Alert"
      Dim showAlertContextMenuItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem = New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem()
      showAlertContextMenuItem.Text = "Show Alert"
      showAlertContextMenuItem.ImageUrl = ""
      m_customContextMenuSimpleResult.Items.Add(showAlertContextMenuItem)

      ' Add a menu item with the text "Zoom To Feature" and a picture of a wrench
      Dim zoomToFeatureContextMenuItem As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem = New ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem()
      zoomToFeatureContextMenuItem.Text = "Zoom To Feature"
      zoomToFeatureContextMenuItem.ImageUrl = "images/wrench.gif"
      m_customContextMenuSimpleResult.Items.Add(zoomToFeatureContextMenuItem)

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

    Protected Overrides Sub OnLoad(ByVal eventArgs As System.EventArgs)
      MyBase.OnLoad(eventArgs)

      ' Add an event handler that fires when a node on the associated task results container is clicked
      AddHandler TaskResultsInstance.NodeClicked, AddressOf TaskResults_NodeClicked

      ' Add an item to the custom context menu to remove nodes.  To do this, we simply use the item from the
      ' associated task results container's RemoveOnly context menu
      m_customContextMenuSimpleResult.Items.Add(TaskResultsInstance.RemoveOnlyContextMenu.Items(0))
    End Sub

    #End Region

    #Region "Web ADF WebControl Event Handlers"

    Private Sub TaskResults_NodeClicked(ByVal sender As Object, ByVal treeViewPlusNodeEventArgs As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs)
      ' Get the text of the node that was clicked
      Dim nodeText As String = treeViewPlusNodeEventArgs.Node.Text

      ' Construct a JavaScript callback result that will update the window's status bar with the text of the
      ' clicked node and the current millisecond of the server's DateTime object
      Dim jsUpdateStatusBar As String = String.Format("window.status = 'Clicked on {0} during millisecond value of {1}'", nodeText, System.DateTime.Now.Millisecond)
      Dim updateStatusBarCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsUpdateStatusBar)
      TaskResultsInstance.CallbackResults.Add(updateStatusBarCallbackResult)
    End Sub

    Private Sub customContextMenuSimpleResult_Dismissed(ByVal sender As Object, ByVal contextMenuDismissedEventArgs As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuDismissedEventArgs)
      ' Get the task results node that was clicked to display the context menu
      Dim treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = TaskResultsInstance.Nodes.FindByNodeID(contextMenuDismissedEventArgs.Context)
      If Not treeViewPlusNode Is Nothing Then
        ' Construct a JavaScript callback to unselect the node in the TaskResults container
        Dim jsUnselectResultsNode As String = String.Format("var node = document.getElementById('{0}_textCell');" & "if(node!=null){{node.style.backgroundColor='{1}';}}", treeViewPlusNode.NodeID, System.Drawing.ColorTranslator.ToHtml(TaskResultsInstance.BackColor))
        Dim unselectResultsNodeCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsUnselectResultsNode)
        m_customContextMenuSimpleResult.CallbackResults.Add(unselectResultsNodeCallbackResult)
      End If
    End Sub

    Private Sub customContextMenuSimpleResult_ItemClicked(ByVal sender As Object, ByVal contextMenuItemEventArgs As ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItemEventArgs)
      ' Get the task results node that was right-clicked to display the context menu
      Dim treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = TaskResultsInstance.Nodes.FindByNodeID(contextMenuItemEventArgs.Context)

      ' Determine which context menu item was clicked by checking the text of the passed-in item
      Select Case contextMenuItemEventArgs.Item.Text
        Case "Show Alert"
          ' Construct a JavaScript callback to display an alert showing the worker process identity
          ' under which the application is running
          Dim jsWorkerProcessIdentityAlert As String = String.Format("alert('Worker process identity: {0}')", System.Security.Principal.WindowsIdentity.GetCurrent().Name.Replace("\", "\\"))
          Dim workerProcessIdentityAlertCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsWorkerProcessIdentityAlert)
          m_customContextMenuSimpleResult.CallbackResults.Add(workerProcessIdentityAlertCallbackResult)
        Case "Zoom To Feature"
          ' Make sure the node on which the context menu was shown is a FeatureNode (i.e. corresponds to a 
          ' feature on the map)
          If TypeOf treeViewPlusNode Is ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode Then
            ' Get a reference to the node as a FeatureNode
            Dim featureNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode = CType(treeViewPlusNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)

            ' Get the feature's data as a row
            Dim dataRow As System.Data.DataRow = featureNode.DataRow

            ' Get the graphics layer containing the feature
            Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = CType(dataRow.Table, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)

            ' Get the feature's geometry
            Dim adfGeometry As ESRI.ArcGIS.ADF.Web.Geometry.Geometry = featureGraphicsLayer.GeometryFromRow(dataRow)

            ' Get the bounding envelope of the geometry
            Dim boundingEnvelope As ESRI.ArcGIS.ADF.Web.Geometry.Envelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(Double.MaxValue, Double.MaxValue, Double.MinValue, Double.MinValue)
            If TypeOf adfGeometry Is ESRI.ArcGIS.ADF.Web.Geometry.Polygon Then
              GetBoundingExtent(CType(adfGeometry, ESRI.ArcGIS.ADF.Web.Geometry.Polygon), boundingEnvelope)

              ' Expand the envelope to include some area on the map around the feature
              boundingEnvelope = boundingEnvelope.Expand(10)
            ElseIf TypeOf adfGeometry Is ESRI.ArcGIS.ADF.Web.Geometry.Point Then
              GetBoundingExtent(CType(adfGeometry, ESRI.ArcGIS.ADF.Web.Geometry.Point), boundingEnvelope)

              ' Expand the bounding envelope so that it is 1/8 of the map width.  We do this instead of using
              ' Envelope.Expand because that method expands by percentage.  Since the bounding envelope of a
              ' poing geometry has a height and width of zero, expanding by any percentage will result in no
              ' change.
              Dim mapWidthSixteenth As Double = TaskResultsInstance.MapInstance.GetFullExtent().Width / 16
              boundingEnvelope = New ESRI.ArcGIS.ADF.Web.Geometry.Envelope(boundingEnvelope.XMin - mapWidthSixteenth, boundingEnvelope.YMin - mapWidthSixteenth, boundingEnvelope.XMax + mapWidthSixteenth, boundingEnvelope.YMax + mapWidthSixteenth)
            End If

            ' Set the map's extent to be the bounding envelope
            TaskResultsInstance.MapInstance.Extent = boundingEnvelope

            ' Copy the map's callback results (created by changing the extent) to the custom context menu so
            ' that they are processed on the client
            m_customContextMenuSimpleResult.CallbackResults.CopyFrom(TaskResultsInstance.MapInstance.CallbackResults)

          End If
        Case "Remove"
          ' Remove the node, refresh the task results control, and return callback results accordingly
          treeViewPlusNode.Parent.Nodes.Remove(treeViewPlusNode)
          TaskResultsInstance.Refresh()
          m_customContextMenuSimpleResult.CallbackResults.CopyFrom(TaskResultsInstance.CallbackResults)
      End Select
    End Sub

    #End Region

    #Region "QueryAttributesTask Overrides"

    ' Customizes the task results
    Public Overrides Sub ExecuteTask()
      ' Call the base task's ExcecuteTask function so that a default set of results is created
      MyBase.ExecuteTask()

      ' Only manipulate the results if they are returned in a DataSet.  If they are not, that means no results
      ' were found or there was an error.
      If TypeOf Results Is System.Data.DataSet Then
        Dim resultsDataSet As System.Data.DataSet = TryCast(Results, System.Data.DataSet)

        ' Get the callback arguments from CallbackEventArgument, which contains callback arg/val pairs
        Dim callbackArgumentsCollection As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(Me.CallbackEventArgument)

        ' Remove any results already generated by this task
        For Each treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In TaskResultsInstance.Nodes
          If treeViewPlusNode.Text.StartsWith("Selected Set") Then
            RemoveResultsFromMap(TryCast(treeViewPlusNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode))
            treeViewPlusNode.Remove()
            TaskResultsInstance.Refresh()
            Exit For
          End If
        Next treeViewPlusNode

        ' Retrieve the task's job ID from the callback arguments
        Dim taskJobID As String = callbackArgumentsCollection("taskJobID")
        ' Create a new task results node to display the non-spatial results data
        Dim taskResultsNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode("Selected Set")

        ' Associate the node with the Task Results container
        TaskResultsInstance.SetupTaskResultNode(Me, taskJobID, Input, taskResultsNode)

        ' Make sure the results dataset contains tables
        If Not resultsDataSet.Tables Is Nothing Then
          ' If the properties of the task results container specifies that the number of feature found
          ' (i.e. rows) is to be shown, append the number of rows to the text of the task results node
          If TaskResultsInstance.ShowRowCount Then
            Dim rowCount As Integer = 0
                        Dim ii As Integer = 0
                        Do While ii < resultsDataSet.Tables.Count
                            rowCount += resultsDataSet.Tables(ii).Rows.Count
                            ii += 1
                        Loop

            taskResultsNode.Text += System.String.Format(" ({0})", rowCount)
          End If

          Dim resultsDataTable As System.Data.DataTable = Nothing
          Dim dataTableTreeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = Nothing

          ' Loop through the tables in the results dataSet, adding the data for each to the results node
          Dim i As Integer = 0
          Do While i < resultsDataSet.Tables.Count
            resultsDataTable = resultsDataSet.Tables(i)

            ' Check whether the current table can be cast to a Web ADF GraphicsLayer
            If TypeOf resultsDataTable Is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer Then
              ' Since the current data table is a GraphicsLayer, create a GraphicsLayerNode for it.  This
              ' will enable functionality such as zooming to the layer and toggling the entire layer's 
              ' visiblity when the node is checked/unchecked
              Dim adfGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer = TryCast(resultsDataTable, ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
              dataTableTreeViewPlusNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode(adfGraphicsLayer, Me.ShowLegend, Nothing, Nothing)

              ' If the number of features in the layer are to be shown, append this number to the text of
              ' the node
              If TaskResultsInstance.ShowRowCount Then
                dataTableTreeViewPlusNode.Text += System.String.Format(" ({0})", adfGraphicsLayer.Rows.Count)
              End If

              TaskResultsInstance.SetupContextMenu(TaskResultsInstance.GraphicsLayerContextMenu, dataTableTreeViewPlusNode)
            Else
              ' Since the dataTable is not a graphics layer, set up a node without spatial functionality
              dataTableTreeViewPlusNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode(resultsDataTable.TableName)
              dataTableTreeViewPlusNode.ShowCheckBox = False
              TaskResultsInstance.SetupContextMenu(TaskResultsInstance.RemoveOnlyContextMenu, dataTableTreeViewPlusNode)
            End If

            ' Set the data table node to expand or collapse when clicked
            dataTableTreeViewPlusNode.ClickBehavior = ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.ExpandCollapse

            ' If the task results control's properties specifies showing a legend for results, add a text-only
            ' node with the text "Features" to the results node tree before adding feature nodes
            If Me.ShowLegend AndAlso TypeOf resultsDataTable Is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer Then
              Dim featuresNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode("Features")
              featuresNode.ShowCheckBox = False
              featuresNode.ClickBehavior = ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.None
              dataTableTreeViewPlusNode.Nodes.Add(featuresNode)
            End If

            ' Get the display field from the result table's extended properties
            Dim displayFieldName As String = TryCast(resultsDataTable.ExtendedProperties(ESRI.ArcGIS.ADF.Web.Constants.ADFHeader), String)

            Dim dataRow As System.Data.DataRow = Nothing
            Dim featureNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = Nothing
            Dim attributesNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode = Nothing

            ' Get a default template to use in displaying the non-spatial results
            Dim contentsTemplate As String = ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer.GetContentsTemplate(resultsDataTable, False, TaskResultsInstance.SelectedColor, True)

            ' Loop through the individual results (rows/features), creating a node for each
            Dim j As Integer = 0
            Do While j < resultsDataTable.Rows.Count
              dataRow = resultsDataTable.Rows(j)

              ' Create a hyperlink for state name values.  To do this, get the value of the STATE_NAME field
              ' for the current row, add hyperlink markup around it, and re-assign the STATE_NAME value with
              ' the marked-up text
              Dim stateName As String = TryCast(dataRow("STATE_NAME"), String)
              Dim hyperlinkedStateName As String = "<a target='_blank' href='http://www.google.com/search?q=" & stateName & "' >" & stateName & "</a>"
              dataRow("STATE_NAME") = hyperlinkedStateName

              ' If the results are a feature graphics layer, create a node with highlighting and zoom-to
              ' functionality.
              If TypeOf resultsDataTable Is ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer Then
                Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = TryCast(resultsDataTable, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)

                ' Create a feature node based on the data row and the value in the display field
                Dim displayValue As String = TryCast(dataRow(displayFieldName), String)
                featureNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode(displayValue, dataRow)

                ' Get a reference to the current node as a GraphicsLayerNode
                Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = TryCast(dataTableTreeViewPlusNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode)

                ' Retrieve the client-side ID of the graphics layer.  Note that GetGraphicsLayerClientID
                ' isn't implemented for task results graphics layers, so we have to manually assemble the
                ' ID from the map ID, resource name of the task results, and results data table name.
                'string graphicsLayerID = 
                '    this.TaskResultsInstance.MapInstance.GetGraphicsLayerClientID(featureGraphicsLayer);
                Dim graphicsLayerID As String = String.Format("{0}_{1}_{2}", TaskResultsInstance.MapInstance.ClientID, graphicsLayerNode.GetResourceName(TaskResultsInstance), resultsDataTable.TableName)

                ' Create a JavaScript string that will (1) set the text color of the current node
                ' (2) get the graphicFeatureGroup (i.e. layer) for the GraphicsLayer, (3) get the 
                ' graphicFeature corresponding to the current row index from the graphicFeatureGroup, 
                ' and (4) set the highlight on the graphicFeature
                'string jsSetRowAndFeatureHighlight = "for (var obj in Sys.Application._components)" +
                '    "{ alert(obj); }" +
                '    "while (obj != null);";
                Dim jsSetRowAndFeatureHighlight As String = "this.style.color='{0}';" & "var graphicFeatureGroup = $find('{1}');" & "var graphicFeature = graphicFeatureGroup.get({2});" & "graphicFeature.set_highlight({3});"

                ' Assign the JavaScript to the node's onmouseover and onmouseout events.  Highlight
                ' the graphics and change the node text's color on mouseover, then un-highlight and
                ' reset the node text's color on mouseout.  Note that the iterator j is used for the
                ' graphicFeature's index, since this coincides with the index of the current dataRow
                ' in the results table
                featureNode.Attributes.Add("onmouseover", String.Format(jsSetRowAndFeatureHighlight, System.Drawing.ColorTranslator.ToHtml(TaskResultsInstance.HoverColor), graphicsLayerID, j, "true"))
                featureNode.Attributes.Add("onmouseout", String.Format(jsSetRowAndFeatureHighlight, System.Drawing.ColorTranslator.ToHtml(TaskResultsInstance.ForeColor), graphicsLayerID, j, "false"))

                ' Specify left and right-click behavior
                featureNode.ClickBehavior = ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.ExpandCollapse
                TaskResultsInstance.SetupContextMenu(m_customContextMenuSimpleResult, featureNode)
              Else
                ' Set-up a node that simply shows the text of the display field
                featureNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode(TryCast(dataRow(displayFieldName), String))
                featureNode.ClickBehavior = ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.None
                featureNode.ShowCheckBox = False
                featureNode.ClickBehavior = ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.ExpandCollapse
                TaskResultsInstance.SetupContextMenu(TaskResultsInstance.RemoveOnlyContextMenu, featureNode)
              End If

              ' Add the feature node to the data table (i.e. graphics layer) node
              dataTableTreeViewPlusNode.Nodes.Add(featureNode)


              ' Create a node to display the attributes of the current row (i.e. feature)
              attributesNode = New ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode()

              ' Use the graphics layer's contents template to initialize the attribute node's text.
              ' This formats the contents of the dataRow into html mark-up based on the specifications
              ' of the template.
              attributesNode.Text = String.Format(contentsTemplate, dataRow.ItemArray)
              attributesNode.ShowCheckBox = False
              attributesNode.ClickBehavior = ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.None
              featureNode.Nodes.Add(attributesNode)

              j += 1
            Loop
            contentsTemplate = Nothing

            ' Add the data table (i.e. graphics layer) node to the root results node
            taskResultsNode.Nodes.Add(dataTableTreeViewPlusNode)
            If (Not Me.GroupResultsByTable) Then
              dataTableTreeViewPlusNode.HideNodeShowChildren = True
            End If
            i += 1
          Loop
        End If

        Results = taskResultsNode
      End If
    End Sub

    #End Region

    #Region "Instance Properties"

    ' Convenient access to the first TaskResults control in the Task's TaskResultsContainers collection
    Private ReadOnly Property TaskResultsInstance() As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults
      Get
        ' Retrieve the TaskResults control if it has not already been
        If (m_taskResults Is Nothing) AndAlso (Not TaskResultsContainers(0) Is Nothing) Then
          m_taskResults = TryCast(Utility.FindControl(TaskResultsContainers(0).Name, Page), ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults)
        End If
        Return m_taskResults
      End Get
    End Property

    #End Region

    #Region "Instance Methods"

    Private Sub RemoveResultsFromMap(ByVal taskResultNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode)
      ' Make sure the task results control is associated with a map control
      If TaskResultsInstance.MapInstance Is Nothing Then
      Return
      End If

      ' Remove the task result graphics from the map and get a list of resources affected by this
      Dim resourcesToRefresh As System.Collections.Generic.List(Of String) = New System.Collections.Generic.List(Of String)()
      RemoveResultGraphics(taskResultNode, resourcesToRefresh)

      ' Refresh the resources affected by graphics removal so the changes are shown on the map
      For Each resourceName As String In resourcesToRefresh
        TaskResultsInstance.MapInstance.RefreshResource(resourceName)
      Next resourceName

      CallbackResults.CopyFrom(TaskResultsInstance.MapInstance.CallbackResults)
    End Sub

    Private Sub RemoveResultGraphics(ByVal treeViewPlusNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode, ByRef affectedResourceList As System.Collections.Generic.List(Of String))
      Dim affectedResourceName As String = Nothing

      ' Only manipulate MapDisplayNodes, since this is the node type for results displayed on the map
      If TypeOf treeViewPlusNode Is ESRI.ArcGIS.ADF.Web.UI.WebControls.MapDisplayNode Then
        Dim mapDisplayNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.MapDisplayNode = TryCast(treeViewPlusNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.MapDisplayNode)

        ' Use the RemoveFromMap function to remove the result graphics from the map.  This function returns
        ' the name of the affected map resource.
        affectedResourceName = mapDisplayNode.RemoveFromMap(TaskResultsInstance)

        ' Add the name of the affected resource to the list if it is not already included
        If Not affectedResourceName Is Nothing AndAlso (Not affectedResourceList.Contains(affectedResourceName)) Then
          affectedResourceList.Add(affectedResourceName)
        End If
      End If

      ' Remove result graphics recursively for any child nodes
      If treeViewPlusNode.Nodes.Count > 0 Then
        For Each childNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode In treeViewPlusNode.Nodes
          RemoveResultGraphics(childNode, affectedResourceList)
        Next childNode
      End If
    End Sub

    ' Retrieves the bounding box of a polygon
    Private Sub GetBoundingExtent(ByVal adfPolygon As ESRI.ArcGIS.ADF.Web.Geometry.Polygon, ByVal boundingBox As ESRI.ArcGIS.ADF.Web.Geometry.Envelope)
      ' Loop through all the rings of the polygon and update the bounding box to account for each vertex
      Dim i As Integer = 0
      Do While i < adfPolygon.Rings.Count
        Dim adfRing As ESRI.ArcGIS.ADF.Web.Geometry.Ring = adfPolygon.Rings(i)
        Dim j As Integer = 0
        Do While j < adfRing.Points.Count
          GetBoundingExtent(adfRing.Points(j), boundingBox)
          j += 1
        Loop
        i += 1
      Loop
    End Sub

    Private Sub GetBoundingExtent(ByVal adfPoint As ESRI.ArcGIS.ADF.Web.Geometry.Point, ByVal boundingBox As ESRI.ArcGIS.ADF.Web.Geometry.Envelope)
      ' Update the passed-in envelope based on whether the passed-in point falls outside the envelope's bounds
      If adfPoint.X < boundingBox.XMin Then
        boundingBox.XMin = adfPoint.X
      End If
      If adfPoint.X > boundingBox.XMax Then
        boundingBox.XMax = adfPoint.X
      End If
      If adfPoint.Y < boundingBox.YMin Then
        boundingBox.YMin = adfPoint.Y
      End If
      If adfPoint.Y > boundingBox.YMax Then
        boundingBox.YMax = adfPoint.Y
      End If
    End Sub

    #End Region
  End Class
End Namespace