Tasks are defined as a set of related actions that provide common results. The concept of a task is presented at a number of levels within ESRI products. ArcGIS Server Geoprocessing services provide server tasks to perform work in a GIS Server. ArcExplorer tasks are designed to consolidate related actions in a desktop client application as well as be distributed distinct components. The Web ADF also provides a task framework to create, integrate, and distribute components, or "Web tasks". In essence, a Web task is a ASP.NET Web control that utilizes and extends the Web ADF task framework to encapsulate custom functionality in a distributable component. The framework includes a set of interfaces and abstract classes to create custom tasks, a set of controls to support task management and results, and a pluggable architecture to integrate a custom task in Visual Studio or ArcGIS Manager. In addition, a set of out-of-the-box task controls are included with the Web ADF to support some of the common GIS functionality requirements in a Web application. Each of these topics will be discussed below:
-
Task support controls
Use of the following controls is optional. They are designed to enhance the Web task runtime experience.
TaskManager The TaskManager control is designed to organize tasks in a Web application. The TaskManager control generates hierarchical XML data that can be used by an ASP.NET navigation control such as a Menu or TreeView. At runtime, nodes in the ASP.NET Menu or TreeView can be used to display the floating panel for a task. It is only visible at design-time. TaskResults The TaskResults control is designed to store the results of tasks that produce ADO.NET DataSet output. The results are presented as nodes in a TreeView. The TaskResults control also enables the ability to zoom or pan to a feature, highlight a feature in a result set, re-run a task, or remove task results.
-
Out-of-the-box task controls
All task controls implement and extend the task framework to be fully integrated with Web ADF application creation capabilities and development environments.
SearchAttributesTask The SearchAttributesTask control enables a developer to select a set of fields, provided by resources, in which to search for the occurrence of a user provided value. At runtime, each field is queried for the occurrence of a value provided by the user. Partial values are permitted. Only feature layers in resources provided by a MapResourceManager can be used. The results are provided as an ADO.NET DataSet to be displayed in a TaskResults control.
QueryAttributesTask The QueryAttributesTask control enables a developer to explicitly define the parameters used to query values in a field. This control enhances the basic query capability of the SearchAttributesTask. At runtime, the a QueryAttributesTask query can provide a drop down list of preset choices or a textbox for general text entry. Each query can also utilize a validator to restrict the values entered. In addition, multiple queries can be grouped together to produce a single result. Only feature layers in resources provided by a MapResourceManager can be used. The results are provided as an ADO.NET DataSet to be displayed in a TaskResults control.
FindPlaceTask The FindPlaceTask control is designed to use ArcWeb services that provide place name searchable layers. The control interface includes a simple textbox to enter a place name. The results (matched places) are provided as an ADO.NET DataSet to be displayed in a TaskResults control.
FindAddressTask The FindAddressTask control utilizes resources in the GeocodeResourceManager to perform geocode operations. The control interface is created dynamically at runtime, based on the inputs required by the geocode resource it will use. The results (match or address candidates) are provided as an ADO.NET DataSet to be displayed in a TaskResults control.
GeoprocessingTask The GeoprocessingTask control utilizes resources in the GeoprocessingResourceManager to execute geoprocessing tasks. The control interface is created dynamically at runtime, based on the inputs required by the geoprocessing resource and task it will use. The results, which include input and output parameters as well as progress messages, are provided as an ADO.NET DataSet to be displayed in a TaskResults control.
EditorTask The EditorTask control provides a suite of tools for Web-based editing of features in both versioned and non-versioned enterprise geodatabases. If configured, specific versions can be selected for editing. The runtime dialog provides tools to modify, add, and remove feature geometry and attributes.
PrintTask
The PrintTask control provides runtime print capabilities to a Web ADF application. It enables users to print a map and task results, along with a legend, scalebar, copyright and north arrow.
-
Task framework
The diagram below illustrates the classes and interfaces in the task framework. The items in red are part of the Web ADF task framework. The items in green illustrate where a developer may choose to customize the task framework and how it is related to existing framework components. In general, the Task and FloatingPanelTask abstract classes implement the basic ITask interface. All can be used to create a custom task depending on the capabilities required by the task. This is all that is required to create a custom task. However, to integrate with other Web ADF components, you have the option of customizing other task components. The TaskDesigner enables developers to customize the Visual Studio design-time verbs available on the custom task control. The IWebConfigurator interface defines how to integrate the custom task into the Manager application. This will enable you to select the custom task from a list when creating a new Web application using Manager. The ITaskResultsContainer interface enables a developer to define a custom container\control to display task results.

The core task interface is ITask in the ESRI.ArcGIS.ADF.Web.UI.WebControls namespace. At the most basic level, a task must implement the ITask interface. The interface definition is provided below:
namespace ESRI.ArcGIS.ADF.Web.UI.WebControls { public interface ITask { string ShowUrl { get;} string Title { get; set;} string ToolTip { get;set;} string NavigationPath { get; set;} BuddyControlCollection TaskResultsContainers { get;} object Results { get;set;} void ExecuteTask(); string TaskActivityIndicatorText { get;} bool GroupResultsByTable { get;set;} bool ShowFieldAttributes { get;set;} bool ShowLegend { get; set;} object Input { get; set;} void Refresh(); void Show(); CallbackResultCollection CallbackResults { get;} string UniqueID { get;} List<GISResourceItemDependency> GetGISResourceItemDependencies(); } }Two abstract classes have been provided for you to handle most of the overhead associated with ITask implementation. The Task and FloatingPanelTask abstract classes are designed to be extended. Use these classes to get started. The main difference between the two: a custom task that extends Task is not contained in a FloatingPanel, a custom task that extends FloatingPanelTask is contained in a FloatingPanel at runtime.
Tasks are essentially ASP.NET composite controls. As a result, both Task and FloatingPanelTask extend
System.Web.UI.WebControls.CompositeControl. CompositeControl implements the ICallbackEventHandler interface, which enables a control to work with AJAX functionality in an ASP.NET Web application.
The IBuddyControlSupport interface defines a method to return the types of controls the custom task can buddy with. The Task and FloatingPanelTask classes define that controls of type ITaskResultsContainer, like the TaskResults control, can be buddied with them.

Walkthrough: Creating a custom task
Drawing on the information discussed thus far, lets create a simple task that takes advantage of the implementation classes provided with the framework. The following walkthrough will provide a simple and instructive guide to creating a custom task. At runtime, the task will contain a button and textbox in a floating panel. Enter text in the textbox and press the button - the textbox content will be added to a TaskResults control along with the current time on the Web server. At design time, the custom task will enable the ability to modify properties exposed via a custom verb on the control in Web page design view. The custom task can also be used and configured from within Manager. The sample code for the custom task walkthrough is located here: Common_SimpleTask .
Using the custom task in Visual Studio at design-time, will look something like:

At runtime, the same Web page is render as follows. This screenshot assumes that some text ("Hello World", "Hello Redlands", "Hello California") has been entered into the textbox and Execute button clicked.
-
Add a reference to the System.Web.dll, System.Drawing.dll and ESRI.ArcGIS.ADF.Web.UI.WebControls.dll. Add using statements defined in the example below.
-
Start with the FloatingPanelTask abstract class. This class will provide a floating panel container at runtime, in which you can add your task controls and client-side logic. All of the out-of-the-box controls extend the FloatingPanelTask. In the example below, the namespace SimpleTask_CSharp contains a class SimpleTask_CSharp that extends FloatingTaskPanel.
-
Since we're building a Web control, provide some information on how this control will appear in a Web page at design-time. Use the ToolboxData attribute to define the content written to the page when the control is dragged onto it. In the example below, the "{0}" indicates a dynamically defined control tag prefix when added to a Web page. Other attributes define that this control will run on the server and other design-time display properties (width is 100 pixels, border width is 1 pixel).
using System; using System.Collections.Generic; using System.Text; using System.Web.UI; using System.Drawing; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; using System.Collections.Specialized; using ESRI.ArcGIS.ADF.Web.UI.WebControls; using System.ComponentModel; namespace SimpleTask_CSharp { [ToolboxData("<{0}:SimpleTask_CSharp runat=\"server\" Width=\"100px\" BorderWidth=\"1px\"> </{0}:SimpleTask_CSharp>")] public class SimpleTask_CSharp: FloatingPanelTask { -
Add some member variables to reference the controls rendered at runtime.
private TextBox textBox = null; private TextBox phantomTextBox = null; private HtmlInputButton button = null; -
Override CreateChildControls() method. This method constructs the visual interface of the task at runtime, as a result, the output must work within a browser client (e.g. contain HTML, JavaScript, etc). This process must be done programmatically, so you can use .NET HTML control classes or render the contents of an ASP.NET control to create the interface. In the example below, the task dialog will contain an HTML table with two cells. Once cell contains a textbox, the other contains a button. The Controls property inherited from the System.Web.UI.WebControls.CompositeControl class is used to add content to the task dialog.
protected override void CreateChildControls() { Controls.Clear(); base.CreateChildControls(); textBox = new TextBox(); textBox.ID = "textBox"; // The phantom text box is an invisible text box that causes the browser to // not do a postback when the enter button is hit. phantomTextBox = new TextBox(); phantomTextBox.ID = "phantomTextBox"; phantomTextBox.Style[HtmlTextWriterStyle.Display] = "none"; button = new HtmlInputButton(); button.ID = "button"; button.Value = ButtonText; Controls.Add(textBox); Controls.Add(phantomTextBox); Controls.Add(button); string getArgumentJS = string.Format("'textBoxValue=' + document.getElementById('{0}').value", textBox.ClientID); string onClick = string.Format("executeTask({0},\"{1}\");", getArgumentJS, CallbackFunctionString); string onKeyDown = string.Format("if(event.keyCode==13){{{0}return false;}}", onClick); button.Attributes.Add("onclick", onClick); textBox.Attributes.Add("onkeydown", onKeyDown); } -
Override the GetCallbackResult() method (from FloatingPanelTask) to set the Input property to the value being submitted by the task at runtime. The Input property is implemented within the FloatingPanelTask. It is of type object, but is often merely a string. It modifies the "input" member variable used in the ExecuteTask() method to process data provided when a user interacts with the control at runtime.
public override string GetCallbackResult() { NameValueCollection keyValColl = CallbackUtility.ParseStringIntoNameValueCollection(_callbackArg); Input = keyValColl["textBoxValue"]; return base.GetCallbackResult(); } -
Add the execution logic for the custom task. Get the input value provided by the user (if any) via the Input property or "input" member variable. Populate the Results property with something to display in the TaskResults control. The DisplayResult() method in TaskResults is responsible for processing the Results object. The type should be a ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleResultTask, or System.Data.DataSet. The TaskResults control will render the contents in a TreeView structure. In this example, a custom SimpleTaskResult is being used to return the input value and time on the Web server. The heading property is the parent node; the detail property defines the child node content.
public override void ExecuteTask() { Results = null; if (Input == null) return; string textBoxValue = input as string; string heading = string.Format("The time on the server is {0}", DateTime.Now.ToShortTimeString()); string detail = string.Format("The value in the text box is: {0}", textBoxValue); SimpleTaskResult str = new SimpleTaskResult(heading, detail); Results = str; } -
Override the Refresh() method to manage the ability to run the task again and refresh the task in the TaskResult control at runtime. Both options are available as a context menu off task result nodes.
public override void Refresh() { string tmp = Input as string; if (!string.IsNullOrEmpty(tmp)) textBox.Text = tmp; base.Refresh(); } -
To complete the custom task, include a mandatory override to maintain a list of dependent resource items, if a dependency exists. In this example, no dependencies are defined.
public override List<GISResourceItemDependency> GetGISResourceItemDependencies() { List<GISResourceItemDependency> list = new List<GISResourceItemDependency>(); return list; } } } -
In preparation for deployment, add a custom tag prefix to uniquely identify this task from other Web controls, tasks, etc. in a Web page. By convention, this information is stored in a separate file compiled with the control. The file is named AssemblyInfo.<language> (for example, AssemblyInfo.cs for C# class library projects). The TagPrefix attribute associates a Web control namespace\assembly with a suggested tag prefix. If the Web control is not already registered to a tag prefix in a Web.config file or via the Register directive, the suggested tagprefix will be used to register the assembly in the page. For this example, use the following definition:
[assembly: TagPrefix("SimpleTask_CSharp", "simpleTaskCS")]
At this point, the custom task can be used within Visual Studio. Build the custom task class library and add the assembly to the Visual Studio toolbox. Here are the steps to do this:- In a Web project, with a Web page active, right-click on the toolbox and select Add Tab. Enter a name for the new tab, for example "Custom Task Controls".
- Right-click on the new tab and select "Choose Items...".
- On the Choose Items dialog, click the Browse button and navigate to the location of the SimpleTask_CSharp.dll and click the Open button. The new control will be added to and selected in the Choose Items dialog. Select and check the box next to the "SimpleTask_CSharp" item. Click OK to exit the dialog.
- The new tab should contain the custom task control.
- Drag the task control and a Web ADF TaskResults control onto a Web page.
- Using the verbs off the custom task control, buddy the TaskResults control to the custom task. The custom task should also function with an ASP.NET navigation control as the out-of-the-box tasks do.
Task Runtime Workflow
At runtime, most tasks will follow a standard set of steps to process user data and generate and display results. Web ADF JavaScript, Web ADF control classes, and Task framework interfaces and implementations all work together to support a task. The following diagram and workflow discussion will walkthrough task execution at runtime using the walkthrough example (included above) where applicable. The green circles indicate the sequence of events in the callback initiated by the task:

Task Execution Steps
1. A user action in the browser calls the executeTask function in a Web ADF JavaScript file display_task.js. By default, the display_task.js is embedded for use by task controls that extend FloatingPanelTask. The user action is defined by the task control, usually as an action on one of the controls or elements that make up the task user interface. Two arguments are important for the executeTask function to work, the callback arguments and the callback function string. The callback arguments provide information on what the user wants the task to do. For example, provide a text string to search a database. The callback function string defines the content of the ASP.NET Callback Manager function, WebForm_DoCallback. For more information, see the general discussion of AJAX and ASP.NET. In the example below, the onclick event of an element (e.g. HTML button) triggers the call to the executeTask JavaScript function.
onclick="executeTask('textBoxValue=The Payload', "WebForm_DoCallback('TaskManager1$SimpleTask_CSharp1',argument,processCallbackResult,context,postBackError,true)");"The executeTask function will do two things before executing the task: 1) assign a job id to track asynchronous requests, and 2) initiate a callback to start the activity indicator in a TaskResults control, if one is buddied to the executing task. The activity indicator callback skips steps 5 and 6 since the user inputs are not being processed yet. After the callback to start the activity indicator, the startJob function is called to process user inputs for the task.
2. The startJob function constructs a list of argument value pairs to provide the task with the information it needs to process the user request. The EventArg argument is used by the task control to determine if it should execute and provide results. Custom arguments provide the input values the task needs to process the callback request. All are packaged as a string in the "argument" parameter of the WebForm_DoCallback function call. An example may include the following where "textBoxValue" is a custom argument:
"EventArg=executeTask&taskJobID=2&textBoxValue=The Payload"
The first parameter in the WebForm_DoCallback function indicates the name of the control that will process the callback request. In this case, the name should match the runtime name of the task control. The XMLHttpRequest object initiates the callback to the server and the Web application passes the callback content to the task control. Since the task control extends FloatingPanelTask, which is a composite control, the RaiseCallbackEvent method in the CompositeControl class captures and stores the argument content in a member variable named "_callbackArg", which will be used during task execution.
3. In the callback lifecycle (discussed in the topic AJAX and ASP.NET) the GetCallbackResult method is called after RaiseCallbackEvent. The GetCallbackResult method in the task control class is used to retrieve the values for the custom argument values used by the task. The values are stored in a property named "Input" which is defined by the ITask interface, implemented in and inherited from the FloatingPanelTask class. The example code below is a typical for the GetCallbackResult method in a custom task. The _callbackArg member variable is parsed using the Web ADF CallbackUtility class to return a set of name\value (argument\value) pairs. In this case, only one custom argument (named "textBoxValue") is of interest to the custom task, so it is assigned to the Input property. A final call to the base class (FloatingPanelTask) GetCallbackResult method continues the standard task processing.
NameValueCollection keyValColl = CallbackUtility.ParseStringIntoNameValueCollection(_callbackArg); Input = keyValColl["textBoxValue"]; return base.GetCallbackResult();
4. The GetCallbackResult method in the FloatingPanelTask is responsible for calling the methods to execute the task and construct the callback response string to display results in the browser. First the _callbackArg variable is parsed to determine the primary event argument "EventArg". FloatingPanelTask looks for two specific values, "startTaskActivityIndicator" and "executeTask". If "startTaskActivityIndicator" then a callback response will display an activity indicator in a TaskResults control, if available. If "executeTask", the ExecuteTask method in the custom task class will be called, followed by a call to the DisplayResults method in the FloatingPanelTask class to generate a callback string.
public override string GetCallbackResult() { NameValueCollection keyValColl = CallbackUtility.ParseStringIntoNameValueCollection(_callbackArg); string eventArg = keyValColl["EventArg"]; string taskJobID = keyValColl["taskJobID"]; if (eventArg == "startTaskActivityIndicator") { StartTaskActivityIndicator(taskJobID); } else if (eventArg == "executeTask") { ExecuteTask(); DisplayResults(taskJobID, Input, Results); } else { return base.GetCallbackResult(); } return CallbackResults.ToString(); }5. The ExecuteTask method in the custom task class is where the bulk of the task processing takes place. Web ADF controls, resources, functionalities, and data source specific APIs can be utilized here to process the input values (stored in the Input property). The results of task execution can be stored in a property named "Results" which will be displayed in a TaskResults control. The Results property is defined by the ITask interface, implemented in and inherited from the FloatingPanelTask class. It can be one of three different types: SimpleTaskResult, DataSet, and TaskResultNode. All are discussed in the custom content discussion of the TaskResults control.
6. The DisplayResults method in the FloatingPanelTask is called and provided with the Results from the execution of the task. DisplayResults adds the Results object to a TaskResults control buddied to the custom task, if available. The TaskResults control itself has a DisplayResults method which is designed to work with predefined types (see previous step) and render HTML content for the control in a browser. Web ADF controls use CallbackResult objects to update browser rendering of the control (see the Control Callbacks discussion for more details). When the TaskResults control changes on the server, it generates a collection of CallbackResults from processing by the client (browser). The CallbackResults property of the custom task control, inherited from the CompositeControl class, will be used to generate the callback response to the browser. As a result, the CallbackResults from the TaskResults control need to be added to the custom task control's CallbackResults property. The DisplayResults method in the FloatingPanelTask control is provided below:
protected virtual void DisplayResults(string jobID, object taskInput, object taskResults) { ITaskResultsContainer resultsContainer = null; for (int i = 0; i < TaskResultsContainers.Count; i++) { resultsContainer = Utility.FindControl(TaskResultsContainers[i].Name, Page) as ITaskResultsContainer; if (resultsContainer != null) { resultsContainer.DisplayResults(this, jobID, taskInput, taskResults); CallbackResults.CopyFrom(resultsContainer.CallbackResults); } } }7. Recall in step 4 that the GetCallbackResult method of the custom task control must send the callback response to the client browser (the method returns a string). The custom task control uses the CallbackResults property to store the content string that needs to be returned to the browser. More specifically, the content string will be processed by the Web ADF JavaScript to update Web ADF control rendering in the browser. The CallbackResults property stores a collection of CallbackResult objects. Each object is associated with a Web ADF defined string to be processed by Web ADF JavaScript. The ToString() method is called on the CallbackResults property to create the callback string. Since the callback results for a TaskResults control includes the HTML and JavaScript content that will be dynamically added to the page in the browser, the complete callback string can be quite large. The first part of an example callback string is provided below:
"TaskResults:::TaskResults1:::content:::<div id=\"TaskResults1\" title=\"Right-click for context. . ."
8. The ASP.NET 2.0 WebResource for callbacks directs the callback response string to the registered callback response function, in this case, the Web ADF defined processCallbackResult function. This function is designed to parse the callback response for Web ADF controls on the client and trigger subsequent actions to update client-side display.
9. Elements in the client browser are updated to reflect the new content from the callback response. In this example, the callback response updates the content of the TaskResults control with the results of a task executed on the server.
Enhance Visual Studio Integration
To enhance the Visual Studio design-time configuration of the custom task, extend the ESRI.ArcGIS.ADF.Web.UI.WebControls.Design.Designers.TaskDesigner class included with the Web ADF. This class provides the framework for exposing custom verbs off a task control when setting properties of the control in design-view. The process involves three steps
1) expose properties in the custom task to change via a custom verb
2) create a Windows form for a Visual Studio developer to set the property and
3) extend TaskDesigner and associate it with the custom task.
The section builds on standard .NET practices for enhancing custom Web controls in the Visual Studio IDE.
-
In the custom task class (SimpleTask_CSharp.cs), add a public property to modify the text on the button. The attributes before the property will define how TaskDesigner will work with characteristics and defaults of the property.
[Browsable(true)] [Category("Appearance")] [DefaultValue("Execute")] [PersistenceMode(PersistenceMode.Attribute)] public string ButtonText { get { object o = StateManager.GetProperty("buttonText"); return (o == null) ? "Execute" : o as string; } set { StateManager.SetProperty("buttonText", value); }
} -
Create a new Windows form in the same custom task library project. The name of the custom form class is ButtonTextEditorForm. It should be included with the project so when you distribute the custom task assembly, a Visual Studio developer will be able to use the form to set properties on the task, in design-time. The form will have the following appearance:

Here is the source code for the form (ButtonTextEditorForm.cs). One important note, the form contains a TextBox named txtButtonText:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace SimpleTask_CSharp { public partial class ButtonTextEditorForm : Form { public ButtonTextEditorForm(string value) { InitializeComponent(); Value = value; } public string Value { get { return txtButtonText.Text; } set { if (value != null) txtButtonText.Text = value; } } private void cmdOK_Click(object sender, EventArgs e) { DialogResult = DialogResult.OK; Hide(); } private void cmdCancel_Click(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; Hide(); } } } -
The form will be initialized by a custom class that extends the System.Drawing.Design.UITypeEditor class. This class provides a base class to implement a custom type editor for a Web control in the Visual Studio IDE at design-time. The custom class, named ButtonTextEditor, will initialize the Windows form to enter a value - in this case, the value of the text on the button in the custom task. The value returned will be made available to the custom task.
using System; using System.ComponentModel; using System.ComponentModel.Design; using System.Diagnostics; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Design; using System.Windows.Forms; using System.Windows.Forms.ComponentModel; using System.Windows.Forms.Design; using System.Collections; namespace SimpleTask_CSharp { public class ButtonTextEditor : UITypeEditor { public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) { if ((context != null) && (provider != null)) { System.Web.UI.Control ctrl = context.Instance as System.Web.UI.Control; if (ctrl == null) return value; ButtonTextEditorForm form = new ButtonTextEditorForm(value as string); if (form.ShowDialog() == DialogResult.OK) value = form.Value; } return value; } public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) { if (context != null) { return UITypeEditorEditStyle.Modal; } return base.GetEditStyle(context); } } } -
Create the class that links input in the Windows form to the custom task in Visual Studio at design-time. Create a class, named SimpleTaskDesigner_CSharp, that extends the Web ADF TaskDesigner class. In the constructor, add a new DesignerVerb to the custom task control to initiate the ButtonTextEditorForm Windows form. When initiated, get a description of the ButtonText property in the custom task, create a new ButtonTextEditor instance which opens the Windows form, capture the input value and update the ButtonText property.
using System; using System.Collections.Generic; using System.Text; using System.Drawing.Design; using System.ComponentModel; using System.ComponentModel.Design; using ESRI.ArcGIS.ADF.Web.UI.WebControls.Design.Designers; namespace SimpleTask_CSharp { public class SimpleTaskDesigner_CSharp : TaskDesigner { public SimpleTaskDesigner_CSharp() : base() { verbs.Add(new DesignerVerb("Edit the button text", new EventHandler(OnEditButtonText))); } protected void OnEditButtonText(object sender, EventArgs args) { SimpleTask_CSharp task = this.Component as SimpleTask_CSharp; PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(task)["ButtonText"]; UITypeEditor editor = new ButtonTextEditor(); object newValue = editor.EditValue(new TaskControlDesignerTypeDescriptorContext(this, propertyDescriptor), (IServiceProvider)this, propertyDescriptor.GetValue(task)); propertyDescriptor.SetValue(task, newValue); } } } -
Now, associate this functionality with the custom task. At the top of the custom task class, add the "Designer" attribute to define that a custom TaskDesigner should be associated with the task. The attribute will associate the custom task with a new TaskDesigner (class named SimpleTaskDesigner_CSharp) created earlier. In the example code below, only one new line referencing the Designer attribute has been added to the existing content.
namespace SimpleTask_CSharp { [ToolboxData("<{0}:SimpleTask_CSharp runat=\"server\" Width=\"100px\" BorderWidth=\"1px\"> </{0}:SimpleTask_CSharp>")] [Designer(typeof(SimpleTaskDesigner_CSharp))] public class SimpleTask_CSharp: FloatingPanelTask { -
When the custom task control is added to a Web page, a new verb is now available to change the text of the button on the task. Click the "Edit the button text" verb and the ButtonTextEditorForm should display. The screenshot below illustrates the new addition.

Manager Integration
The Web ADF task framework also enables a custom task to be integrated into the Manager Web application building framework. Manager provides a Tasks panel to list the tasks available while designing a new Web application. While the out-of-the-box tasks are already included by default, you can extend this list by adding your custom task. Manager maintains a list of configured tasks in the Tasks.xml file located in the App_Data folder for the Manager Web application (e.g. C:\Inetpub\wwwroot\ArcGIS\Manager\App_Data). A custom task will need to add an item to this list to be available in Manager. It is important to note that when uninstalling or reinstalling the Web ADF, the Tasks.xml file will be overwritten. As a result, you will need to re-add the custom task item. A couple options are provided in the discussion below.
The ESRI.ArcGIS.ADF.Web.UI.WebControls.IWebConfigurator interface provides the basic framework for implementing task usage, appearance and integration within Manager. Once finished, the Tasks panel in Manager will provide the option to add and configure the custom task. If a Web configurator is available, it can be used to set properties of the task within Manager. A screenshot of the custom task integrated with Manager is provided below:

-
Create a custom class that extends CompositeControl and implements IWebConfigurator and IBuddyControlSupport. Manager is itself a Web application. Since we need to create a class that will be used to visually configure the custom task in Manager at runtime, it must also be a composite Web control. To integrate with the Task configuration framework in Manager, the IWebConfigurator and IBuddyControlSupport interfaces are implemented. Create a new class named SimpleTaskWebConfigurator_CSharp and add a reference to the System.Web.dll, System.Drawing.dll and ESRI.ArcGIS.ADF.Web.UI.WebControls.dll. Keep the namespace the same as the previous classes (SimpleTask_CSharp).
using System; using System.Collections.Generic; using System.Text; using System.Web.UI; using System.Collections.Specialized; using System.Web.UI.WebControls; using System.Drawing.Design; using System.ComponentModel; using System.Collections; using System.Web.UI.HtmlControls; using ESRI.ArcGIS.ADF.Web.UI.WebControls; namespace SimpleTask_CSharp { public class SimpleTaskWebConfigurator_CSharp : ESRI.ArcGIS.ADF.Web.UI.WebControls.CompositeControl, IWebConfigurator, IBuddyControlSupport { -
Add some member variables to store references to the controls in the Web configurator interface.
private Button okButton = null; private Button cancelButton = null; private TextBox buttonText = null; private TextBox title = null; private ColorPicker colorPicker = null; -
Override the CreateChildControls() method to create the runtime interface of the Web configurator control. This process is similar to creating the interface for the custom task. Unfortunately there is no visual designer for this process at the moment, so manually formatting the control programmatically is required. The Web configurator will contain two textboxes to change the title of the custom task floating panel and the button text; two buttons to approve or cancel the changes; and a ColorPicker to interactively select the background color of the control in the Web application at runtime.
protected override void CreateChildControls() { Controls.Clear(); title = new TextBox(); title.ID = "title"; buttonText = new TextBox(); buttonText.Text = "Find"; buttonText.ID = "buttonText"; colorPicker = new ColorPicker(); colorPicker.ID = "colorPicker"; colorPicker.Font.Name = "Verdana"; colorPicker.Font.Size = new FontUnit(new Unit(8, UnitType.Point)); colorPicker.BackColor = System.Drawing.Color.White; colorPicker.DropDownBorderColor = System.Drawing.Color.Silver; colorPicker.DropDownBorderStyle = BorderStyle.Solid; colorPicker.DropDownBorderWidth = new Unit(1, UnitType.Pixel); colorPicker.ChosenColor = System.Drawing.Color.White; colorPicker.ShowColorNames = false; colorPicker.DisplayText = "Background color of the task"; okButton = new Button(); okButton.ID = "okButton"; okButton.Text = "OK"; okButton.Width = new Unit(75, UnitType.Pixel); okButton.Click += new EventHandler(okButton_Click); cancelButton = new Button(); cancelButton.ID = "cancelButton"; cancelButton.Text = "Cancel"; cancelButton.Width = new Unit(75, UnitType.Pixel); cancelButton.Click += new EventHandler(cancelButton_Click); Controls.Add(title); Controls.Add(buttonText); Controls.Add(colorPicker); Controls.Add(okButton); Controls.Add(cancelButton); } -
The following methods are designed to enable the Web configurator to work within events in the Manager Web application. The OK and Cancel buttons in the Web configurator control will call events that hook into implementation methods for the IWebConfigurator interface, namely the "OnWebConfiguration*" event handlers. Since the control is operating with a Web application, it can override page events to initialize and update Web configurator properties.
void cancelButton_Click(object sender, EventArgs e) { OnWebConfigurationCancel(new EventArgs()); } private void okButton_Click(object sender, EventArgs e) { if (TaskInstance == null) return; TaskInstance.Title = title.Text; TaskInstance.ButtonText = buttonText.Text; TaskInstance.BackColor = colorPicker.ChosenColor; OnWebConfigurationComplete(new WebConfigurationCompleteEventArgs(TaskInstance, getDesignTimeTag())); } protected override HtmlTextWriterTag TagKey { get { return HtmlTextWriterTag.Div; } } protected override void OnLoad(EventArgs e) { base.OnLoad(e); } protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); if (!Page.IsCallback) { // load current properties of control to edit loadProperties(); } } -
If the custom task has already been configured, for example you attempt to edit an existing Web application that contains the custom task, we need to reload the current control settings. Both loadProperties and Refresh will enable the retrieval of existing custom task control properties.
private void loadProperties() { if (TaskInstance == null) return; title.Text = TaskInstance.Title; buttonText.Text = TaskInstance.ButtonText; colorPicker.ChosenColor = TaskInstance.BackColor; } public override void Refresh() { loadProperties(); base.Refresh(); } -
A set of IWebConfiguratior properties and methods need to be implemented. In this example a basic implementation of the event handlers is provided. The ControlToConfigure property is designed to return a reference to an instance of the custom task, created when the custom task was added to the Current Tasks panel in Manager.
private ControlCollection controls = null; private SimpleTask_CSharp TaskInstance = null; public ControlCollection AdditionalControls { get { return controls; } set { controls = value; } } public Control ControlToConfigure { get { return TaskInstance; } set { if (!(value is SimpleTask_CSharp)) { throw new ArgumentException(); } TaskInstance = value as SimpleTask_CSharp; } } public bool ValidateResources(out string message) { message = null; return true; } private WebConfigurationCompleteEventHandler onWebConfigurationComplete; public event WebConfigurationCompleteEventHandler WebConfigurationCompleted { add { onWebConfigurationComplete += value; } remove { onWebConfigurationComplete -= value; } } protected virtual void OnWebConfigurationComplete(WebConfigurationCompleteEventArgs args) { if (onWebConfigurationComplete != null) onWebConfigurationComplete(this, args); } private WebConfigurationCanceledEventHandler onWebConfigurationCancel; public event WebConfigurationCanceledEventHandler WebConfigurationCanceled { add { onWebConfigurationCancel += value; } remove { onWebConfigurationCancel -= value; } } protected virtual void OnWebConfigurationCancel(EventArgs args) { if (onWebConfigurationCancel != null) onWebConfigurationCancel(this, args); } -
The abstract class CompositeControl, from which our Web configurator class derives, implements the ICallbackEventHandler interface. This interface enables a control to work with callbacks. We need to override this method to update the _callbackArg string with the content that pertains to our Web configurator (e.g. changes in the ColorPicker).
public override string GetCallbackResult() { NameValueCollection keyValColl = CallbackUtility.ParseStringIntoNameValueCollection(_callbackArg); return CallbackResults.ToString(); } -
Implementing the IBuddyControlSupport interface involves implementing the GetSupportedBuddyControlTypes() method. This method returns the types of controls that another control can buddy with. In this case, the Web configurator control can buddy with the custom task control. This enables the Web configurator to get a reference to the custom task instance.
public Type[] GetSupportedBuddyControlTypes() { Type[] types = new Type[1]; types[0] = typeof(SimpleTask_CSharp); return types; } -
When the custom task is written out to the Web application created by Manager, the custom tags it adds to the page must be defined. The getDesignTimeTag() method defines the content of the output. In essence, the output is a set of strings to declaratively define the custom task control.
private string getDesignTimeTag() { string openTag = string.Format("<simpleTaskCS:SimpleTask_CSharp ID=\"{0}\" runat=\"server\" Style=\"z-index: 10000; left: 100px; position: absolute; top: 100px\" Width=\"200px\" Visible=\"False\" ButtonText=\"{1}\" Title=\"{2}\" ToolTip=\"{3}\" NavigationPath=\"{4}\" BackColor=\"{5}\">", TaskInstance.ID, TaskInstance.ButtonText, TaskInstance.Title, TaskInstance.ToolTip, TaskInstance.NavigationPath, System.Drawing.ColorTranslator.ToHtml(TaskInstance.BackColor)); StringBuilder trcTag = new StringBuilder(); trcTag.Append("<TaskResultsContainers>"); trcTag.Append("<esri:BuddyControl Name=\"TaskResults1\" />"); trcTag.Append("</TaskResultsContainers>"); string endTag = "</simpleTaskCS:SimpleTask_CSharp>"; StringBuilder tag = new StringBuilder(); tag.Append(openTag); tag.Append(trcTag.ToString()); tag.Append(endTag); return tag.ToString(); } -
The custom Web configurator class is complete. To associate the Web configurator with the task class, use the WebConfigurator attribute and specify the name of the Web configurator class (SimpleTaskWebConfigurator_CSharp). In the example code below, only one new line referencing the WebConfigurator attribute has been added to the existing content.
namespace SimpleTask_CSharp { [ToolboxData("<{0}:SimpleTask_CSharp runat=\"server\" Width=\"100px\" BorderWidth=\"1px\"> </{0}:SimpleTask_CSharp>")] [WebConfigurator(typeof(SimpleTaskWebConfigurator_CSharp))] [Designer(typeof(SimpleTaskDesigner_CSharp))] public class SimpleTask_CSharp: FloatingPanelTask { -
Now the custom task assembly needs to be signed so we can add it to the GAC. Signing the assembly will make it easier to distribute and register with Manager. To sign the assembly, you may need to generate a signed key. Use the sn.exe utility included with the .NET SDK. Open a Visual Studio 2005 Command Prompt and enter:
sn -k MyKeyPair.snk
-
In Visual Studio, right-click on the custom task class library project in Solution Explorer window and select Properties in the context menu. Click the Signing tab and check the "Sign the assembly" check box. Enter the correct path to the key.
-
Rebuild the custom task assembly.
Deploying the custom task for Manager
The following steps can be used to deploy a custom task for use within Manager. The steps are the same regardless of whether you created the custom task and have the source code or were provided the custom task assembly from another party. -
Add the custom task assembly to the GAC. Navigate to the location of the SimpleTask_CSharp.dll. Use the gacutil.exe utility included with the .NET SDK. Open a Visual Studio 2005 Command Prompt and enter:
gacutil -i SimpleTask_CSharp.dll
-
If you change the custom task, you will need to update the GAC using the -if option with gacutil.exe. Another option is to automate reinstalling the custom task assembly to the GAC by modifying the pre and post build events in Visual Studio. Use the following steps to configure this option:
1) In Visual Studio, open the property page for the custom task project.
2) Add the following in the pre-build event command line textbox. The first line initializes the command line environment for the .NET Framework tools. The second line uninstalls the existing custom task assembly from the GAC:call "%VS80COMNTOOLS%vsvars32.bat" gacutil -u $(TargetName)
3) Add the following in the post-build event command line textbox. Again, the first line initializes the command line environment for the .NET Framework tools. The second line installs the existing custom task assembly into the GAC. The last line restarts IIS and is optional. It assumes that task development and runtime testing occur on the same machine. IIS must be restarted because ASP.NET Web applications may work with a cached copy of an assembly in the GAC (e.g. custom task control), otherwise the most recent task modifications may not be accessible during runtime testing:
call "%VS80COMNTOOLS%vsvars32.bat" gacutil -i $(TargetPath)
iisreset
-
Add the custom task information to the Tasks.xml used by Manager. A series of Task elements, one for each task control available in Manager, is listed. The Task attributes have the following definitions:
Task Attribute Description Name Task class name. DisplayName The name of the custom task displayed in the Manager dialog to add and configure a task. Type Contains five comma-delimited parameters. Listed in order they are:
1) Fully qualified task class name
2) Name of the assembly that contains the task classes and resources
3) Version number of the assembly
4) Culture of the assembly
5) PublicKeyToken of the assembly. Defined by the key used to sign the assemblyTagPrefix The tag prefix name assigned to the task control. Defined by the TagPrefix attribute of the assembly.
In the App_Data folder for the Manager Web application (e.g. C:\Inetpub\wwwroot\ArcGIS\Manager\App_Data) open the Tasks.xml file in a text editor. Add the following line within the <Tasks> tags. Be sure to confirm that the assembly version is correct.
<Task Name="SimpleTask_CSharp" DisplayName="Simple Task CSharp" Type="SimpleTask_CSharp.SimpleTask_CSharp,
SimpleTask_CSharp, Version=1.3.0.0, Culture=neutral, PublicKeyToken=a284737434b9d17c" TagPrefix="simpleTaskCS" />
To confirm that the Version, Culture, and PublicKeyToken attributes are correct, open a Visual Studio 2005 Command Prompt and enter:
gacutil -l SimpleTask_CSharp
Upon uninstall or reinstall of the Web ADF, custom entries in the Tasks.xml file will be overwritten. As a result, you are responsible for retaining or re-adding the entry. A number of options are available including:
1) Manual reentry
Manually reentering the custom task items will work, but it is a labor intensive, possibly error prone option. This is probably only an option for custom task developers.
2) Retain Tasks.xml with custom task entries
Store the custom Tasks.xml in another location (other than the install location), reinstall, and copy it to the appropriate Manager location. Again, this option is error prone since it relies on manually copying a file from one location to another. In addition, it will only work if the Web ADF version remains the same. Again, this is only a reliable option for custom task developers.
3) Create a custom task deployment setup
Create setup application to configure your custom task. This is probably the best option for distribution since an end user will only need to run a custom task setup program included with the custom task. The installation and setup of the custom task will manage the addition of the custom task item in the Tasks.xml. This option will require some additional work for the custom task developer (e.g. creation of a setup application), but will provide a more familiar end user experience.
-
Restart IIS for Manager to recognize the changes. Open a Visual Studio 2005 Command Prompt and enter:
iisreset
- Manager should provide you with the ability to configure the custom task while designing a new Web application. In the Tasks panel, select the "Simple Task CSharp" task and add it to the current list of tasks to be included in the Web application.
- Highlight the custom task in the Current Tasks window and click the "Configure" button. Set the name of the custom task window, the button text, and the background color of the control.
-
Upon completion, initialize the custom task in the Web application by expanding
the Tasks panel and clicking on the custom task in the menu. Here is an
screenshot to illustrate what you may see:

-