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