Common Custom tasks
Common_CustomTasks_CSharp\FindNearTask_CSharp\FindNearTask.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.
// 

// Embedded images
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.point.bmp", "image/bmp")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.pointD.bmp", "image/bmp")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.pointU.bmp", "image/bmp")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.point.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.point_selected.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.point_hover.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.polyline.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.polyline_selected.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.polyline_hover.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.polygon.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.polygon_selected.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.polygon_hover.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.clearInput.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.clearInput_selected.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.clearInput_hover.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.selectFeature.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.selectFeature_selected.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.selectFeature_hover.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.selectTaskResult.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.selectTaskResult_selected.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.selectTaskResult_hover.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.preview.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.contextMenuRemove.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.contextMenuZoomTo.gif", "image/gif")]
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.images.contextMenuViewTable.gif", "image/gif")]

// Embedded JavaScript
[assembly: System.Web.UI.WebResource("FindNearTask_CSharp.Resources.javascript.FindNearTask.js", "text/javascript")]

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

        #region Instance Varables

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

        // Tracking variables shared internally
        private string _taskFrameworkJobID = null;
        private bool _newTaskResultsPanel = false;
        private string _originalDataSetName = null;

        // Internal references to buddied Map and TaskResults controls
        ESRI.ArcGIS.ADF.Web.UI.WebControls.Map _map = null;
        ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults _taskResults = null;

        #endregion

        #region Properties

        #region Internal Properties

        // The task's input parameters.  See InputParameters.cs for the parameters' class definition.
        internal FindNearTask_CSharp.InputParameters TaskInput
        {
            get
            {
                // Attempt to retrieve the input parameters from state
                InputParameters inputParameters = StateManager.GetProperty("FindNearTaskInputParameters") as InputParameters;
                if (inputParameters == null)
                {
                    // 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
                    this.TaskInput = inputParameters;
                }
                // Return the parameters
                return inputParameters;
            }
            set 
            {
                // Store the passed-in parameters in state
                StateManager.SetProperty("FindNearTaskInputParameters", value);
            }
        }

        // 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.
        internal FindNearTask_CSharp.GraphicsResource TaskInputGraphicsResource
        {
            get
            {
                // Attempt to retrieve the task's graphics resource from state
                FindNearTask_CSharp.GraphicsResource taskGraphicsResource =
                    this.StateManager.GetProperty("TaskInputGraphicsResource") as FindNearTask_CSharp.GraphicsResource;
                if (taskGraphicsResource == null)
                {
                    // Since the task's graphics resource is not yet in state, create it and store it
                    string resourceName = string.Format("{0} Input Graphics Resource", this.ClientID);
                    taskGraphicsResource = new FindNearTask_CSharp.GraphicsResource(resourceName, this.MapInstance);
                    this.StateManager.SetProperty("TaskInputGraphicsResource", taskGraphicsResource);
                }

                // Return the resource
                return taskGraphicsResource;
            }
        }

        #endregion

        #region Private Properties

        // Whether to define the search area by selecting a task result
        private bool SearchAreaByTaskResult
        {
            get
            {
                // Check whether the property is stored in state.  If not, return false.  If so, return
                // the value from state.
                if (StateManager.GetProperty("SearchAreaByTaskResult") == null)
                    return false;
                else
                    return (bool)StateManager.GetProperty("SearchAreaByTaskResult");
            }
            set
            {
                // Store the passed-in value in state
                StateManager.SetProperty("SearchAreaByTaskResult", value);
            }
        }

        // Returns the buddied Map
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Map MapInstance
        {
            get
            {
                return this.TaskResultsInstance.MapInstance;
            }
        }

        // Returns the first buddied TaskResults container that has a buddied Map
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults TaskResultsInstance
        {
            get
            {
                // Check whether the internal task results variable is null
                if (_taskResults == null)
                {
                    // Iterate through the the task's buddied TaskResults containers.  Exit the loop when the first one wtih
                    // a buddied Map control is found.
                    for (int i = 0; i < this.TaskResultsContainers.Count; i++)
                    {
                        _taskResults = ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(
                            this.TaskResultsContainers[i].Name, this.Page) as ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults;
                        if (_taskResults != null && _taskResults.MapInstance != null)
                            break;
                    }
                }
                // Return the internal variable storing the reference to the TaskResults
                return _taskResults;
            }
        }

        // Counts the number of times the task has executed.  Used to apply a unique ID to new TaskResultsPanels displaying resutls.
        private int SearchCount
        {
            get
            {
                if (this.StateManager.GetProperty("OperationCount") == null)
                    return 1;
                else
                    return (int)this.StateManager.GetProperty("OperationCount");
            }
            set
            {
                this.StateManager.SetProperty("OperationCount", value);
            }
        }

        #endregion

        #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 TabularResultOptions TabularResultOptions
        {
            get
            {
                // Attempt retrieving the tabular result options from state.  If not found, return the default
                // option.  Otherwise, return the value from state.
                object obj = StateManager.GetProperty("TabularResultOptions");
                return (obj == null) ? TabularResultOptions.TableInFloatingPanel : (TabularResultOptions)obj;
            }
            // Store the passed-in value in state
            set { StateManager.SetProperty("TabularResultOptions", value); }
        }

        /// <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 string GeometryServiceUrl
        {
            get
            {
                // Attempt retrieving the geometry service URL from state.  If not found, return the default
                // URL.  Otherwise, return the value from state.
                object obj = StateManager.GetProperty("GeometryServiceUrl");
                return (obj == null) ? "http://tasks.arcgisonline.com/arcgis/services/Geometry/GeometryServer" : (string)obj;
            }
            // Store the passed-in value in state
            set { StateManager.SetProperty("GeometryServiceUrl", value); }
        }

        #endregion

        #endregion

        #region ASP.NET WebControl Life Cycle Event Handlers

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

            // Do not create child controls if the control is being rendered at design-time
            if (this.DesignMode)
                return;

            // Table to format the task interface and add it to the task's controls
            System.Web.UI.WebControls.Table taskInterfaceTable = new System.Web.UI.WebControls.Table();
            Controls.Add(taskInterfaceTable);
            
            // Row for the distance label, distance textbox, and units label
            System.Web.UI.WebControls.TableRow tableRow = new System.Web.UI.WebControls.TableRow();
            taskInterfaceTable.Rows.Add(tableRow);

            // Cell for the search area tools label
            System.Web.UI.WebControls.TableCell tableCell = 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
            System.Web.UI.WebControls.Label label = 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)
                this.CreateTaskResultsPanelContextMenu();

            // 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
            this.TaskResultsInstance.NodeAdded += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeAddedEventHandler(TaskResults_NodeAdded);
            this.TaskResultsInstance.NodeRemoved += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeRemovedEventHandler(TaskResults_NodeRemoved);
            this.TaskResultsInstance.NodeClicked += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickedEventHandler(TaskResults_NodeClicked);
        }

        protected override void Render(System.Web.UI.HtmlTextWriter writer)
        {
            base.Render(writer);

            if (DesignMode)
                return;

            // Register the JavaScript file containing the functions used by the task
            if (!this.Page.ClientScript.IsStartupScriptRegistered("FindNearTaskScript"))
                this.Page.ClientScript.RegisterStartupScript(typeof(FindNearTask_CSharp.FindNearTask),
                    "FindNearTaskScript", FindNearTask_CSharp.ResourceUtility.GetJavascript(this, "FindNearTask.js",
                    typeof(FindNearTask_CSharp.FindNearTask)));

            // Registered the TaskResultsPanel's scripts
            FindNearTask_CSharp.TaskResultsPanel.RegisterScripts(this);

            // Find all the toolbars on the page and construct a string containing their IDs
            System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar> toolbarList =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControls<
                ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar>(this.Page.Controls);
            string toolbarIDs = "";
            foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar toolbar in toolbarList)
                toolbarIDs += toolbar.ClientID + ",";
            if (toolbarIDs.Length > 0)
                toolbarIDs = toolbarIDs.Substring(0, toolbarIDs.Length - 1);

            // Construct JavaScript to pass server-side task initialization data to the client
            string initializationScriptKey = "FindNearTaskInitializationScript";
            if (!Page.ClientScript.IsStartupScriptRegistered(initializationScriptKey))
            {
                string initializationScript = @"
                    function findNearTaskInit() {{
                        _callbackFunctionString = ""{0}"";
                    
                        var toolbarIDs = '{1}';
                        toolbarIDs = toolbarIDs.split(',');
                        for (var i = 0; i < toolbarIDs.length; i++)
                        {{
                            var toolbar = $find(toolbarIDs[i]);
                            if (toolbar)
                                toolbar.add_onToolSelected(toolSelected);
                        }}

                        _selectionLayerLabel = $get('{2}');                    
                        _selectionLayerDropDownList = $get('{3}');
                        _searchDistanceTextBox = $get('{4}');

                        _unitsDropDownList = $get('{5}');
                        _buddyMapID = '{6}';
                        _findButton = $get('{7}');
                        _activityIndicatorDiv = $get('{8}');
                        _buddyTaskResultsID = '{9}';

                        initAsyncRequestHandler();
                    }}

                    Sys.Application.add_init(findNearTaskInit);";
                initializationScript = string.Format(initializationScript, this.CallbackFunctionString,
                    toolbarIDs, _selectionLayerLabel.ClientID,
                    _selectionLayerDropDownList.ClientID, _searchDistanceTextBox.ClientID, _unitsDropDownList.ClientID,
                    this.MapInstance.ClientID, _findButton.ClientID, _activityIndicatorDiv.ClientID,
                    this.TaskResultsInstance.ClientID);

                // Register the script on the page
                Page.ClientScript.RegisterStartupScript(this.GetType(), initializationScriptKey, initializationScript, true);
            }

            this.TaskResultsInstance.ShowRowCount = false;
        }

        #endregion

        #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.
        void TaskResults_NodeRemoved(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeRemovedEventArgs args)
        {
            // Check whether results are being shown in a TaskResultsPanel
            if (this.TabularResultOptions == TabularResultOptions.TableInFloatingPanel)
            {
                // 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
                ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode =
                    this.FindChildGraphicsLayerNode(args.Node);

                // Check whether a GraphicsLayerNode was found
                if (graphicsLayerNode != null)
                {
                    // Construct JavaScript to find the TaskResultsPanel corresponding to the GraphicsLayerNode and 
                    // destroy it.
                    string taskResultsPanelClientID = string.Format("{0}_{1}_TaskResultsPanel", this.ClientID, 
                        graphicsLayerNode.NodeID);
                    string disposeResultsPanelJavaScript = @"
                    var taskResultsPanel = $find('{0}');
                    if (taskResultsPanel)
                    {{
                        taskResultsPanel.hide(false);
                        taskResultsPanel.dispose();
                    }}
                    var element = $get('{0}');
                    if (element)
                        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
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult disposeResultsPanelCallbackResult =
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(disposeResultsPanelJavaScript);
                    this.CallbackResults.Add(disposeResultsPanelCallbackResult);
                }
            }

            // Retrieve any FeatureNodes that are children of the removed node.  Note this call will also check whether
            // the current node is a FeatureNode.
            System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode> featureNodes =
                new System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode>();
            this.FindChildFeatureNodes(args.Node, ref featureNodes);

            // Check whether any FeatureNodes were found
            if (featureNodes.Count > 0)
            {
                // For each FeatureNode, construct JavaScript to reset the corresponding entry in the client-side array 
                // storing FeatureNode IDs to null
                string removeNodeIDsJavaScript = "";
                foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode featureNode in featureNodes)
                    removeNodeIDsJavaScript += string.Format("_featureNodeIDs['{0}'] = null;", featureNode.NodeID);

                // Wrap the JavaScript in a CallbackResult and add it to the task's collection
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult removeNodeIDsCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(removeNodeIDsJavaScript);
                this.CallbackResults.Add(removeNodeIDsCallbackResult);
            }

            // Add any CallbackResults created by the task to the calling controls results so they are processed on the client
            this.CopyCallbackResultsToCaller(this.CallbackResults);
        }

        // Fires when a node is added to the TaskResults control buddied to the Task.  
        void TaskResults_NodeAdded(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs args)
        {
            // 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 (this._newTaskResultsPanel && (this.TabularResultOptions == TabularResultOptions.TableInFloatingPanel))
            {
                // Get the GraphicsLayerNode that is an ancestor or descendant of the current node
                ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode =
                    this.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 != null && graphicsLayerNode.Layer.DataSet.DataSetName != this._originalDataSetName)
                {
                    // Create the TaskResultsPanel that will be used to display results
                    string taskResultsPanelID = string.Format("{0}_TaskResultsPanel", graphicsLayerNode.NodeID);
                    string taskResultsPanelTitle = string.Format("{0} Results - {1}", this.Title,
                        graphicsLayerNode.Layer.TableName);
                    FindNearTask_CSharp.TaskResultsPanel taskResultsPanel =
                        this.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.                    
                    string checkDockJavaScript = string.Format("$find('{0}')._checkDock();", taskResultsPanel.ClientID);
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult callbackResultToRemove = null;
                    foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult callbackResult in taskResultsPanel.CallbackResults)
                    {
                        if (callbackResult.Parameters[0] as string == checkDockJavaScript)
                        {
                            callbackResultToRemove = callbackResult;
                            break;
                        }
                    }
                    if (callbackResultToRemove != null)
                        taskResultsPanel.CallbackResults.Remove(callbackResultToRemove);

                    // Get the name of the resource containing the results GraphicsLayer and call SetLayer to associate the 
                    // TaskResultsPanel with the GraphicsLayer
                    string resourceName = graphicsLayerNode.Layer.DataSet.DataSetName;
                    taskResultsPanel.SetLayer(graphicsLayerNode.Layer as ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer,
                        resourceName, this.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
                    this.CallbackResults.CopyFrom(taskResultsPanel.CallbackResults);

                    // Reset the flag indicating whether a new TaskResultsPanel needs to be created
                    this._newTaskResultsPanel = false;
                }
            }

            // Find any FeatureNodes that are descendants of the added node
            System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode> featureNodes =
                new System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode>();
            this.FindChildFeatureNodes(args.Node, ref featureNodes);
            // Check whether any FeatureNodes were found
            if (featureNodes.Count > 0)
            {
                // 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.
                string defineNodeIDsJavaScript = "";
                foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode featureNode in featureNodes)
                    defineNodeIDsJavaScript += string.Format("_featureNodeIDs['{0}'] = true;", featureNode.NodeID);

                // Put the JavaScript in a CallbackResult and add it to the task's collection
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult defineNodeIDsCallbackResult =
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(defineNodeIDsJavaScript);
                this.CallbackResults.Add(defineNodeIDsCallbackResult);
            }

            // 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 (this.GetCallingControl(this.Page).UniqueID != this.UniqueID)
                this.CopyCallbackResultsToCaller(this.CallbackResults);
        }

        // 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, 
        void TaskResults_NodeClicked(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeEventArgs args)
        {
            // Check whether the passed-in node is a feature node and the define search area by task result tool is active
            if (this.SearchAreaByTaskResult && (args.Node is ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode))
            {
                // Get a reference to the clicked node as a FeatureNode
                ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode clickedFeatureNode = args.Node as
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode;

                // Loop through the data of the FeatureNode until the feature geometry is found
                foreach (object value in clickedFeatureNode.DataRow.ItemArray)
                {
                    if (value is ESRI.ArcGIS.ADF.Web.Geometry.Geometry)
                    {
                        // Put the feature geometry in a geometry array
                        ESRI.ArcGIS.ADF.Web.Geometry.Geometry[] inputGeometries =
                            new ESRI.ArcGIS.ADF.Web.Geometry.Geometry[1];
                        inputGeometries[0] = value as ESRI.ArcGIS.ADF.Web.Geometry.Geometry;

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

                        // Update the task's user input and buffer graphics layer
                        this.UpdateInputGraphicsLayers();

                        // Create a callback result to call the client-side function that hides the activity indicator
                        string hideActivityIndicatorJavaScript = "hideUpdateAreaIndicator();";
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult hideIndicatorCallbackResult =
                            ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(hideActivityIndicatorJavaScript);
                        this.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 (this.GetCallingControl(this.Page).UniqueID != this.UniqueID)
                            this.CopyCallbackResultsToCaller(this.CallbackResults);
                        break;
                    }
                }
            }
        }

        // Fires when the custom GraphicLayer context menu is closed.
        private void GraphicsLayerContextMenu_Dismissed(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuDismissedEventArgs args)
        {
            this.TaskResultsInstance.ContextMenuDismissed(_graphicsLayerContextMenu, args);
        }

        // Fires when an item on the custom GraphicsLayer context menu is clicked
        private void GraphicsLayerContextMenu_ItemClicked(object sender, ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItemEventArgs args)
        {
            // Get the node on which the context menu was displayed
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = 
                TaskResultsInstance.Nodes.FindByNodeID(args.Context) as ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode;

            // Check the text of the item clicked
            switch (args.Item.Text)
            {
                case "Zoom To Selected Features":
                    if (graphicsLayerNode == null || this.MapInstance == null) 
                        return;
                    bool hasFeaturesSelected = false;

                    // Get the GraphicsLayer associated with the node
                    ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer graphicsLayer = graphicsLayerNode.Layer;

                    // Declare an envelope to store the combined extent of all features in the layer
                    ESRI.ArcGIS.ADF.Web.Geometry.Envelope adfEnvelope = 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 (int i = 0; i < graphicsLayer.Rows.Count; i++)
                    {
                        if ((bool)graphicsLayer.Rows[i][graphicsLayer.IsSelectedColumn])
                        {
                            hasFeaturesSelected = true;

                            ESRI.ArcGIS.ADF.Web.Geometry.Geometry rowGeometry = 
                                graphicsLayer.GeometryFromRow(graphicsLayer.Rows[i]);
                            adfEnvelope.Union(rowGeometry);
                        }
                    }

                    if (!hasFeaturesSelected) return;

                    // If combined envelope width or height is zero, zoom in the amount specified by the 
                    // ZoomToPointFactor property
                    if (adfEnvelope.Width == 0 || adfEnvelope.Height == 0)
                    {
                        ESRI.ArcGIS.ADF.Web.Geometry.Point adfPoint = new ESRI.ArcGIS.ADF.Web.Geometry.Point(adfEnvelope.XMax, adfEnvelope.YMax);
                        ESRI.ArcGIS.ADF.Web.Geometry.Envelope fullExtentEnvelope = this.MapInstance.GetFullExtent();

                        double widthMargin = (fullExtentEnvelope.Width / TaskResultsInstance.ZoomToPointFactor) / 2;
                        double heightMargin = (fullExtentEnvelope.Height / TaskResultsInstance.ZoomToPointFactor) / 2;

                        ESRI.ArcGIS.ADF.Web.Geometry.Envelope zoomToEnvelope = 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;

                        this.MapInstance.Extent = zoomToEnvelope;
                    }
                    else
                    {
                        // Apply the combined feature extent to the map
                        this.MapInstance.Extent = adfEnvelope;
                    }

                    // Copy the map's callback results to the context menu so the extent change is processed on the client
                    _graphicsLayerContextMenu.CallbackResults.CopyFrom(this.MapInstance.CallbackResults);
                    break;
                case "Remove":
                    if (this.MapInstance == null || graphicsLayerNode == null) 
                        return;

                    // Check whether there is a GraphicsLayer associated with the node
                    if (graphicsLayerNode.Layer != null)
                    {
                        // Remove the GraphicsLayer associated with the node from the map and refresh the layer's
                        // parent resource
                        string graphicsResourceName = graphicsLayerNode.RemoveFromMap(this.TaskResultsInstance);
                        this.MapInstance.RefreshResource(graphicsResourceName);

                        // Copy the map's callback results to the context menu so the map is updated on the client
                        _graphicsLayerContextMenu.CallbackResults.CopyFrom(this.MapInstance.CallbackResults);
                    }

                    // Remove the node and refresh the buddied TaskResults control
                    graphicsLayerNode.Remove();
                    this.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);
                    break;
                case "View Attribute Table":
                    // Construct JavaScript to call the client-side Web ADF function to display the TaskResultsPanel
                    string taskResultsPanelClientID = string.Format("{0}_{1}_TaskResultsPanel", this.ClientID, args.Context);
                    string showTaskResultsPanelJavaScript =string.Format("showFloatingPanel('{0}', false);", taskResultsPanelClientID);
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult showTaskResultsPanelCallbackResult =
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(showTaskResultsPanelJavaScript);
                    _graphicsLayerContextMenu.CallbackResults.Add(showTaskResultsPanelCallbackResult);
                    break;
            }
        }

        #endregion

        #region ICallbackEventHandler Overrides - GetCallbackResults

        public override string GetCallbackResult()
        {
            // Convert the callback argument string into a name-value collection using the Web ADF's callback 
            // argument parsing utility
            System.Collections.Specialized.NameValueCollection callbackArgsCollection = 
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(
                this.CallbackEventArgument);

            // Get the task's job ID
            _taskFrameworkJobID = callbackArgsCollection["taskJobID"];

            // Get the callback event argument
            string eventArg = callbackArgsCollection["EventArg"];

            switch (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
                    string searchDistance = callbackArgsCollection["searchDistance"];
                    string units = callbackArgsCollection["units"];

                    // Make sure a search distance was entered
                    if (!string.IsNullOrEmpty(searchDistance))
                    {
                        float newSearchDistance;
                        if (float.TryParse(searchDistance, out newSearchDistance))
                            this.TaskInput.SearchDistance = newSearchDistance;

                        this.TaskInput.Units = units;

                        // Update the graphics layer showing the search area
                        this.UpdateInputGraphicsLayers();

                        // Copy the callback results from the map so the graphics layer update is processed
                        // on the client
                        this.CallbackResults.CopyFrom(MapInstance.CallbackResults);
                    }

                    // Create callback result to hide the task's activity indicator
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult hideActivityIndicatorCallbackResult =
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript("hideUpdateAreaIndicator();");
                    this.CallbackResults.Add(hideActivityIndicatorCallbackResult);
                    break;
                case "toggleSelectByTaskResult":
                    // Set the flag indicating wheteher the define search area by task result tool
                    this.SearchAreaByTaskResult = bool.Parse(callbackArgsCollection["Value"]);
                    break;
                case "toggleAddToInput":
                    // Set the flag indicating whether newly defined geometries are to be added to those already defined
                    this.TaskInput.AddToInputGeometry = !this.TaskInput.AddToInputGeometry;
                    break;
                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.
                    string[] searchLayerParameters = 
                        callbackArgsCollection["searchLayerParameters"].Split(new char[] { ',' });
                    // Update the task's input parameters with the node's information
                    this.TaskInput.SearchResource = searchLayerParameters[0];
                    this.TaskInput.SearchLayer = searchLayerParameters[1];
                    break;
            }
            return base.GetCallbackResult();
        }

        // Updates the geometries of the user input and buffer graphics layers
        internal void UpdateInputGraphicsLayers()
        {
            // If there are not defined input geometries, exit the function
            if (this.TaskInput.UserInputGeometries == null || this.TaskInput.UserInputGeometries.Length == 0)
                return;

            // Get the graphics layer displaying the user defined geometries and clear it
            ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer inputGraphicsLayer = 
                this.TaskInputGraphicsResource.GetUserInputLayer(this.MapInstance.MapResourceManagerInstance)
                as ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer;
            ESRI.ArcGIS.ADF.Web.Geometry.Geometry[] adfSearchGeometries = this.TaskInput.UserInputGeometries;
            inputGraphicsLayer.Clear();

            // Get the search distance
            float searchDistance = TaskInput.SearchDistance;

            // Make sure the user specified geometries and distance are valid
            if (adfSearchGeometries != null && !float.IsNaN(searchDistance) && searchDistance > 0.0)
            {
                ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement inputGraphicElement = null;

                // Create a symbol for the user specified geometry
                for (int i = 0; i < adfSearchGeometries.Length; i++)
                {
                    if (adfSearchGeometries[i] is ESRI.ArcGIS.ADF.Web.Geometry.Point)
                    {
                        ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleMarkerSymbol adfSearchPointSymbol =
                            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);
                    }
                    else if (adfSearchGeometries[i] is ESRI.ArcGIS.ADF.Web.Geometry.Polyline)
                    {
                        ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleLineSymbol adfSearchLineSymbol =
                            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);
                    }
                    else if (adfSearchGeometries[i] is ESRI.ArcGIS.ADF.Web.Geometry.Polygon)
                    {
                        ESRI.ArcGIS.ADF.Web.Display.Symbol.SimpleFillSymbol adfSearchFillSymbol =
                            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);
                    }

                    inputGraphicsLayer.Add(inputGraphicElement);
                }

                // Get the buffer geometry for the search geometry and distance
                this.TaskInput.BufferGeometry = this.BufferGeometries(adfSearchGeometries, searchDistance, this.TaskInput.Units);

                // Create a graphic element for the buffer and add it to the graphics layer
                ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement bufferElement =
                    new ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicElement(this.TaskInput.BufferGeometry, 
                        System.Drawing.Color.Red, 75);
                ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer bufferGraphicsLayer =
                    this.TaskInputGraphicsResource.GetBufferLayer(this.MapInstance.MapResourceManagerInstance);
                bufferGraphicsLayer.Clear();
                bufferGraphicsLayer.Add(bufferElement);
            }

            // Refresh the resource containing the input graphics layer
            MapInstance.RefreshResource(this.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 (this.GetCallingControl(this.Page).UniqueID != this.MapInstance.UniqueID)
                this.CopyCallbackResultsToCaller(this.MapInstance.CallbackResults);
        }

        // Clears the user input and buffer graphics layers
        internal void ClearTaskInput()
        {
            // Temporarily store AddToInputGeometry
            bool addToInputGeometry = this.TaskInput.AddToInputGeometry;

            // Set AddToInputGeometry to false and pass null to SetUserInputGeometries to clear the input
            // geometries
            this.TaskInput.AddToInputGeometry = false;
            this.TaskInput.SetUserInputGeometries(null);

            // Revert the value of AddToInputGeometry
            this.TaskInput.AddToInputGeometry = addToInputGeometry;

            // Set the buffer geometry to null
            this.TaskInput.BufferGeometry = null;

            // Retrieve and clear the user input and buffer graphics layers
            ESRI.ArcGIS.ADF.Web.Display.Graphics.ElementGraphicsLayer elementGraphicsLayer =
                this.TaskInputGraphicsResource.GetUserInputLayer(this.MapInstance.MapResourceManagerInstance);
            elementGraphicsLayer.Clear();

            elementGraphicsLayer = this.TaskInputGraphicsResource.GetBufferLayer(
                this.MapInstance.MapResourceManagerInstance);
            elementGraphicsLayer.Clear();

            // Update the resource containing the input graphics layers
            this.MapInstance.RefreshResource(this.TaskInputGraphicsResource.ResourceName);
        }

        #endregion

        #region Web ADF Task Overrides - ExecuteTask, GetGISResourceItemDependencies

        // Called when the Find button is clicked
        public override void ExecuteTask()
        {
            if (this.TaskInput.BufferGeometry == null)
                return;

            // Get the buddied map resource manager
            ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager mapResourceManager = 
                this.MapInstance.MapResourceManagerInstance;
            // Get the map resource item containing the resource to be searched
            ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem mapResourceItem = 
            mapResourceManager.ResourceItems.Find(this.TaskInput.SearchResource);
            // Make sure the search resource is initialized
            if (mapResourceItem.Resource == null || !mapResourceItem.Resource.Initialized)
                mapResourceItem.InitializeResource();

            // Get a reference to the resource item's map resource
            ESRI.ArcGIS.ADF.Web.DataSources.IMapResource commonMapResource = 
                mapResourceItem.Resource as ESRI.ArcGIS.ADF.Web.DataSources.IMapResource;                

            // Use the resource to create a query functionality
            string queryFunctionalityName = string.Format("{0}_QueryFunctionality", this.ID);
            ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality queryFunctionality = 
                commonMapResource.CreateFunctionality(typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), 
                queryFunctionalityName) as ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality;

            // Instantiate a spatial filter with the buffer geometry
            ESRI.ArcGIS.ADF.Web.SpatialFilter adfSpatialFilter = new ESRI.ArcGIS.ADF.Web.SpatialFilter();               
            adfSpatialFilter.Geometry = this.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
            System.Data.DataTable resultsDataTable = 
                queryFunctionality.Query(null, TaskInput.SearchLayer, adfSpatialFilter);

            // Make sure results were found and they can be shown as a graphics layer
            if (resultsDataTable == null || !(resultsDataTable is 
            ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)) 
                return;

            // Convert query results to a graphics layer
            ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer resultsGraphicsLayer = 
                ESRI.ArcGIS.ADF.Web.Converter.ToGraphicsLayer(resultsDataTable);
            
            // Retrieve and apply the layer format of the search layer to the results
            ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat layerFormat = 
                ESRI.ArcGIS.ADF.Web.UI.WebControls.LayerFormat.FromMapResourceManager(mapResourceManager, 
                this.TaskInput.SearchResource, this.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
            foreach (System.Data.DataRow dataRow in resultsGraphicsLayer.Rows)
                dataRow[resultsGraphicsLayer.IsSelectedColumn] = true;

            // Create a graphics dataset and add the results graphics layer to it
            ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet resultsGraphicsDataSet =
                new ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet();
            resultsGraphicsDataSet.DataSetName = string.Format("{0} Results ({1})", this.Title, 
                resultsGraphicsLayer.Rows.Count);
            resultsGraphicsDataSet.Tables.Add(resultsGraphicsLayer);

            // Get the number of searches performed and append the ordinal suffix 
            string searchCount = this.SearchCount.ToString();
            string searchCountDescription;
            switch (searchCount.Substring(searchCount.Length - 1))
            {
                case "1":
                    searchCount += "st";
                    break;
                case "2":
                    searchCount += "nd";
                    break;
                case "3":
                    searchCount += "rd";
                    break;
                default:
                    searchCount += "th";
                    break;
            }
            // 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
            this.SearchCount++;

            // 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_CSharp.TabularResultOptions.TreeViewInTaskResults)
                this.Results = resultsGraphicsDataSet;
            else if (this.TabularResultOptions == FindNearTask_CSharp.TabularResultOptions.TableInFloatingPanel)
            {
                this.Results = this.CreateCustomTaskResultsNode(resultsGraphicsDataSet);

                if (resultsGraphicsLayer.Rows.Count > 0)
                {
                    this._newTaskResultsPanel = true;
                    this._originalDataSetName = resultsGraphicsDataSet.DataSetName;                        
                }
            }
        }        

        // 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 override System.Collections.Generic.List<
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency> GetGISResourceItemDependencies()
        {
            return null;
        }

        #endregion

        #region Private Methods

        // Retrieves queryable layers from all resources in the passed-in MapResourceManager
        private void GetQueryableLayers(ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceManager mapResourceManager, 
            out System.Collections.Generic.Dictionary<string, string[]> layerNamesByResources, 
            out System.Collections.Generic.Dictionary<string, string[]> layerIDsByResources)
        {
            layerNamesByResources = new System.Collections.Generic.Dictionary<string, string[]>();
            layerIDsByResources = new System.Collections.Generic.Dictionary<string, string[]>();

            bool initializedResourceManager = false;
            try
            {
                // If the MapResourceManager is not initialize, do so here
                if (!mapResourceManager.Initialized)
                {
                    mapResourceManager.Initialize();
                    initializedResourceManager = true;
                }

                ESRI.ArcGIS.ADF.Web.UI.WebControls.MapResourceItem mapResourceItem;
                
                // Loop through the resources in the MapResourceManager
                for (int i = 0; i < mapResourceManager.ResourceItems.Count; i++)
                {
                    mapResourceItem = mapResourceManager.ResourceItems[i];

                    // If the current resource is not displayed in the Toc, skip to the next
                    if (!mapResourceItem.DisplaySettings.DisplayInTableOfContents) 
                        continue;

                    // Ensure the resource is initialized
                    mapResourceItem.InitializeResource();

                    // Get the map resource from the resource item
                    ESRI.ArcGIS.ADF.Web.DataSources.IMapResource commonMapResource = 
                        mapResourceItem.Resource as ESRI.ArcGIS.ADF.Web.DataSources.IMapResource;
                    if (commonMapResource == null) 
                        continue;

                    // If the resource does not support querying, skip to the next
                    if (!commonMapResource.SupportsFunctionality(typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality))) 
                        continue;

                    // Create a query functoinality form the resource
                    ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality commonQueryFunctionality = commonMapResource.CreateFunctionality(
                        typeof(ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality), System.Guid.NewGuid().ToString("N"))
                        as ESRI.ArcGIS.ADF.Web.DataSources.IQueryFunctionality;

                    // Get arrays containing the resources queryable layer names and IDs
                    string[] layerIDs = null;
                    string[] layerNames = null;
                    commonQueryFunctionality.GetQueryableLayers(null, out layerIDs, out 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);
                }
            }
            catch { }
            finally
            {
                // If the MapResourceManager was initialized in this method, call Dispose to restore it to its initial state
                if (initializedResourceManager)
                    mapResourceManager.Dispose();
            }

            return;
        }

        #region Task Results Setup Methods

        // Creates a TaskResultNode customized for use with a TaskResultsPanel
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode CreateCustomTaskResultsNode(
            ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsDataSet graphicsDataSet)
        {
            // Instantiate a TaskResultNode
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode taskResultNode = 
                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 (int i = 0; i < graphicsDataSet.Tables.Count; i++)
            {
                // Check whether the current table contains data for a GraphicsLayer
                if (graphicsDataSet.Tables[i] is ESRI.ArcGIS.ADF.Web.Display.Graphics.GraphicsLayer)
                {
                    // Create a GraphicsLayerNode with the data from the current table.  Make it so the node only
                    // contains the legend swatch.
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode = 
                        this.TaskResultsInstance.CreateDataTableNode(graphicsDataSet.Tables[i], false, true, false, 
                        graphicsDataSet.Tables[i].TableName, graphicsDataSet.Tables[i].TableName) as 
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode;

                    // Associate a custom context menu with the node.  This allows opening the associated TaskResultsPanel.
                    this.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
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode 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);
                }
            }

            // Call SetupTaskResultNode to initialize the functionality of the parent node
            TaskResultsInstance.SetupTaskResultNode(this, _taskFrameworkJobID, this.TaskInput, taskResultNode);

            return taskResultNode;
        }

        // Creates a TaskResultsPanel with the passed-in ID and title
        private FindNearTask_CSharp.TaskResultsPanel CreateTaskResultsPanel(string ID, string title)
        {
            // Initialize the TaskResultsPanel
            FindNearTask_CSharp.TaskResultsPanel taskResultsPanel = new FindNearTask_CSharp.TaskResultsPanel();
            taskResultsPanel.ID = ID;
            taskResultsPanel.Visible = false;
            taskResultsPanel.CopyAppearance(this);
            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
            this.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(this, this.CallbackFunctionString);
            return taskResultsPanel;
        }

        // Instantiates and initializes the context menu to show on task results if results are being displayed in a 
        // TaskResultsPanel.
        private void 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(this.Font);
            _graphicsLayerContextMenu.UseDefaultWebResources = this.UseDefaultWebResources;

            // Wire item clicked and menu dismissed event handlers
            _graphicsLayerContextMenu.ItemClicked += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItemClickedEventHandler(GraphicsLayerContextMenu_ItemClicked);
            _graphicsLayerContextMenu.Dismissed += 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuDismissedEventHandler(GraphicsLayerContextMenu_Dismissed);

            // Add a menu item to zoom to selected features
            ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem contextMenuItem = 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.ContextMenuItem();
            contextMenuItem.ImageUrl = ResourceUtility.GetImage("contextMenuZoomTo.gif", this, typeof(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", this, typeof(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", this, typeof(FindNearTask));
            contextMenuItem.Text = "View Attribute Table";
            _graphicsLayerContextMenu.Items.Add(contextMenuItem);

            // Add the context menu to the task's controls collection
            this.Controls.Add(_graphicsLayerContextMenu);
        }

        // Method that hooks up the context menu to graphics layer node.
        void SetupContextMenu(ESRI.ArcGIS.ADF.Web.UI.WebControls.GraphicsLayerNode graphicsLayerNode)
        {
            TaskResultsInstance.SetupContextMenu(_graphicsLayerContextMenu, graphicsLayerNode);
        }

        #endregion

        #region User Interface Creation Methods

        // Instantiates and populates FindNearTask's toolbar
        private void CreateTaskToolbar()
        {
            // Instantiate the toolbar and add it to the cell
            _taskToolbar = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Toolbar();

            // Initialize toolbar properties
            _taskToolbar.UseDefaultWebResources = this.UseDefaultWebResources;
            _taskToolbar.ID = "FindNearTaskToolbar";
            _taskToolbar.WebResourceLocation = this.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
            string[] toolbarGroupNamesArray =
                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 == null || toolbarGroupNamesArray.Length < 1)
                _taskToolbar.Group = string.Format("{0}_FindNearTaskToolbarGroup", MapInstance.UniqueID);
            else
                _taskToolbar.Group = toolbarGroupNamesArray[0];

            // Encapsulate the map in a BuddyControl and add it to the toolbar's buddy control collection
            ESRI.ArcGIS.ADF.Web.UI.WebControls.BuddyControl mapBuddyControl =
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.BuddyControl(MapInstance.UniqueID);
            _taskToolbar.BuddyControls.Add(mapBuddyControl);
        }

        // Creates the tool to define search area by drawing a point
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool CreateSearchPointTool()
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool addSearchPointTool = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool();
            addSearchPointTool.Name = "SearchAreaByPoint";
            addSearchPointTool.ServerActionAssembly = "FindNearTask_CSharp";
            addSearchPointTool.ServerActionClass = "FindNearTask_CSharp.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", this, typeof(FindNearTask));
            addSearchPointTool.SelectedImage = ResourceUtility.GetImage("point_selected.gif", this, typeof(FindNearTask));
            addSearchPointTool.HoverImage = ResourceUtility.GetImage("point_hover.gif", this, typeof(FindNearTask));
            return addSearchPointTool;
        }

        // Creates the tool to define search area by drawing a polyline
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool CreateSearchPolylineTool()
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool addSearchPolylineTool = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool();
            addSearchPolylineTool.Name = "SearchAreaByLine";
            addSearchPolylineTool.ServerActionAssembly = "FindNearTask_CSharp";
            addSearchPolylineTool.ServerActionClass = "FindNearTask_CSharp.SearchAreaByPolyline";
            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", this, typeof(FindNearTask));
            addSearchPolylineTool.SelectedImage = ResourceUtility.GetImage("polyline_selected.gif", this, typeof(FindNearTask));
            addSearchPolylineTool.HoverImage = ResourceUtility.GetImage("polyline_hover.gif", this, typeof(FindNearTask));
            return addSearchPolylineTool;
        }

        // Creates the tool to define search area by drawing a polygon
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool CreateSearchPolygonTool()
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool addSearchPolygonTool = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool();
            addSearchPolygonTool.Name = "SearchAreaByPolygon";
            addSearchPolygonTool.ServerActionAssembly = "FindNearTask_CSharp";
            addSearchPolygonTool.ServerActionClass = "FindNearTask_CSharp.SearchAreaByPolygon";
            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", this, typeof(FindNearTask));
            addSearchPolygonTool.SelectedImage = ResourceUtility.GetImage("polygon_selected.gif", this, typeof(FindNearTask));
            addSearchPolygonTool.HoverImage = ResourceUtility.GetImage("polygon_hover.gif", this, typeof(FindNearTask));
            return addSearchPolygonTool;
        }

        // Creates the tool to define search area by selecting a task result
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool CreateInputByTaskResultTool()
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool selectInputByTaskResultTool = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool();
            selectInputByTaskResultTool.Name = "SearchAreaByTaskResult";
            selectInputByTaskResultTool.ServerActionAssembly = "FindNearTask_CSharp";
            selectInputByTaskResultTool.ServerActionClass = "FindNearTask_CSharp.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", this, typeof(FindNearTask));
            selectInputByTaskResultTool.SelectedImage = ResourceUtility.GetImage("selectTaskResult_selected.gif", this, typeof(FindNearTask));
            selectInputByTaskResultTool.HoverImage = ResourceUtility.GetImage("selectTaskResult_hover.gif", this, typeof(FindNearTask));
            return selectInputByTaskResultTool;
        }

        // Creates the tool to define search area by selecting features from the map
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool CreateInputByMapTool()
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool selectInputByMapTool = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Tool();
            selectInputByMapTool.Name = "SearchAreaBySelection";
            selectInputByMapTool.ServerActionAssembly = "FindNearTask_CSharp";
            selectInputByMapTool.ServerActionClass = "FindNearTask_CSharp.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", this, typeof(FindNearTask));
            selectInputByMapTool.SelectedImage = ResourceUtility.GetImage("selectFeature_selected.gif", this, typeof(FindNearTask));
            selectInputByMapTool.HoverImage = ResourceUtility.GetImage("selectFeature_hover.gif", this, typeof(FindNearTask));
            return selectInputByMapTool;
        }

        // Creates the command for clearing task input geometry from the map
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.Command CreateClearInputCommand()
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.Command clearInputCommand = new ESRI.ArcGIS.ADF.Web.UI.WebControls.Command();
            clearInputCommand.Name = "ClearInput";
            clearInputCommand.ServerActionAssembly = "FindNearTask_CSharp";
            clearInputCommand.ServerActionClass = "FindNearTask_CSharp.ClearInput";
            clearInputCommand.ToolTip = "Clear search area features and buffers from the map";
            clearInputCommand.DefaultImage = ResourceUtility.GetImage("clearInput.gif", this, typeof(FindNearTask));
            clearInputCommand.SelectedImage = ResourceUtility.GetImage("clearInput_selected.gif", this, typeof(FindNearTask));
            clearInputCommand.HoverImage = ResourceUtility.GetImage("clearInput_hover.gif", this, typeof(FindNearTask));
            return clearInputCommand;
        }

        // Creates the controls for specifying the layer to select features from for task input
        private void 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";
        }

        // Creates the checkbox determining whether newly defined search areas are merged with those previously defined
        private System.Web.UI.WebControls.CheckBox CreateAddToInputCheckbox()
        {
            System.Web.UI.WebControls.CheckBox addToInputCheckbox = 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;
        }

        // Creates the label for the search distance textbox
        private System.Web.UI.WebControls.Label CreateSearchDistanceLabel()
        {
            System.Web.UI.WebControls.Label label = 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;
        }

        // Creates and populates the search distance textbox and units drop-down list
        private void 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 = float.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
            ESRI.ArcGIS.ADF.Web.DataSources.IMapFunctionality primaryResourceMapFunctionality =
                this.MapInstance.GetFunctionality(MapInstance.PrimaryMapResource);
            // Set the task's input units to those of the primary map resource
            if (this.TaskInput.Units == null)
                this.TaskInput.Units = primaryResourceMapFunctionality.Units.ToString();

            _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
            System.Array adfUnitArray = System.Enum.GetValues(typeof(ESRI.ArcGIS.ADF.Web.DataSources.Units));
            int selectedIndex = 0;
            int dropDownIndex = -1;
            foreach (int i in adfUnitArray)
            {
                string unit = adfUnitArray.GetValue(i).ToString();
                if ((unit != "Unknown") && (unit != "DecimalDegrees") && (unit != "Points"))
                {
                    _unitsDropDownList.Items.Add(unit);
                    dropDownIndex++;
                }

                if (unit == this.TaskInput.Units)
                    selectedIndex = dropDownIndex;
            }
            _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()");
        }

        // Creates the label for the search layer drop-down list
        private System.Web.UI.WebControls.Label CreateSearchLayerLabel()
        {
            System.Web.UI.WebControls.Label label = 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;
        }

        // Creates and populates the search layer drop-down list
        private void 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 (this.TaskInput.SearchLayer == null)
            {
                // Dictionaries to hold the names and IDs of queryable layers
                System.Collections.Generic.Dictionary<string, string[]> layerNamesByResourceDictionary;
                System.Collections.Generic.Dictionary<string, string[]> layerIDsByResourceDictionary;

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

                // Loop through all the items in the layer names dictionary, adding each to the search layer and 
                // selection layer lists
                foreach (System.Collections.Generic.KeyValuePair<string, string[]> layerNamesPair in
                    layerNamesByResourceDictionary)
                {
                    // Get the name of the resource from the current key value pair
                    string resourceName = layerNamesPair.Key;

                    // Get the array of layer names for the current resource
                    string[] layerNamesArray = layerNamesPair.Value;

                    // Loop through the names, creating a tree view plus node below the current resource node for each
                    for (int i = 0; i < layerNamesArray.Length; i++)
                    {
                        // Get the layer ID for the current layer
                        string layerID = layerIDsByResourceDictionary[resourceName][i];

                        if (_searchLayerDropDownList.Items.Count == 0)
                        {
                            // Set the layer and resource input parameters based on the current node
                            TaskInput.SearchResource = resourceName;
                            TaskInput.SearchLayer = layerID;
                        }

                        // 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.
                        System.Web.UI.WebControls.ListItem layerListItem = 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);
                    }
                }
            }
        }

        // Creates the Find button
        private void 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
            string executeTaskJavaScript = string.Format(@"
                executeTask('',""{0}"");",
                this.CallbackFunctionString, _taskToolbar.ClientID, MapInstance.ClientID);

            // Wire the JavaScript to execute when the Find button is clicked
            _findButton.Attributes.Add("onclick", executeTaskJavaScript);
        }

        // Creates the activity indicator div, which contains the image and label to be shown when the search 
        // area is being updated
        private void 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
            System.Web.UI.HtmlControls.HtmlImage activityIndicator = new System.Web.UI.HtmlControls.HtmlImage();
            string activityIndicatorUrl = ESRI.ArcGIS.ADF.Web.UI.WebControls.ResourceUtility.GetImage(
                "callbackActivityIndicator.gif", this, typeof(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
            System.Web.UI.WebControls.Label label = 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);
        }

        #endregion

        #region GeoSpatial Utility Methods

        // Calculates the buffer of the input geometries
        private ESRI.ArcGIS.ADF.Web.Geometry.Geometry BufferGeometries(
            ESRI.ArcGIS.ADF.Web.Geometry.Geometry[] adfInputGeometryArray, float bufferDistance, string units)
        {
            // Put the ArcGIS Server SOAP polygon in an ArcGIS Server SOAP geometry array to pass to the
            // buffer operation
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapInputGeometryArray =
                new ESRI.ArcGIS.ADF.ArcGISServer.Geometry[adfInputGeometryArray.Length];
            for (int i = 0; i < adfInputGeometryArray.Length; i++)
                agsSoapInputGeometryArray[i] =
                    ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.FromAdfGeometry(adfInputGeometryArray[i]);

            // Get a reference to an ArcGIS Server Geometry service
            ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy geometryServerProxy =
                new ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy(this.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
            ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapBufferSpatialReference =
                this.CreateOperationSpatialReference(agsSoapInputGeometryArray, geometryServerProxy);

            // Use the units specified by the user to create an ArcGIS Server LinearUnit object
            ESRI.ArcGIS.ADF.ArcGISServer.LinearUnit agsSoapBufferUnits =
                new ESRI.ArcGIS.ADF.ArcGISServer.LinearUnit();
            agsSoapBufferUnits.WKID = this.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.
            double[] bufferDistances = new double[1];
            bufferDistances[0] = System.Convert.ToDouble(bufferDistance);

            ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapInputSpatialReference =
                this.GetSpatialReference(agsSoapInputGeometryArray[0]);

            // Execute the buffer operation via the geometry service  
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsBufferGeometryArray = geometryServerProxy.Buffer(
                agsSoapInputSpatialReference, agsSoapBufferSpatialReference, agsSoapInputSpatialReference,
                bufferDistances, agsSoapBufferUnits, true, agsSoapInputGeometryArray);

            // Retrieve the buffer polygon from the array of result geometries
            ESRI.ArcGIS.ADF.Web.Geometry.Geometry adfBufferGeometry =
                ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.Converter.ToAdfGeometry(agsBufferGeometryArray[0]);

            if (adfBufferGeometry.SpatialReference == null)
                adfBufferGeometry.SpatialReference = adfInputGeometryArray[0].SpatialReference;

            return adfBufferGeometry;
        }

        // Returns the well-known ID of the passed-in unit
        private int GetWkidByUnitName(string unitName)
        {
            int wkid = -1;
            switch (unitName)
            {
                case "Inches":
                    wkid = 109009;
                    break;
                case "Feet":
                    wkid = 9003;
                    break;
                case "Yards":
                    wkid = 109002;
                    break;
                case "Miles":
                    wkid = 9035;
                    break;
                case "NauticalMiles":
                    wkid = 9030;
                    break;
                case "Millimeters":
                    wkid = 109007;
                    break;
                case "Centimeters":
                    wkid = 109006;
                    break;
                case "Meters":
                    wkid = 9001;
                    break;
                case "Kilometers":
                    wkid = 9036;
                    break;
                case "Decimeters":
                    wkid = 109005;
                    break;
                default:
                    // default is Kilometers
                    wkid = 9036;
                    break;
            }

            return wkid;
        }

        // Creates a spatial reference that minimizes distortion in the vicinity of the passed-in geometriess
        private ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference CreateOperationSpatialReference(
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapGeometryArray,
            ESRI.ArcGIS.ADF.ArcGISServer.GeometryServerProxy geometryServerProxy)
        {
            // Get the polygon's minimum enclosing rectangle (MER)
            ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN agsSoapBoundingEnvelope = 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
            ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapSpatialReference =
                this.GetSpatialReference(agsSoapGeometryArray[0]);
            if (agsSoapSpatialReference is ESRI.ArcGIS.ADF.ArcGISServer.ProjectedCoordinateSystem)
            {
                // Create an ArcGIS Server spatial reference initalized to use the WGS 1984 coordinate system
                ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapGeographicSpatialReference =
                    new ESRI.ArcGIS.ADF.ArcGISServer.GeographicCoordinateSystem();
                agsSoapGeographicSpatialReference.WKID = 4326;
                agsSoapGeographicSpatialReference.WKIDSpecified = true;

                // Place the input MER in an array for the project operation
                ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapInputGeometryArray =
                    new ESRI.ArcGIS.ADF.ArcGISServer.Geometry[1];
                agsSoapInputGeometryArray[0] = agsSoapBoundingEnvelope;

                // Execute the projection
                ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapOutputGeometryArray =
                    geometryServerProxy.Project(agsSoapSpatialReference, agsSoapGeographicSpatialReference,
                    true, null, null, agsSoapInputGeometryArray);

                // Retrieve the projected MER from the results array
                agsSoapBoundingEnvelope = agsSoapOutputGeometryArray[0] as ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN;
            }

            // Get the latitude (Y coordinate) at the center of the MER
            double centerLatitude = 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
            string hotineObliqueMercatorDefinition = @"
            PROJCS[""World_Hotine"",
            GEOGCS[""GCS_WGS_1984"",
            DATUM[""D_WGS_1984"",
            SPHEROID[""WGS_1984"",6378137.0,298.257223563]],
            PRIMEM[""Greenwich"",0.0],
            UNIT[""Degree"",0.0174532925199433]],
            PROJECTION[""Hotine_Oblique_Mercator_Two_Point_Natural_Origin""],
            PARAMETER[""False_Easting"",0.0],
            PARAMETER[""False_Northing"",0.0],
            PARAMETER[""Latitude_Of_1st_Point"",{0}],   
            PARAMETER[""Latitude_Of_2nd_Point"",{1}],
            PARAMETER[""Scale_Factor"",1.0],
            PARAMETER[""Longitude_Of_1st_Point"",{2}],
            PARAMETER[""Longitude_Of_2nd_Point"",{3}],
            PARAMETER[""Latitude_Of_Center"",{4}],
            UNIT[""Meter"",1.0]]";

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

            // Create the spatial reference
            ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapBufferSpatialReference =
                geometryServerProxy.FindSRByWKT(customHotineObliqueCylindricalMercator, null, true, true);

            return agsSoapBufferSpatialReference;
        }

        // Returns the spatial reference of an ArcGIS Server SOAP Geometry
        private ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference GetSpatialReference(
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry agsSoapGeometry)
        {
            ESRI.ArcGIS.ADF.ArcGISServer.SpatialReference agsSoapSpatialReference = null;
            if (agsSoapGeometry is ESRI.ArcGIS.ADF.ArcGISServer.PolygonN)
            {
                ESRI.ArcGIS.ADF.ArcGISServer.PolygonN agsSoapPolygon = agsSoapGeometry as
                    ESRI.ArcGIS.ADF.ArcGISServer.PolygonN;
                agsSoapSpatialReference = agsSoapPolygon.SpatialReference;
            }
            else if (agsSoapGeometry is ESRI.ArcGIS.ADF.ArcGISServer.PolylineN)
            {
                ESRI.ArcGIS.ADF.ArcGISServer.PolylineN agsSoapPolyline = agsSoapGeometry as
                    ESRI.ArcGIS.ADF.ArcGISServer.PolylineN;
                agsSoapSpatialReference = agsSoapPolyline.SpatialReference;
            }
            else if (agsSoapGeometry is ESRI.ArcGIS.ADF.ArcGISServer.PointN)
            {
                ESRI.ArcGIS.ADF.ArcGISServer.PointN agsSoapPoint = agsSoapGeometry as
                    ESRI.ArcGIS.ADF.ArcGISServer.PointN;
                agsSoapSpatialReference = agsSoapPoint.SpatialReference;
            }

            return agsSoapSpatialReference;
        }

        // Gets the bounding extent of the passed-in ArcGIS Server SOAP geometries
        private ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN GetBoundingExtent(
            ESRI.ArcGIS.ADF.ArcGISServer.Geometry[] agsSoapGeometryArray)
        {
            // Instantiate an envelope with max values minimized and min values maximized
            ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN agsSoapBoundingBox =
                new ESRI.ArcGIS.ADF.ArcGISServer.EnvelopeN();
            agsSoapBoundingBox.XMin = double.MaxValue;
            agsSoapBoundingBox.XMax = double.MinValue;
            agsSoapBoundingBox.YMin = double.MaxValue;
            agsSoapBoundingBox.YMax = double.MinValue;

            for (int i = 0; i < agsSoapGeometryArray.Length; i++)
            {
                ESRI.ArcGIS.ADF.ArcGISServer.Geometry agsSoapGeometry = agsSoapGeometryArray[i];
                if (agsSoapGeometry is ESRI.ArcGIS.ADF.ArcGISServer.PolygonN)
                {
                    ESRI.ArcGIS.ADF.ArcGISServer.PolygonN agsSoapPolygon = agsSoapGeometry as
                        ESRI.ArcGIS.ADF.ArcGISServer.PolygonN;
                    // Iterate through all the polygon's vertices
                    for (int j = 0; j < agsSoapPolygon.RingArray.Length; j++)
                    {
                        ESRI.ArcGIS.ADF.ArcGISServer.Ring agsSoapRing = agsSoapPolygon.RingArray[j];
                        for (int k = 0; k < agsSoapRing.PointArray.Length; k++)
                        {
                            // 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
                            ESRI.ArcGIS.ADF.ArcGISServer.PointN agsSoapCurrentPoint = agsSoapRing.PointArray[k]
                                as ESRI.ArcGIS.ADF.ArcGISServer.PointN;

                            if (agsSoapCurrentPoint.X < agsSoapBoundingBox.XMin)
                                agsSoapBoundingBox.XMin = agsSoapCurrentPoint.X;
                            else if (agsSoapCurrentPoint.X > agsSoapBoundingBox.XMax)
                                agsSoapBoundingBox.XMax = agsSoapCurrentPoint.X;

                            if (agsSoapCurrentPoint.Y < agsSoapBoundingBox.YMin)
                                agsSoapBoundingBox.YMin = agsSoapCurrentPoint.Y;
                            else if (agsSoapCurrentPoint.Y > agsSoapBoundingBox.YMax)
                                agsSoapBoundingBox.YMax = agsSoapCurrentPoint.Y;
                        }
                    }
                }
                else if (agsSoapGeometry is ESRI.ArcGIS.ADF.ArcGISServer.PolylineN)
                {
                    ESRI.ArcGIS.ADF.ArcGISServer.PolylineN agsSoapPolyline = agsSoapGeometry as
                        ESRI.ArcGIS.ADF.ArcGISServer.PolylineN;
                    // Iterate through all the polyline's vertices
                    for (int j = 0; j < agsSoapPolyline.PathArray.Length; j++)
                    {
                        ESRI.ArcGIS.ADF.ArcGISServer.Path agsSoapPath = agsSoapPolyline.PathArray[j];
                        for (int k = 0; k < agsSoapPath.PointArray.Length; k++)
                        {
                            // 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
                            ESRI.ArcGIS.ADF.ArcGISServer.PointN agsSoapCurrentPoint = agsSoapPath.PointArray[k]
                                as ESRI.ArcGIS.ADF.ArcGISServer.PointN;

                            if (agsSoapCurrentPoint.X < agsSoapBoundingBox.XMin)
                                agsSoapBoundingBox.XMin = agsSoapCurrentPoint.X;
                            else if (agsSoapCurrentPoint.X > agsSoapBoundingBox.XMax)
                                agsSoapBoundingBox.XMax = agsSoapCurrentPoint.X;

                            if (agsSoapCurrentPoint.Y < agsSoapBoundingBox.YMin)
                                agsSoapBoundingBox.YMin = agsSoapCurrentPoint.Y;
                            else if (agsSoapCurrentPoint.Y > agsSoapBoundingBox.YMax)
                                agsSoapBoundingBox.YMax = agsSoapCurrentPoint.Y;
                        }
                    }
                }
                else if (agsSoapGeometry is ESRI.ArcGIS.ADF.ArcGISServer.PointN)
                {
                    ESRI.ArcGIS.ADF.ArcGISServer.PointN agsSoapPoint = agsSoapGeometry
                        as ESRI.ArcGIS.ADF.ArcGISServer.PointN;

                    agsSoapBoundingBox.XMin = agsSoapPoint.X - .1;
                    agsSoapBoundingBox.XMax = agsSoapPoint.X + .1;
                    agsSoapBoundingBox.YMin = agsSoapPoint.Y - .1;
                    agsSoapBoundingBox.YMax = agsSoapPoint.Y + .1;
                }
            }

            return agsSoapBoundingBox;
        }

        #endregion

        #region TaskResultNode Utility 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;
        }

        // Retrieves FeatureNodes that are descendants of the passed-in node if available
        private void FindChildFeatureNodes(ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode treeViewPlusNode,
            ref System.Collections.Generic.List<ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode> featureNodes)
        {
            ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode featureNode = treeViewPlusNode
                as ESRI.ArcGIS.ADF.Web.UI.WebControls.FeatureNode;

            if (featureNode != null)
                featureNodes.Add(featureNode);

            if (treeViewPlusNode.Nodes.Count > 0)
            {
                foreach (ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNode childNode in treeViewPlusNode.Nodes)
                    this.FindChildFeatureNodes(childNode, ref featureNodes);
            }

            return;
        }

        #endregion

        #region Asynchronous Request Utility Methods

        // Retrieves the control that initiated the asynchronous request
        private System.Web.UI.Control GetCallingControl(System.Web.UI.Page page)
        {
            if (page == null) return null;
            if (page.IsCallback)
            {
                string controlID = page.Request.Params["__CALLBACKID"];
                System.Web.UI.Control control = page.FindControl(controlID);
                return control;
            }
            // For 9.3 we could be using a partial postback instead
            else if (page.IsPostBack && System.Web.UI.ScriptManager.GetCurrent(page) != null &&
                System.Web.UI.ScriptManager.GetCurrent(page).IsInAsyncPostBack)
            {
                string controlID = System.Web.UI.ScriptManager.GetCurrent(page).AsyncPostBackSourceElementID;
                System.Web.UI.Control control = page.FindControl(controlID);
                return control;
            }
            else return null; //Not an asyncronous request
        }

        // Copies the passed-in callback results to the callback results collection of the control that initiated
        // the asynchronous request
        private void CopyCallbackResultsToCaller(
            ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResultCollection callbackResults)
        {
            System.Web.UI.Control callingControl = this.GetCallingControl(this.Page);
            if (callingControl is ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl)
            {
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl compositeControl = callingControl as
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl;
                compositeControl.CallbackResults.CopyFrom(callbackResults);
            }
            else if (callingControl is ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl)
            {
                ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl webControl = callingControl as
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.WebControl;
                webControl.CallbackResults.CopyFrom(callbackResults);
            }
        }

        #endregion

        #endregion
    }
}