Developing a simple task list using the Workflow API

A common task when developing a custom site using ADAM is to integrate a workflow task list in (one or more of) your pages. In this article I'm going to implement a basic web user control that does just that -- display the list of workflow tasks assigned to the currently logged on user.

I'm assuming a basic knowledge of the ADAM Workflow API and a working development installation of the ADAM Workflow Server (AgilePoint).

Getting started

First things first: we're going to set up a new Visual Studio 2008 ASP.NET project and rig it in order to connect to ADAM and ADAM Workflow. This means that we're going to add the necessary configuration sections in the Web.config file.

Launch Visual Studio, create a new ASP.NET Web Application Project and add the required ADAM assembly references:

  • Adam.Core
  • Adam.Tools
  • Adam.Web.Core
  • Adam.Workflow.Core
  • Adam.Workflow.Web.Core

Add the configuration section definitions to the Web.config file in order to configure ADAM correctly:

XML
1
2
3
4
5
<configSections>
    <section name="Adam.Core" type="Adam.Core.ConfigurationHandler, Adam.Core"/>
    <section name="Adam.Tools" type="Adam.Tools.ConfigurationHandler, Adam.Tools"/>
    <section name="Adam.Web.Core" type="Adam.Web.Core.Configuration.AdamWebConfiguration, Adam.Web.Core" allowDefinition="Everywhere" />
</configSections>

Specify the registration name and application name to use for ADAM, and configure any log listeners you want to use for logging:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Adam.Core>
    <defaultRegistration name="AdamTest"/>
    <applicationName name="CustomTaskList"/>
</Adam.Core>

<Adam.Web.Core>
    <authentication enabled="true" loginUrl="~/LogOn.aspx" cookieName=".ADAMAUTH" requireSSL="false" persistentTimeout="30"/>
</Adam.Web.Core>

<Adam.Tools>
    <logListeners>
        <add type="Adam.Tools.LogHandler.EventLogListener, Adam.Tools" severity="Verbose"/>
    </logListeners>
</Adam.Tools>

We also need to add a simple login page and some configuration settings in order to set up authentication for our custom site. However, as this is beyond the scope of this article, I'll leave this as an exercise for the reader (or you could examine the source code accompanying this article).

Setting up the user control

Add a new Web User Control to the project called TaskList.ascx. Open up the markup view and enter the code to make it a true workflow task list:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<table border="0" cellpadding="0" cellspacing="0" class="taskList">
    <tr>
        <th>Task Name</th>
        <th>Definition Name</th>
    </tr>

    <asp:Repeater ID="rptTaskList" runat="server">
        <ItemTemplate>
            <tr>
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DisplayName")) %></td>
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DefinitionName")) %></td>
            </tr>
        </ItemTemplate>
        <AlternatingItemTemplate>
            <tr class="alternate">
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DisplayName")) %></td>
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DefinitionName")) %></td>
            </tr>
        </AlternatingItemTemplate>
    </asp:Repeater>

    <tr id="trEmptyCollection" runat="server">
        <td class="empty" colspan="2">No items to display</td>
    </tr>
</table>

Next, we're going to add the codebehind of the user control. Upon page load, the list of tasks assigned to the currently logged on user is automatically bound to the Repeater control (note: we could also use a GridView control for this). The code below uses nothing but the ADAM Workflow API:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private void BindTaskList()
{
  WorkflowEngine oWorkflowEngine = WorkflowEngineHelper.GetWorkflowEngine();
  WorkflowTaskCollection oTaskCollection = oWorkflowEngine.CreateTaskCollection();
  bool isLastPage;

  TaskLoadOptions options = new TaskLoadOptions(oWorkflowEngine.App.UserId, null, TaskStatus.Assigned);

  oTaskCollection.Load(options, 1, -1, out isLastPage, 2000);

  rptTaskList.DataSource = oTaskCollection;
  rptTaskList.DataBind();

  // Show "Collection is empty" message when appropriate.
  trEmptyCollection.Visible = (oTaskCollection.Count == 0);
}

At this point, we have a reusable task list control. However, it is not possible to click on a task to complete it.

Adding the task page for completing tasks

In the UserControls folder of the project, add a new user control called Task.ascx. This control will be responsible for displaying the selected task's user interface and allowing the user to complete the task.

 The task's user interface will be loaded into a placeholder control:

XML
1
<asp:PlaceHolder ID="taskUIControlContainer" runat="server" />

In the codebehind of the user control, change the base class of the user control from UserControl to WorkflowTaskUserControl, which will automatically detect the query string containing the task id and load the workflow task in the Task property of the control. Next wire up the events and provide some methods to make the user control fully functional:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
protected override void OnInit(EventArgs e)
{
  base.OnInit(e);

  // Initialize the UI control for the task.
  if (!this.InitUIControl())
  {
    // Display a warning, because no task UI could be loaded.
    AdamContext.Current.Messages.InformationMessages.Add(this.WorkflowEngine.GetErrorMessage(WorkflowErrorCode.TaskUIControlTypeNotFound));

    // Return to the main task list page.
    if (Request.UrlReferrer != null) Response.Redirect(Request.UrlReferrer.ToString());
    else Response.Redirect("/");
  }
}

public bool CompleteTask()
{
  TaskUIControl control = this.taskUIControlContainer.FindControl("__UIControlType") as TaskUIControl;

  if (control != null)
    return control.CompleteTask();

  return false;
}

private bool InitUIControl()
{
  TaskUIControl control = TaskUIControl.CreateTaskUIControl(this.Task, false);

  if (control != null)
  {
    // Render the work item UI control.
    control.ID = "__UIControlType";
    control.Finish += new EventHandler(control_Finish);

    taskUIControlContainer.Controls.Add(control);

    return true;
  }
  else
  {
    return false;
  }
}

protected void control_Finish(object sender, EventArgs e)
{
  // Return control to the main page.
  Response.Redirect("/");
}

Next, all we have to do is create the Task.aspx page, add complete and cancel buttons to the page and wire up the events of these buttons to a Task.ascx user control on the page:

 

Putting it all together

Next, we're going to modify the TaskList.ascx control to add a link to the Task.aspx page, on which the user can complete the task. We will do this by adding a single link column to the table:

XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<table border="0" cellpadding="0" cellspacing="0" class="taskList">
    <tr>
        <th>Task Name</th>
        <th>Definition Name</th>
        <th>Action</th>
    </tr>

    <asp:Repeater ID="rptTaskList" runat="server">
        <ItemTemplate>
            <tr>
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DisplayName")) %></td>
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DefinitionName")) %></td>
                <td><asp:HyperLink runat="server" Text="View" NavigateUrl='<%# string.Format("~/Task.aspx?g={0}", (string)DataBinder.Eval(Container.DataItem, "Id")) %>' /></td>
            </tr>
        </ItemTemplate>
        <AlternatingItemTemplate>
            <tr class="alternate">
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DisplayName")) %></td>
                <td><%# HttpUtility.HtmlEncode((string)DataBinder.Eval(Container.DataItem, "DefinitionName")) %></td>
                <td><asp:HyperLink runat="server" Text="View" NavigateUrl='<%# string.Format("~/Task.aspx?g={0}", (string)DataBinder.Eval(Container.DataItem, "Id")) %>' /></td>
            </tr>
        </AlternatingItemTemplate>
    </asp:Repeater>

    <tr id="trEmptyCollection" runat="server">
        <td class="empty" colspan="3">No items to display</td>
    </tr>
</table>

Note that we are passing the id of the task to complete through the query string of the URL. As discussed in the previous section, this query string will be handled by the Task.ascx user control because it derives from the WorkflowTaskUserControl class.

At this point, you should have a complete, reusable task list control capable of completing ADAM Workflow tasks, by using nothing but the ADAM Workflow API. Feel free to explore the source code of this example provided below. Happy coding.

Sample Code

The article contains sample code project(s).
You must be logged in to view or download sample code.
Sign in now

Comments

Leave a comment
You must be logged in to post comments.
Sign in now
 
 
CATEGORIES
AnnouncementsDocMaker StudioEngineSharePoint ConnectorWeb DevelopmentWebinarsWorkflow Studio
rss feed