Common MapTips
Common_MapTips_CSharp\App_Code\AttributesOnDemandTask.cs
// 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.
// 

namespace ESRI.ADF.Samples.CustomTasks
{
    public class AttributesOnDemandTask : ESRI.ArcGIS.ADF.Tasks.QueryAttributesTask
    {
        #region Instance Variable Declarations

        // Stores a reference to the buddied TaskResults Control
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults m_taskResults = null;

        // Tracks whether the current page request has routed through ExecuteTask
        private bool _taskExecuted = false;

        #endregion

        #region ASP.NET WebControl Life Cycle Event Handlers

        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Add a handler to the buddied TaskResults Control's NodeAdded event
            this.TaskResultsInstance.NodeAdded +=
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeAddedEventHandler(TaskResultsInstance_NodeAdded);
        }

        #endregion

        #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.
        void TaskResultsInstance_NodeAdded(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs args)
        {
            // Check whether the currently added node has a parent or child graphics layer and the task has executed during the 
            // current request
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = this.GetRelatedGraphicsLayerNode(args.Node);
            if (graphicsLayerNode != null && this._taskExecuted)
            {
                // Retrieve the task's layerFormat
                ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat layerFormat =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(
                    this.TaskResultsInstance.MapInstance.MapResourceManagerInstance, this.PredefinedQuery.MapResource,
                    this.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
                string uniqueIDField = null;
                string currentTemplateRow = null;
                System.Collections.Generic.List<string> defaultTemplateRows = new System.Collections.Generic.List<string>();
                string hasAttributesTemplate = "<table>";
                foreach (ESRI.ArcGIS.ADF.Web.DataSources.FieldInfo fieldInfo in layerFormat.Fields)
                {
                    if (fieldInfo.Visible)
                    {
                        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);
                    }

                    // If the current field is a unique identifier, store a reference to the field name
                    if (graphicsLayerNode.Layer.Columns[fieldInfo.Name].Unique)
                        uniqueIDField = fieldInfo.Name;
                }
                foreach (string templateRow in defaultTemplateRows)
                    hasAttributesTemplate += templateRow;

                hasAttributesTemplate += "</table>";

                // Get the URL of the Web ADF activity indicator
                string activityIndicatorUrl = ESRI.ArcGIS.ADF.Web.UI.WebControls.ResourceUtility.GetImage(
                    "callbackActivityIndicator.gif", this, typeof(ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanelTask), "Runtime");

                // Create the HTML markup for the MapTips content area while attribute data is being retrieved
                string retrievingAttributesTemplate = "<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
                ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer featureGraphicsLayer = graphicsLayerNode.Layer as
                    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.
                string graphicsLayerID = string.Format("{0}_{1} {2} Results_{3}", this.TaskResultsInstance.MapInstance.ClientID,
                    this.TaskResultsInstance.ClientID, featureGraphicsLayer.FeatureType.ToString(), graphicsLayerNode.Layer.TableName);
                string id = this.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.
                string enableAttributesOnDemandJavaScript = @"
                    window.setTimeout(""var graphicFeatureGroup = $find('{0}');"" +
                    ""var retrievingAttributesTemplate = String.format(\""{1}\"", graphicFeatureGroup.get_id());"" +
                    ""var hasAttributesTemplate = String.format(\""{2}\"", graphicFeatureGroup.get_id());"" +
                    ""graphicFeatureGroup.setupMapTipsAttributesOnDemand(retrievingAttributesTemplate, hasAttributesTemplate,"" +
                    ""'{3}', '{4}', '{5}', \""{6}\"");"", 0);";
                enableAttributesOnDemandJavaScript = string.Format(enableAttributesOnDemandJavaScript, graphicsLayerID,
                    retrievingAttributesTemplate, hasAttributesTemplate, uniqueIDField, this.PredefinedQuery.LayerID,
                    this.PredefinedQuery.MapResource, this.CallbackFunctionString);
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult enableAttributesOnDemandCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(enableAttributesOnDemandJavaScript);
                this.CallbackResults.Add(enableAttributesOnDemandCallbackResult);
            }
        }

        #endregion

        #region Task Overrides

        public override void ExecuteTask()
        {
            // Create a default set of results
            base.ExecuteTask();

            this._taskExecuted = true;
        }

        public override string GetCallbackResult()
        {
            System.Collections.Specialized.NameValueCollection callbackArgs =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(this.CallbackEventArgument);

            // Check whether the callback event argument indicates retrieval of MapTips attributes
            if (callbackArgs["EventArg"] == "retrieveMapTipsAttributes")
            {
                // === 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);

                base.GetCallbackResult();

                // Get a reference to the resource that was passed as part of the callback arguments
                ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality commonMapFunctionality =
                    this.TaskResultsInstance.MapInstance.GetFunctionality(callbackArgs["ResourceName"]);
                ESRI.ArcGIS.ADF.Web.DataSources.IGISResource gisResource = commonMapFunctionality.Resource;

                // Retrieve query functionality for the resource
                ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality commonQueryFunctionality =
                    (ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality)
                    gisResource.CreateFunctionality(
                    typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), null);

                // Initialize a query filter
                ESRI.ArcGIS.ADF.Web.QueryFilter adfQueryFilter = 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
                System.Data.DataTable resultsTable = commonQueryFunctionality.Query(null,
                    callbackArgs["LayerID"], adfQueryFilter);

                if (resultsTable == null)
                    return base.GetCallbackResult();

                // Create a JSON string with the results feature's attributes
                string jsonAttributes = "{ ";
                foreach (System.Data.DataColumn column in resultsTable.Columns)
                    jsonAttributes += string.Format("'{0}':'{1}', ", column.ColumnName, resultsTable.Rows[0][column.ColumnName]);

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

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

                this.CallbackResults.Add(updateAttributesCallbackResult);
                return this.CallbackResults.ToString();
            }
            else
            {
                return base.GetCallbackResult();
            }
        }

        #endregion

        #region Instance Properties

        // Convenient access to the first TaskResults control in the Task's TaskResultsContainers collection
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults TaskResultsInstance
        {
            get
            {
                // Retrieve the TaskResults control if it has not already been
                if ((m_taskResults == null) && (TaskResultsContainers[0] != null))
                    m_taskResults = ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(TaskResultsContainers[0].Name, Page) as
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults;
                return m_taskResults;
            }
        }

        #endregion

        #region Instance Methods

        // Retrieves a GraphicsLayerNode that is an ancestor or descendant of the passed-in node, if available
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode GetRelatedGraphicsLayerNode(
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode node)
        {
            // Check whether the passed-in node is a GraphicsLayerNode
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = node
                as ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode;

            // Check whether the passed-in node has an ancestor GraphicsLayerNode
            if (graphicsLayerNode == null)
                graphicsLayerNode = this.FindParentGraphicsLayerNode(node);

            // Check whether the passed-in node has a descendant GraphicsLayerNode
            if (graphicsLayerNode == null)
                graphicsLayerNode = this.FindChildGraphicsLayerNode(node);

            return graphicsLayerNode;
        }

        // Retrieves a GraphicsLayerNode that is a descendant of the passed-in node, if available
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode FindChildGraphicsLayerNode(
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode node)
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = node
                as ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode;
            if (graphicsLayerNode == null && node.Nodes.Count > 0)
            {
                foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode childNode in node.Nodes)
                {
                    graphicsLayerNode = this.FindChildGraphicsLayerNode(childNode);
                    if (graphicsLayerNode != null)
                        break;
                }
            }

            return graphicsLayerNode;
        }

        // Retrieves a GraphicsLayerNode that is an ancestor of the passed-in node, if available
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode FindParentGraphicsLayerNode(
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode node)
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = node
                as ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode;
            if (graphicsLayerNode == null && node.Parent is ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode)
                graphicsLayerNode = this.FindParentGraphicsLayerNode(node.Parent as
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode);

            return graphicsLayerNode;
        }

        #endregion
    }
}