
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.

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")]

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.
"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.
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.
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.
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.


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 assembly |
| TagPrefix | 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\Applications\App_Data) open the Tasks.xml
file in a text editor. Add the following line within the <Tasks>
tags:
<Task Name="SimpleTask_CSharp" DisplayName="Simple Task CSharp" Type="SimpleTask_CSharp.SimpleTask_CSharp,
SimpleTask_CSharp, Version=1.1.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
Restart IIS for Manager to recognize the changes. Open a Visual Studio 2005 Command Prompt and enter:
iisreset
