Common Extend tasks
Common_ExtendTasks_CSharp\App_Code\ExtendQueryTask.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 ExtendedTasks
{
    public class ExtendQueryTask : ESRI.ArcGIS.ADF.Tasks.QueryAttributesTask
    {
        #region Instance Variable Declarations

        ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenu m_customContextMenuSimpleResult;
        ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults m_taskResults = null;

        #endregion

        #region ASP.NET WebControl Life Cycle Event Overrids

        // Add custom context menu to the task's controls collection
        protected override void CreateChildControls()
        {
            // Create the default task controls
            base.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
            m_customContextMenuSimpleResult.ItemClicked += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItemClickedEventHandler(
                    customContextMenuSimpleResult_ItemClicked);
            m_customContextMenuSimpleResult.Dismissed += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuDismissedEventHandler(
                    customContextMenuSimpleResult_Dismissed);

            // Add a menu item with the text "Show Alert"
            ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem showAlertContextMenuItem =
                    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
            ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem zoomToFeatureContextMenuItem =
                    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);
        }

        protected override void OnLoad(System.EventArgs eventArgs)
        {
            base.OnLoad(eventArgs);

            // Add an event handler that fires when a node on the associated task results container is clicked
            this.TaskResultsInstance.NodeClicked += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickedEventHandler(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]);
        }

        #endregion

        #region Web ADF WebControl Event Handlers

        void TaskResults_NodeClicked(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs 
            treeViewPlusNodeEventArgs)
        {
            // Get the text of the node that was clicked
            string nodeText = 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
            string jsUpdateStatusBar = string.Format("window.status = 'Clicked on {0} during millisecond value of {1}'", 
                nodeText, System.DateTime.Now.Millisecond);
            ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult updateStatusBarCallbackResult = 
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsUpdateStatusBar);
            TaskResultsInstance.CallbackResults.Add(updateStatusBarCallbackResult);
        }

        void customContextMenuSimpleResult_Dismissed(object sender, 
            ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuDismissedEventArgs contextMenuDismissedEventArgs)
        {
            // Get the task results node that was clicked to display the context menu
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode treeViewPlusNode = 
                TaskResultsInstance.Nodes.FindByNodeID(contextMenuDismissedEventArgs.Context);
            if (treeViewPlusNode != null)
            {
                // Construct a JavaScript callback to unselect the node in the TaskResults container
                string jsUnselectResultsNode = string.Format("var node = document.getElementById('{0}_textCell');" +
                    "if(node!=null){{node.style.backgroundColor='{1}';}}", treeViewPlusNode.NodeID, 
                    System.Drawing.ColorTranslator.ToHtml(TaskResultsInstance.BackColor));
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult unselectResultsNodeCallbackResult = 
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsUnselectResultsNode);
                m_customContextMenuSimpleResult.CallbackResults.Add(unselectResultsNodeCallbackResult);
            }
        }

        void customContextMenuSimpleResult_ItemClicked(object sender, 
            ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItemEventArgs contextMenuItemEventArgs)
        {
            // Get the task results node that was right-clicked to display the context menu
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode treeViewPlusNode =
                TaskResultsInstance.Nodes.FindByNodeID(contextMenuItemEventArgs.Context);

            // Determine which context menu item was clicked by checking the text of the passed-in item
            switch (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
                    string jsWorkerProcessIdentityAlert = string.Format("alert('Worker process identity: {0}')",
                        System.Security.Principal.WindowsIdentity.GetCurrent().Name.Replace("\\", "\\\\"));
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult workerProcessIdentityAlertCallbackResult =
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(jsWorkerProcessIdentityAlert);
                    m_customContextMenuSimpleResult.CallbackResults.Add(workerProcessIdentityAlertCallbackResult);
                    break;
                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 (treeViewPlusNode is ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)
                    {
                        // Get a reference to the node as a FeatureNode
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode featureNode =
                            (ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode)treeViewPlusNode;

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

                        // Get the graphics layer containing the feature
                        ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer featureGraphicsLayer = 
                            (ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)dataRow.Table;
                        
                        // Get the feature's geometry
                        ESRI.ArcGIS.ADF.Web.Geometry.Geometry adfGeometry = featureGraphicsLayer.GeometryFromRow(dataRow);
                        
                        // Get the bounding envelope of the geometry
                        ESRI.ArcGIS.ADF.Web.Geometry.Envelope boundingEnvelope =
                            new ESRI.ArcGIS.ADF.Web.Geometry.Envelope(double.MaxValue, double.MaxValue, 
                                double.MinValue, double.MinValue);
                        if (adfGeometry is ESRI.ArcGIS.ADF.Web.Geometry.Polygon)
                        {
                            GetBoundingExtent((ESRI.ArcGIS.ADF.Web.Geometry.Polygon)adfGeometry, boundingEnvelope);

                            // Expand the envelope to include some area on the map around the feature
                            boundingEnvelope = boundingEnvelope.Expand(10);
                        }
                        else if (adfGeometry is ESRI.ArcGIS.ADF.Web.Geometry.Point)
                        {
                            GetBoundingExtent((ESRI.ArcGIS.ADF.Web.Geometry.Point)adfGeometry, 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.
                            double mapWidthSixteenth = 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);
                        }

                        // 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);
                        
                    }
                    break;
                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);
                    break;
            }
        }

        #endregion

        #region QueryAttributesTask Overrides

        // Customizes the task results
        public override void ExecuteTask()
        {
            // Call the base task's ExcecuteTask function so that a default set of results is created
            base.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 (Results is System.Data.DataSet)
            {
                System.Data.DataSet resultsDataSet = Results as System.Data.DataSet;

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

                // Remove any results already generated by this task
                foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode treeViewPlusNode in 
                    TaskResultsInstance.Nodes)
                {
                    if (treeViewPlusNode.Text.StartsWith("Selected Set"))
                    {
                        RemoveResultsFromMap(treeViewPlusNode as ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode);
                        treeViewPlusNode.Remove();
                        TaskResultsInstance.Refresh();
                        break;
                    }
                }

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

                // Associate the node with the Task Results container
                TaskResultsInstance.SetupTaskResultNode(this, taskJobID, Input, taskResultsNode);

                // Make sure the results dataset contains tables
                if (resultsDataSet.Tables != null)
                {
                    // 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)
                    {
                        int rowCount = 0;
                        for (int i = 0; i < resultsDataSet.Tables.Count; i++)
                            rowCount += resultsDataSet.Tables[i].Rows.Count;

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

                    System.Data.DataTable resultsDataTable = null;
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode dataTableTreeViewPlusNode = null;

                    // Loop through the tables in the results dataSet, adding the data for each to the results node
                    for (int i = 0; i < resultsDataSet.Tables.Count; i++)
                    {
                        resultsDataTable = resultsDataSet.Tables[i];

                        // Check whether the current table can be cast to a Web ADF GraphicsLayer
                        if (resultsDataTable is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
                        {
                            // 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
                            ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer adfGraphicsLayer = 
                                resultsDataTable as ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer;
                            dataTableTreeViewPlusNode = new ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode(
                                adfGraphicsLayer, this.ShowLegend, null, null);

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

                            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);
                        }

                        // 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 (this.ShowLegend && resultsDataTable is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
                        {
                            ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode featuresNode = 
                                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);
                        }

                        // Get the display field from the result table's extended properties
                        string displayFieldName = resultsDataTable.ExtendedProperties[
                            ESRI.ArcGIS.ADF.Web.Constants.ADFHeader] as string;

                        System.Data.DataRow dataRow = null;
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode featureNode = null;
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode attributesNode = null;

                        // Get a default template to use in displaying the non-spatial results
                        string contentsTemplate = 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
                        for (int j = 0; j < resultsDataTable.Rows.Count; j++)
                        {
                            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
                            string stateName = dataRow["STATE_NAME"] as string;
                            string hyperlinkedStateName = "<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 (resultsDataTable is ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer)
                            {
                                ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer featureGraphicsLayer =
                                    resultsDataTable as ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer;

                                // Create a feature node based on the data row and the value in the display field
                                string displayValue = dataRow[displayFieldName] as string;
                                featureNode = new ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode(displayValue, dataRow);

                                // Get a reference to the current node as a GraphicsLayerNode
                                ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = 
                                    dataTableTreeViewPlusNode as 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);
                                string graphicsLayerID = 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);";
                                string jsSetRowAndFeatureHighlight = "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(
                                    dataRow[displayFieldName] as 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);
                            }

                            // 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);

                        }
                        contentsTemplate = null;

                        // Add the data table (i.e. graphics layer) node to the root results node
                        taskResultsNode.Nodes.Add(dataTableTreeViewPlusNode);
                        if (!this.GroupResultsByTable)
                            dataTableTreeViewPlusNode.HideNodeShowChildren = true;
                    }
                }

                Results = taskResultsNode;
            }
        }

        #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 = Utility.FindControl(TaskResultsContainers[0].Name, Page) as
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults;
                return m_taskResults;
            }
        }

        #endregion

        #region Instance Methods

        private void RemoveResultsFromMap(ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode taskResultNode)
        {
            // Make sure the task results control is associated with a map control
            if (TaskResultsInstance.MapInstance == null) return;

            // Remove the task result graphics from the map and get a list of resources affected by this
            System.Collections.Generic.List<string> resourcesToRefresh =
                new System.Collections.Generic.List<string>();
            RemoveResultGraphics(taskResultNode, ref resourcesToRefresh);

            // Refresh the resources affected by graphics removal so the changes are shown on the map
            foreach (string resourceName in resourcesToRefresh)
                TaskResultsInstance.MapInstance.RefreshResource(resourceName);

            CallbackResults.CopyFrom(TaskResultsInstance.MapInstance.CallbackResults);
        }

        private void RemoveResultGraphics(ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode treeViewPlusNode, 
            ref System.Collections.Generic.List<string> affectedResourceList)
        {
            string affectedResourceName = null;

            // Only manipulate MapDisplayNodes, since this is the node type for results displayed on the map
            if (treeViewPlusNode is ESRI.ArcGIS.ADF.Web.UI.WebControls.MapDisplayNode)
            {
                ESRI.ArcGIS.ADF.Web.UI.WebControls.MapDisplayNode mapDisplayNode = 
                    treeViewPlusNode as 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 (affectedResourceName != null && !affectedResourceList.Contains(affectedResourceName))
                    affectedResourceList.Add(affectedResourceName);
            }

            // Remove result graphics recursively for any child nodes
            if (treeViewPlusNode.Nodes.Count > 0)
                foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode childNode in treeViewPlusNode.Nodes)
                    RemoveResultGraphics(childNode, ref affectedResourceList);
        }

        // Retrieves the bounding box of a polygon
        private void GetBoundingExtent(ESRI.ArcGIS.ADF.Web.Geometry.Polygon adfPolygon,
            ESRI.ArcGIS.ADF.Web.Geometry.Envelope boundingBox)
        {
            // Loop through all the rings of the polygon and update the bounding box to account for each vertex
            for (int i = 0; i < adfPolygon.Rings.Count; ++i)
            {
                ESRI.ArcGIS.ADF.Web.Geometry.Ring adfRing = adfPolygon.Rings[i];
                for (int j = 0; j < adfRing.Points.Count; ++j)
                {
                    GetBoundingExtent(adfRing.Points[j], boundingBox);
                }
            }
        }

        private void GetBoundingExtent(ESRI.ArcGIS.ADF.Web.Geometry.Point adfPoint,
            ESRI.ArcGIS.ADF.Web.Geometry.Envelope boundingBox)
        {
            // Update the passed-in envelope based on whether the passed-in point falls outside the envelope's bounds
            if (adfPoint.X < boundingBox.XMin)
                boundingBox.XMin = adfPoint.X;
            if (adfPoint.X > boundingBox.XMax)
                boundingBox.XMax = adfPoint.X;
            if (adfPoint.Y < boundingBox.YMin)
                boundingBox.YMin = adfPoint.Y;
            if (adfPoint.Y > boundingBox.YMax)
                boundingBox.YMax = adfPoint.Y;
        }

        #endregion
    }
}