Common Custom tasks
Common_CustomTasks_CSharp\AsyncTask_CSharp\BasicAsyncTask.cs
// Copyright 2010 ESRI
// 
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
// 
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
// 
// See the use restrictions.
// 

namespace AsyncTask_CSharp
{
    // Task specifying an architecture for submitting a job for asynchronous processing.  This architecture
    // can be used for jobs that may take a long time to process.  Once operation input is submitted via
    // the task's interface, the job is started and a response is returned to the client immediately, 
    // allowing the user to submit additional jobs.  In progress jobs are indicated via a task result node.
    // Job status checking is managed by JavaScript timeouts that issue callbacks to the server.
    [System.Web.UI.ToolboxData(@"<{0}:BasicAsyncTask runat=""server"" Width=""200px"" Transparency=""35"" 
        BackColor=""White"" TitleBarColor=""WhiteSmoke"" TitleBarSeparatorLine=""True"" 
        TitleBarHeight=""20px"" BorderColor=""LightSteelBlue"" BorderStyle=""Outset"" BorderWidth=""1px"" 
        Font-Names=""Verdana"" Font-Size=""8pt"" ForeColor=""Black""> </{0}:BasicAsyncTask>")]
    public class BasicAsyncTask : ESRI.ArcGIS.ADF.Web.UI.WebControls.FloatingPanelTask
    {
        // Creates the Execute button
        protected override void CreateChildControls()
        {
            base.CreateChildControls();

            // Instantiate the Execute button, set its text, and add it to the task's cotnrols
            System.Web.UI.HtmlControls.HtmlInputButton executeButton = 
                new System.Web.UI.HtmlControls.HtmlInputButton();
            executeButton.Value = "Execute";
            Controls.Add(executeButton);

            // Create JavaScript that calls the Web ADF executeTask function.  We pass executeTask 
            // the task's callback function string so the call is linked to the current task
            // instance.
            string executeTaskJavaScript = string.Format("ESRI.ADF.Tasks.executeTask('',\"{0}\");", 
                this.CallbackFunctionString);
            // Wire the JavaScript to fire when the Execute button is clicked
            executeButton.Attributes.Add("onclick", executeTaskJavaScript);
        }

        #region FloatingPanelTask Overrides - ExecuteTask, GetGISResourceItemDependencies

        // Submits and checks the status of asynchronous jobs, then creates a task results node
        // accordingly
        public override void ExecuteTask()
        {
            System.Collections.Specialized.NameValueCollection callbackArgsCollection =
                ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(
                this.CallbackEventArgument);
            
            // Retrieve the task and async job IDs from the callback arguments.  The task job ID is 
            // created by the Task framework and is used to link the task to task results.  The 
            // asynchronousJobID is created in this implementation and is used to check the status
            // of the job submitted for asynchronous processing.
            string taskFrameworkJobID = callbackArgsCollection["taskJobID"];
            string asynchronousJobID = callbackArgsCollection["asyncJobID"];

            // Initialize the task results
            this.Results = null;

            // If an async job ID was not included in the callback arguments, then the callback was
            // generated by the initial click of the Execute button.  The job therefore needs to be
            // submitted for asynchronous processing.
            if (string.IsNullOrEmpty(asynchronousJobID))
            {
                // Call method to submit the job and get the job's ID
                asynchronousJobID = SubmitSimJob();
                // Uncomment to test against the BufferPoints tool of the BufferTools Geoprocessing service
                //asynchronousJobID = SubmitBufferPointsJob();
 
                asynchronousJobID = asynchronousJobID.Trim();
                // Add the job ID to the list of in-progress jobs
                this.JobsInProgress.Add(asynchronousJobID);
            }

            // Get the job's status
            ESRI.ArcGIS.ADF.Web.DataSources.JobStatus jobStatus;
            jobStatus = GetSimJobStatus(asynchronousJobID);
            // Uncomment to test against the BufferPoints tool of the BufferTools Geoprocessing service
            //jobStatus = GPFunctionality.GetJobStatus(asynchronousJobID);

            // Construct a task result node to show the status
            ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode taskResultNode = 
                new ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode();
            // Pass the node instance and the task job ID to the node setup method
            TaskResults.SetupTaskResultNode(this, taskFrameworkJobID, null, taskResultNode);

            // Give the node an indentation
            taskResultNode.TextCellStyleAttributes.Add("padding-left", "5px");
            // Set up the node to do nothing when clicked
            taskResultNode.ClickBehavior = 
                ESRI.ArcGIS.ADF.Web.UI.WebControls.TreeViewPlusNodeClickBehavior.None;

            // Convert the job status to a string
            string jobStatusString = jobStatus.ToString();

            // Check the status and take action accordingly
            switch (jobStatus)
            {
                // Job succeeded
                case ESRI.ArcGIS.ADF.Web.DataSources.JobStatus.Succeeded:
                    // Get the messages for the completed job
                    string jobResultMessages = GetSimJobResult(asynchronousJobID);
                    // Uncomment to test against the BufferPoints tool of the BufferTools Geoprocessing service
                    //string jobResultMessages = GetBufferPointsResult(asynchronousJobID);

                    // Set the text of the task result node to include the task's title, the job 
                    // status, and the job messages
                    taskResultNode.Text = string.Format("{0} {1} <BR> {2}", this.Title, 
                        jobStatusString, jobResultMessages);

                    // Remove the job from the in-progress list
                    this.JobsInProgress.Remove(asynchronousJobID);

                    break;

                // Job is still in progress
                default:
                    // Set the task result node's text to include the task's title and job status
                    taskResultNode.Text = string.Format("{0} {1}", this.Title, jobStatusString);
                    // Set the task result node's image to be the Web ADF's callback activity indicator
                    taskResultNode.LegendCollapsedImage = 
                        "/aspnet_client/ESRI/WebADF/images/callbackActivityIndicator.gif";
                    taskResultNode.LegendExpandedImage = 
                        "/aspnet_client/ESRI/WebADF/images/callbackActivityIndicator.gif";

                    // Construct JavaScript to set a timeout that calls the Web ADF startJob function.  
                    // This function issues a callback with the specified arguments and task ID.  In 
                    // this implementation, this callback is handled to check the async job's status
                    string checkStatusJavaScript = string.Format("window.setTimeout(" +
                        "\"ESRI.ADF.Tasks.startJob('asyncJobID={0}',\\\"{1}\\\",{2})\", 2000);",
                        asynchronousJobID, this.CallbackFunctionString, taskFrameworkJobID);

                    // Package the JavaScript as a CallbackResult and add it to the task's collection
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult checkStatusCallbackResult =
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackResult.CreateJavaScript(checkStatusJavaScript);
                    this.CallbackResults.Add(checkStatusCallbackResult);

                    break;
            }

            // Return the job status task result node as the task's results
            Results = taskResultNode;
        }

        // The task has no resource dependencies, so this method is overriden to return an empty list
        public override System.Collections.Generic.List<
            ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency> GetGISResourceItemDependencies()
        {
            return new System.Collections.Generic.List<
                ESRI.ArcGIS.ADF.Web.UI.WebControls.GISResourceItemDependency>();
        }

        #endregion

        #region Instance Methods

        // Submits a new job and returns the job's ID
        private string SubmitSimJob()
        {
            // Return a new GUID
            return System.Guid.NewGuid().ToString();
       }

        // Retrieves the status of the job with the specified ID
        private ESRI.ArcGIS.ADF.Web.DataSources.JobStatus GetSimJobStatus(string JobID)
        {
            // Generate a random number between 0 and 3
            System.Random randomizer = new System.Random();
            int randomInteger = randomizer.Next(0, 4);

            // If the random number is 0, return a successful result.  Otherwise, return a result
            // indicating the job is still in progress.
            if (randomInteger == 0)
                return ESRI.ArcGIS.ADF.Web.DataSources.JobStatus.Succeeded;
            else
                return ESRI.ArcGIS.ADF.Web.DataSources.JobStatus.Submitted;
        }

        // Retrieves the result of the job having the passed-in ID
        private string GetSimJobResult(string JobID)
        {
            // Return a string with the Job ID and time
            return string.Format("JobID: {0} <br />Time Returned: {1}", JobID,
                System.DateTime.Now.ToLongTimeString());
        }

        #endregion

        #region Instance Properties

        // Returns a list containing the IDs of submitted jobs
        private System.Collections.Generic.List<string> JobsInProgress
        {
            get
            {
                // Try retrieving the jobs list from state
                System.Collections.Generic.List<string> jobsInProgress = 
                    StateManager.GetProperty("JobsInProgress") 
                    as System.Collections.Generic.List<string>;

                // If the list was not found, instantiate a new list and store it in state
                if (jobsInProgress == null)
                {
                    jobsInProgress = new System.Collections.Generic.List<string>();
                    StateManager.SetProperty("JobsInProgress", jobsInProgress);
                }
                
                return jobsInProgress;
            }
        }

        // Retrieves the task's first buddied TaskResults control
        private ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults TaskResults
        {
            get
            {
                ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults taskResults = null;
                if (TaskResultsContainers[0] != null)
                    taskResults = ESRI.ArcGIS.ADF.Web.UI.WebControls.Utility.FindControl(
                        TaskResultsContainers[0].Name, Page) as ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResults;

                return taskResults;
            }
        }

        #endregion

        #region Members for Testing Against the BufferTools Geoprocessing Service

        ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.GeoprocessingFunctionality _geoprocessingFunctionality = null;
        
        // Submits a new BufferPoints geoprocessing operation
        private string SubmitBufferPointsJob()
        {
            // Retrieve an array containing information about BufferPoints' parameters
            string gpTaskName = "BufferPoints";
            ESRI.ArcGIS.ADF.Web.DataSources.GPToolInfo gpToolInfo = GPFunctionality.GetTask(gpTaskName);
            ESRI.ArcGIS.ADF.Web.DataSources.GPParameterInfo[] gpParameterInfoArray = gpToolInfo.ParameterInfo;

            // Get a reference to the operation's input graphics layer from the parameter array
            ESRI.ArcGIS.ADF.Web.DataSources.GPFeatureGraphicsLayer inputGpFeatureGraphicsLayer = 
                (ESRI.ArcGIS.ADF.Web.DataSources.GPFeatureGraphicsLayer)gpParameterInfoArray[0].Value;
            ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer inputFeatureGraphicsLayer = 
                inputGpFeatureGraphicsLayer.Layer;

            // Create a point and add it to the input graphics layer
            ESRI.ArcGIS.ADF.Web.Geometry.Point adfPoint = new ESRI.ArcGIS.ADF.Web.Geometry.Point(730324, 2309070);
            inputFeatureGraphicsLayer.Add(adfPoint);
            
            // Initialize the input linear unit (distance) to be 300 miles
            ESRI.ArcGIS.ADF.Web.DataSources.GPLinearUnit gpLinearUnit = 
                new ESRI.ArcGIS.ADF.Web.DataSources.GPLinearUnit();
            gpLinearUnit.Units = ESRI.ArcGIS.ADF.Web.DataSources.Units.Miles;
            gpLinearUnit.Value = 300;

            // Package the input parameters in an array and submit the job
            ESRI.ArcGIS.ADF.Web.DataSources.GPValue[] gpValueArray = new ESRI.ArcGIS.ADF.Web.DataSources.GPValue[2];
            gpValueArray[0] = inputGpFeatureGraphicsLayer;
            gpValueArray[1] = gpLinearUnit;
            string jobID = GPFunctionality.SubmitJob(gpTaskName, gpValueArray);
            
            // Return the submitted job's ID
            return jobID;
        }

        // Retrieves the result of the BufferPoints job having the passed-in ID
        private string GetBufferPointsResult(string jobID)
        {
                // Get the geoprocessing result of the BufferPoints job with the passed-in ID
                ESRI.ArcGIS.ADF.Web.DataSources.GPResult gpResult = 
                    GPFunctionality.GetJobResult("BufferPoints", jobID, null, false);                
                ESRI.ArcGIS.ADF.Web.DataSources.GPValue[] resultsGpValuesArray = gpResult.Values;

                // Get the graphics layer containing the result.  Note that nothing is done with this graphics 
                // layer here; the code to retrieve the result is included to for instructive purposes.
                ESRI.ArcGIS.ADF.Web.DataSources.GPFeatureGraphicsLayer resultsGpFeatureGraphicsLayer =
                    resultsGpValuesArray[0] as ESRI.ArcGIS.ADF.Web.DataSources.GPFeatureGraphicsLayer;
                ESRI.ArcGIS.ADF.Web.Display.Graphics.FeatureGraphicsLayer resultsFeatureGraphicsLayer = 
                    resultsGpFeatureGraphicsLayer.Layer;

                // Return the result messages or an empty string if there are no messages
                if (gpResult.Messages != null)
                    return gpResult.Messages[gpResult.Messages.Length - 1].MessageDesc;
                else
                    return "";
        }
        
        // Gets the geoprocessing functionailty for the first geoprocessing resource referenced by the 
        // GeoprocessingResourceManager having an ID of "GeoprocessingResourceManager1"
        private ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.GeoprocessingFunctionality GPFunctionality
        {
            get
            {
                // Check whether the GP functionality member variable has already been initialized
                if (_geoprocessingFunctionality == null)
                {
                    // Get the geoprocessing resource manager
                    ESRI.ArcGIS.ADF.Web.UI.WebControls.GeoprocessingResourceManager geoprocessingResourceManager =
                        Page.FindControl("GeoprocessingResourceManager1") as
                        ESRI.ArcGIS.ADF.Web.UI.WebControls.GeoprocessingResourceManager;

                    // Get the first resource from the resource manager
                    ESRI.ArcGIS.ADF.Web.DataSources.IGeoprocessingResource geoprocessingResource = 
                        geoprocessingResourceManager.GetResource(0);

                    // Create a geoprocessing functionality from the resource and assign it to the GP functionality member
                    // variable
                    _geoprocessingFunctionality = (ESRI.ArcGIS.ADF.Web.DataSources.ArcGISServer.GeoprocessingFunctionality)
                        geoprocessingResource.CreateFunctionality(
                        typeof(ESRI.ArcGIS.ADF.Web.DataSources.IGeoprocessingFunctionality), null);

                    // Make sure the GP functionality is initialized
                    if (!_geoprocessingFunctionality.Initialized)
                        _geoprocessingFunctionality.Initialize();

                }
                return _geoprocessingFunctionality;
            }
        }

        #endregion
    }
}