Walkthrough: Creating a custom server task


Summary This walkthrough topic provides a simple and instructive guide to creating a custom server task. At design time, the custom task can modify properties exposed via a custom verb on the control in Web page design view. At run time, the custom task contains a button and a text box in a floating panel. When you type text in the text box and click the button, the text box content is added to a TaskResults control along with the current time on the Web server. The custom task can also be used, and configured, from within ArcGIS Manager (Manager).

Click here to get the sample associated with this walkthrough.

In this topic


Create the custom server control task

Perform the following steps to create a custom server control task:
  1. Open Visual Studio 2008 and create a project of type class library.
  2. Add a reference to System.Web, System.Web.Extensions, System.Drawing, ESRI.ArcGIS.ADF.Web.UI.WebControls.
  3. In <Classname>.cs add the using statements defined in the following code example.
  4. Start with the FloatingPanelTask abstract class. This class provides a floating panel container at run time, in which you can add your task controls and client-side logic. The out-of-the-box controls also extend the FloatingPanelTask. In the following code, the SimpleServerTask_CSharp namespace contains the SimpleServerTask_CSharp class that extends FloatingPanelTask.
  5. Provide information on how the Web 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 following code, "{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).
Steps 3 through 5 are reflected in the following code:
[C#]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using ESRI.ArcGIS.ADF.Web.UI.WebControls;
namespace SimpleServerTask_CSharp
{
    [System.Web.UI.ToolboxData(
        "<{0}:SimpleServerTask_CSharp runat=\"server\" Width=\"100px\" BorderWidth=\"1px\"><TaskResultsContainers><esri:BuddyControl Name=\"TaskResults1\" /></TaskResultsContainers> </{0}:SimpleServerTask_CSharp>")]

    public class SimpleServerTask_CSharp: FloatingPanelTask
    {
  1. Add member variables to reference the controls rendered at run time as shown in the following code:
[C#]
private System.Web.UI.HtmlControls.HtmlInputText m_textBox = null;
private System.Web.UI.HtmlControls.HtmlInputButton m_executeButton = null;
  1. Override the CreateChildControls() method. This method constructs the visual interface of the task at run time. As a result, the output must work within a browser client (for example, contain Hypertext Markup Language [HTML] or JavaScript). This process must be done programmatically. Use .NET HTML control classes or render the contents of an ASP.NET control to create the interface. In the following sample code, the task dialog contains a text box and a button. The Controls property inherited from the System.Web.UI.WebControls.CompositeControl class is used to add content to the task dialog.
Note: It is not possible to add controls (drag-drop from toolbox) to a server task control at design time.
[C#]
protected override void CreateChildControls()
{
    Controls.Clear();
    base.CreateChildControls();
    m_textBox = new System.Web.UI.HtmlControls.HtmlInputText();
    m_textBox.ID = "txtTaskText";
    Controls.Add(m_textBox);
    m_executeButton = new System.Web.UI.HtmlControls.HtmlInputButton();
    m_executeButton.ID = "executetask";
    m_executeButton.Value = "Execute Task";
    string jsGetTaskCallbackArgument = string.Format(
        "'{0}=' + document.getElementById('{1}').value", m_taskTextKey,
        m_textBox.ClientID);
    string jsOnClick = string.Format("executeTask({0},\"{1}\");",
        jsGetTaskCallbackArgument, CallbackFunctionString);
    m_executeButton.Attributes.Add("onclick", jsOnClick);
    Controls.Add(m_executeButton);
}
  1. Override the GetCallbackResult() method from FloatingPanelTask to set the Input property to the value being submitted by the task at run time as shown in the following code. The Input property is implemented in FloatingPanelTask. It is of type object, but is often 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 run time.
[C#]
public override string GetCallbackResult()
{
    // Set the task's Input member to the value of the task's textbox 
    System.Collections.Specialized.NameValueCollection keyValColl =
        ESRI.ArcGIS.ADF.Web.UI.WebControls.CallbackUtility.ParseStringIntoNameValueCollection(CallbackEventArgument);
    Input = keyValColl[m_taskTextKey]; //value in TextBox
    return base.GetCallbackResult();
}
  1. Add the execution logic for the custom task as shown in the following code. 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 processes the Results object. The type should be ESRI.ArcGIS.ADF.Web.UI.WebControls.TaskResultNode, ESRI.ArcGIS.ADF.Web.UI.WebControls.SimpleResultTask, or System.Data.DataSet. The TaskResults control renders the contents in a TreeView structure. In the following sample code, a custom SimpleTaskResult is 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.
[C#]
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;
}
  1. Override the Refresh() method to run the task again and refresh the task in the TaskResult control at run time as shown in the following code. Both options are available as a context menu from the task result nodes.
[C#]
public override void Refresh()
{
    string tmp = Input as string;
    if (!string.IsNullOrEmpty(tmp))
        textBox.Text = tmp;
    base.Refresh();
}
  1. If a dependency exists, include a mandatory override to maintain a list of dependent resource items. In the following sample code, no dependencies are defined:
[C#]
public override List < GISResourceItemDependency > GetGISResourceItemDependencies()
{
    List < GISResourceItemDependency > list = new List < GISResourceItemDependency >
        ();
    return list;
}

}

}
  1. In preparation for deployment, add a custom tag prefix to uniquely identify this task from other items in a Web page (such as Web controls, tasks, and so on). 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 and 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 tag prefix is used to register the assembly in the page. For this topic, use the following definition:
[C#]
[assembly: System.Web.UI.TagPrefix("SimpleServerTask_CSharp", "simpleServerTaskCS")]
At this point, the custom task can be used in Visual Studio at design time.
  1. Build the custom task class library and add the assembly to the Visual Studio toolbox as described in the following steps:
    1. In a Web project, with a Web page active, right-click the toolbox and select Add Tab.
    2. Type a name for the new tab—for example, Custom Task Controls.
    3. Right-click the new tab and select Choose Items. The Choose Items dialog box opens.
    4. Click the Browse button, navigate to the location of the SimpleTask_CSharp.dll, and click Open. The new control is added to and selected in the Choose Items dialog box.
    5. Select the SimpleTask_CSharp check and click OK to exit the dialog box. The new tab contains the custom task control.
    6. Drag the task control and a Web Application Developer Framework (ADF) TaskResults control onto a Web page. It will look as seen in picture below:
    1. Using the verbs from the custom task control, buddy the TaskResults control to the custom task (as shown below). The custom task will also function with an ASP.NET navigation control, the same way as the out-of-the-box tasks do.
    1. At run time, the same Web page is rendered as shown in the following screen shot:
In the screenshot above, the "Hello World!", "Hello California!", and "Hello Redlands!" text has been entered into the text box and the Execute button has been clicked.

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 broad-level steps are as follows:
  • Expose properties in the custom task that needs to be changed via a custom verb.
  • Create a Windows form for a Visual Studio developer to set\change the property at design time.
  • Extend TaskDesigner and associate it with the custom task.
Until now we have covered steps to create a simple custom server task control. This section will further enhance the simple server task by adding design-time configuration functionality.
This section is built on standard .NET practices for enhancing custom Web controls in the Visual Studio IDE.
  1. In the SimpleTask_CSharp.cs custom task class, add a public property to modify the text on the button as shown in the following code. The attributes before the property define how TaskDesigner works with characteristics and defaults of the property.
[C#]
#region Instance Properties
[System.ComponentModel.Browsable(true)][System.ComponentModel.Category("Appearance")
    ][System.ComponentModel.DefaultValue("Execute")][System.Web.UI.PersistenceMode
    (System.Web.UI.PersistenceMode.Attribute)]
public string ButtonText
{
    get
    {
        object buttonTextObject = StateManager.GetProperty("buttonText");
        return (buttonTextObject == null) ? "Execute" : buttonTextObject as string;
    }
    set
    {
        StateManager.SetProperty("buttonText", value);
    }
}

#endregion
  1. Create a new Windows form in the same custom task library project. The name of the custom form class is ButtonTextEditorForm. Include it with the project so when you distribute the custom task assembly, a Visual Studio developer can use the form to set properties on the task at design-time. The following screen shot shows the form:
 
The source code for the form (ButtonTextEditorForm.cs) is as follows:
The form contains a text box named txtButtonText.
[C#]
namespace SimpleServerTask_CSharp
{
    public partial class ButtonTextEditorForm: System.Windows.Forms.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, System.EventArgs e)
        {
            DialogResult = System.Windows.Forms.DialogResult.OK;
            Hide();
        }
        private void cmdCancel_Click(object sender, System.EventArgs e)
        {
            DialogResult = System.Windows.Forms.DialogResult.Cancel;
            Hide();
        }
    }
}
      1. The form is 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 integrated development environment (IDE) at design time. The custom class, named ButtonTextEditor, initializes the Windows form to enter a value—in this case, the value of the text on the button in the custom task. The returned value is available to the custom task. See the following code:
[C#]
namespace SimpleServerTask_CSharp
{

    public class ButtonTextEditor: System.Drawing.Design.UITypeEditor
    {
        public override object EditValue
            (System.ComponentModel.ITypeDescriptorContext typeDescriptorContext,
            System.IServiceProvider serviceProvider, object value)
        {
            if ((typeDescriptorContext != null) && (serviceProvider != null))
            {
                if (typeDescriptorContext.Instance == null)
                    return value;
                ButtonTextEditorForm buttonTextEditorForm = new ButtonTextEditorForm
                    (value as string);

                if (buttonTextEditorForm.ShowDialog() ==
                    System.Windows.Forms.DialogResult.OK)
                    value = buttonTextEditorForm.Value;
            }
            return value;
        }
        public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle
            (System.ComponentModel.ITypeDescriptorContext typeDescriptorContext)
        {
            if (typeDescriptorContext != null)
            {
                return System.Drawing.Design.UITypeEditorEditStyle.Modal;
            }
            return base.GetEditStyle(typeDescriptorContext);
        }
    }
}
  1. Create the class that links input from the Windows form to the custom task in Visual Studio at design time. Create a class, called SimpleServerTaskDesigner_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 that opens the Windows form, capture the input value, and update the ButtonText property as shown in the following code:
[C#]
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 SimpleServerTask_CSharp
{
    public class SimpleServerTaskDesigner_CSharp: TaskDesigner
    {
        public SimpleTaskDesigner_CSharp(): base()
        {
            verbs.Add(new DesignerVerb("Edit the button text", new EventHandler
                (OnEditButtonText)));
        }
        protected void OnEditButtonText(object sender, EventArgs args)
        {

            SimpleServerTask_CSharp simpleTask = this.Component as
                SimpleServerTask_CSharp;
            PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties
                (simpleTask)["ButtonText"];
            UITypeEditor uiTypeEditor = new ButtonTextEditor();
            object newValue = uiTypeEditor.EditValue(new
                TaskControlDesignerTypeDescriptorContext(this, propertyDescriptor), 
                (IServiceProvider)this, propertyDescriptor.GetValue(simpleTask));
            propertyDescriptor.SetValue(simpleTask, newValue);
        }
    }
}
  1. 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 is associated with the task. The attribute associates the custom task with the TaskDesigner (the SimpleTaskDesigner_CSharp class) created earlier. In the following example code, only one new line referencing the Designer attribute has been added to the existing content:
[C#]
[System.Web.UI.ToolboxData(
    "<{0}:SimpleServerTask_CSharp runat=\"server\" Width=\"100px\" BorderWidth=\"1px\"><TaskResultsContainers><esri:BuddyControl Name=\"TaskResults1\" /></TaskResultsContainers> </{0}:SimpleServerTask_CSharp>")][Designer(typeof(SimpleServerTaskDesigner_CSharp))]

public class SimpleServerTask_CSharp: FloatingPanelTask
{
  1. When the custom task control is added to a Web page, a new verb becomes available to change the text of the button on the task. Click the Edit the button text verb. The Edit Button Text dialog box opens s shown in the following screenshot:

  2. When the application is run and web page loads, the button on the custom task will show the changed text as seen below.


See Also:

Introduction to the Task Framework
Task runtime workflow
Server task Manager integration
Creating a custom UserControlTask