Common MapTips
Common_MapTips_VBNet\App_Code\AttributesOnDemandTask.vb
' Copyright 2011 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 ESRI.ADF.Samples.CustomTasks
  Public Class AttributesOnDemandTask
    Inherits ESRI.ArcGIS.ADF.Tasks.QueryAttributesTask
    #Region "Instance Variable Declarations"

    ' Stores a reference to the buddied TaskResults Control
    Private m_taskResults As ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults = Nothing

    ' Tracks whether the current page request has routed through ExecuteTask
    Private _taskExecuted As Boolean = False

    #End Region

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

    Protected Overrides Sub CreateChildControls()
      MyBase.CreateChildControls()

      ' Add a handler to the buddied TaskResults Control's NodeAdded event
      AddHandler TaskResultsInstance.NodeAdded, AddressOf TaskResultsInstance_NodeAdded
    End Sub

    #End Region

    #Region "Web ADF Control Event Handlers"

    ' Retrieves the results graphics layer and enables Attribute-on-demand MapTips.  Needs to be done here for extended tasks
    ' because the results graphics layer available in ExecuteTask is replaced during subsequent task result node creation.
    Private Sub TaskResultsInstance_NodeAdded(ByVal sender As Object, ByVal args As ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs)
      ' Check whether the currently added node has a parent or child graphics layer and the task has executed during the 
      ' current request
      Dim graphicsLayerNode As ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode = Me.GetRelatedGraphicsLayerNode(args.Node)
      If graphicsLayerNode IsNot Nothing AndAlso Me._taskExecuted Then
        ' Retrieve the task's layerFormat
        Dim layerFormat As ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat = ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(Me.TaskResultsInstance.MapInstance.MapResourceManagerInstance, Me.PredefinedQuery.MapResource, Me.PredefinedQuery.LayerID)

        ' Iterate through the fields of the LayerFormat and create the HTML markup for the MapTips content area that
        ' will be used after attribute data has been retrieved
        Dim uniqueIDField As String = Nothing
        Dim currentTemplateRow As String = Nothing
        Dim defaultTemplateRows As New System.Collections.Generic.List(Of String)()
        Dim hasAttributesTemplate As String = "<table>"
        For Each fieldInfo As ESRI.ArcGIS.ADF.Web.DataSources.FieldInfo In layerFormat.Fields
          If fieldInfo.Visible Then
            currentTemplateRow = "<tr><td style='font-weight:bold; border-bottom: solid 1px gray; " & "border-right: solid 1px gray;'>{0}</td>" & "<td style='border-bottom: solid 1px gray; border-right: solid 1px gray;'>{{{{@{1}}}}}</td></tr>"
            currentTemplateRow = String.Format(currentTemplateRow, fieldInfo.Alias, fieldInfo.Name)
            defaultTemplateRows.Add(currentTemplateRow)
          End If

          ' If the current field is a unique identifier, store a reference to the field name
          If graphicsLayerNode.Layer.Columns(fieldInfo.Name).Unique Then
            uniqueIDField = fieldInfo.Name
          End If
        Next fieldInfo
        For Each templateRow As String In defaultTemplateRows
          hasAttributesTemplate &= templateRow
        Next templateRow

        hasAttributesTemplate &= "</table>"

        ' Get the URL of the Web ADF activity indicator
        Dim activityIndicatorUrl As String = ESRI.ArcGIS.ADF.Web.UI.WebControls.ResourceUtility.GetImage("callbackActivityIndicator.gif", Me, GetType(ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanelTask), "Runtime")

        ' Create the HTML markup for the MapTips content area while attribute data is being retrieved
        Dim retrievingAttributesTemplate As String = "<div style='white-space:nowrap; font-family:Arial; " & "font-style:italic; font-size:10pt; color:gray;'><img src='{0}' />Retrieving Attribute Data</div>"
        retrievingAttributesTemplate = String.Format(retrievingAttributesTemplate, activityIndicatorUrl)

        ' Get a reference to the node's graphics layer as a FeatureGraphicsLayer
        Dim featureGraphicsLayer As ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer = TryCast(graphicsLayerNode.Layer, ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)

        ' Construct the client-side GraphicFeatureGroup ID of the results graphics layer.  Note that this is only necessary
        ' for extended out-of-the-box tasks.  Otherwise, Map::GetGraphicsLayerClientID or MapTips::GraphicsLayerClientID can
        ' be used.
        Dim graphicsLayerID As String = String.Format("{0}_{1} {2} Results_{3}", Me.TaskResultsInstance.MapInstance.ClientID, Me.TaskResultsInstance.ClientID, featureGraphicsLayer.FeatureType.ToString(), graphicsLayerNode.Layer.TableName)
        Dim id As String = Me.TaskResultsInstance.MapInstance.GetGraphicsLayerClientID(featureGraphicsLayer)

        ' JavaScript to call the attributes-on-demand MapTips initialization method.  Here the content templates are 
        ' explicitly defined.  If they are not, default templates will be used.
        Dim enableAttributesOnDemandJavaScript As String = "" & ControlChars.CrLf & "                    window.setTimeout(""var graphicFeatureGroup = $find('{0}');"" +" & ControlChars.CrLf & "                    ""var retrievingAttributesTemplate = String.format(\""{1}\"", graphicFeatureGroup.get_id());"" +" & ControlChars.CrLf & "                    ""var hasAttributesTemplate = String.format(\""{2}\"", graphicFeatureGroup.get_id());"" +" & ControlChars.CrLf & "                    ""graphicFeatureGroup.setupMapTipsAttributesOnDemand(retrievingAttributesTemplate, hasAttributesTemplate,"" +" & ControlChars.CrLf & "                    ""'{3}', '{4}', '{5}', \""{6}\"");"", 0);"
        enableAttributesOnDemandJavaScript = String.Format(enableAttributesOnDemandJavaScript, graphicsLayerID, retrievingAttributesTemplate, hasAttributesTemplate, uniqueIDField, Me.PredefinedQuery.LayerID, Me.PredefinedQuery.MapResource, Me.CallbackFunctionString)
        Dim enableAttributesOnDemandCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(enableAttributesOnDemandJavaScript)
        Me.CallbackResults.Add(enableAttributesOnDemandCallbackResult)
      End If
    End Sub

    #End Region

    #Region "Task Overrides"

    Public Overrides Sub ExecuteTask()
      ' Create a default set of results
      MyBase.ExecuteTask()

      Me._taskExecuted = True
    End Sub

    Public Overrides Function GetCallbackResult() As String
      Dim callbackArgs As System.Collections.Specialized.NameValueCollection = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(Me.CallbackEventArgument)

      ' Check whether the callback event argument indicates retrieval of MapTips attributes
      If callbackArgs("EventArg") = "retrieveMapTipsAttributes" Then
        ' === USED ONLY FOR DEMONSTRATION - REMOVE FOR PRODUCTION PURPOSES ==
        '  Suspend the current thread to allow the retrieving attributes indicator to display
        System.Threading.Thread.Sleep(1000)

        MyBase.GetCallbackResult()

        ' Get a reference to the resource that was passed as part of the callback arguments
        Dim commonMapFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality = Me.TaskResultsInstance.MapInstance.GetFunctionality(callbackArgs("ResourceName"))
        Dim gisResource As ESRI.ArcGIS.ADF.Web.DataSources.IGISResource = commonMapFunctionality.Resource

        ' Retrieve query functionality for the resource
        Dim commonQueryFunctionality As ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality = CType(gisResource.CreateFunctionality(GetType(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), Nothing), ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)

        ' Initialize a query filter
        Dim adfQueryFilter As New ESRI.ArcGIS.ADF.Web.QueryFilter()
        adfQueryFilter.ReturnADFGeometries = False

        ' Create the query clause with information passed from the maptips in the callback
        adfQueryFilter.WhereClause = String.Format("{0} = {1}", callbackArgs("UniqueIDField"), callbackArgs("FeatureID"))

        ' Execute the query
        Dim resultsTable As System.Data.DataTable = commonQueryFunctionality.Query(Nothing, callbackArgs("LayerID"), adfQueryFilter)

        If resultsTable Is Nothing Then
          Return MyBase.GetCallbackResult()
        End If

        ' Create a JSON string with the results feature's attributes
        Dim jsonAttributes As String = "{ "
        For Each column As System.Data.DataColumn In resultsTable.Columns
          jsonAttributes &= String.Format("'{0}':'{1}', ", column.ColumnName, resultsTable.Rows(0)(column.ColumnName))
        Next column

        jsonAttributes = String.Format("{0} }}", jsonAttributes.Substring(0, jsonAttributes.Length - 2))

        ' JavaScript needed to apply the queried feature's attributes to the mapTips
        Dim updateAttributesJavaScript As String = String.Format("$find('{0}').get_mapTips().updateAttributes({1}, '{2}');", callbackArgs("GraphicFeatureGroupID"), jsonAttributes, callbackArgs("FeatureID"))
        Dim updateAttributesCallbackResult As ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult = ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(updateAttributesJavaScript)

        Me.CallbackResults.Add(updateAttributesCallbackResult)
        Return Me.CallbackResults.ToString()
      Else
        Return MyBase.GetCallbackResult()
      End If
    End Function

    #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 (TaskResultsContainers(0) IsNot Nothing) Then
          m_taskResults = TryCast(ESRI.ArcGIS.ADF.Web.UI.WebControls.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"

    ' 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

    #End Region
  End Class
End Namespace