You are on page 1of 87

Step by Step Tutorial.

Creating Workflows for Windows Sharepoint Services and MOSS2007


http://sergeluca.spaces.live.com/blog/cns!E8A06D5F2F585013!859.entry

Step 1: Creating and testing the project


Download the code

1.Introduction.
One of the greatest innovations in Windows Sharepoint Service v3 and MOSS 2007 is the integration
with Windows Workflow Foundation, a core component of the .Net Framework 3.0.

To have a general overview of Windows Workflow Foundation, take a look at these interesting articles
from David Chappell.

Introducing Microsoft Windows Workflow Foundation: An Early Look.

Understanding Workflow in Windows Sharepoint Services and the 2007 Microsoft Office System.

In this long series of articles, we will provide a step by step approach to building workflows in Sharepoint
with both Microsoft Visual Studio and Microsoft Sharepoint designer.

2. Submitting an expense Report: the scenario

The workflow we are going to create step by step is an application that allows users to submit their
expense reports; as soon as their expense report will be submitted, a workflow will be activated; this
workflow will:

• Generate a unique identifier (a guid) for this expense report


• Check if the report must be submitted to approval : if the amount is less than 1000, the expense
report will be automatically approved and the process is completed (status will be “autoapproved”)
• Find the user’s manager
• Create tasks for the manager to approve or reject the expense report
• Update the expense report status (“approved or “rejected”).

Before starting, make sure you have a team site with the following list:

A list named “Expense Reports” with following columns:


A list named “Managers” with the following columns:

(very important , make sure the Manager and Manager Of columns are of type Person or Group with
the Show Field option as account )

Fill up the Managers list with the following values:


3.2 Creating the workflow

Before starting Visual Studio 2005, make sure the followings components have been installed:

• Visual Studio 2005 Extensions for Windows Workflow Foundation.


• Visual Studio 2005 Extensions for Windows Sharepoint services v.3
It’s time to create our first workflow: start Visual Studio 2005 and create a new project :

select Sharepoint project type and the Sequential Workflow Library template. Name the project
U2U.ExpenseReport:

In the solution Explorer, delete Workflow1.cs, and add a new workflow file:
Select sequential Workflow and name the file ExpenseReportWorkflow.cs:

Double click on ExpenseReportWorkflow.cs to visualize the workflow in the Workflow Designer


Take a look at the referenced assemblies in Solution Explorer:

3.2 Setting up the Sharepoint activities


The Sharepoint team provides a set of activities and most of them are compiled in the
microsoft.sharepoint.WorkflowActions.dll that can be found in the C:\Program Files\Common
Files\Microsoft Shared\web server extensions\12\ISAPI folder.

Most activities are either activities derived from HandleExternalEventActivity which is a basic Workflow
Foundation activity waiting for an Event or CallExternalMethod which is an activity calling a class
implementing an interface (this class is called “local service”).

The communication between the WorkflowRuntime host (which in our case is Sharepoint) and the
workflow follows the usual pattern defined by the Windows Workflow Foundation team:

• Sharepoint will communicate with the workflow by sending events


• The workflow will invoke Sharepoint by calling a method on a dedicated interface that will
redirect the call to the Sharepoint api.
The interfaces we will have to deal with are defined in the Microsoft.Sharepoint.Workflow namespace of
the Microsoft.Sharepoint assembly and are decorated with ExtenalDataExchangeAttribue (see the
Windows Services sdk):

• ISharepointService
• IListItemServices
• ITaskServices
• IWorkflowModificationservice

For Windows Workflow Foundation aficionados, it’s worth knowing that the WorkflowRuntime class is
completely encapsulated and hidden by the Sharepoint framework; therefore we cannot add our own local
/runtime services and invoke them from our custom activities as we usually do we host the workflow
runtime ourselves.

To display the Sharepoint activities in Visual Studio Toolbox, let’s create a new tab: “Sharepoint
activities” and drag & drop the Microsoft.sharepoint.WorkflowActions.dll on it (or use the toolbox
browse menu, but the first option is faster).

Many others activities are also available in the WorkflowActions assembly but their ToolboxItem attribute
is set to false so that they won’t show up in the toolbox but they can be used with Microsoft Sharepoint
Designer.

3.3 Finishing, deploying and testing our “hello world” workflow


The first activity in a Sharepoint workflow must be the
Microsoft.SharePoint.WorkflowActions.OnWorkflowActivated activity.

Drag and drop an OnWorkflowActivated activity into the designer:

Set its CorrelationToken property as follows:

CorrelationToken are extensively used is in Workflow-Sharepoint programming and are an interesting


way to logically group activities together; more details about this later…

To make sure our workflow will really work, let’s drag and drop a LogToListHistory activity after the
onWorkflowActivated1 activity.
Set its HistoryOutcome property to “hello from Serge”:

It’s time to deploy our hello world workflow: workflows in Sharepoint must be deployed as features, so
we need to provide more details about our current feature in the feature.xml file .

Make sure the features code snippet are activated (the snippets are installed on C:\Program
Files\Microsoft Visual Studio 8\Xml\1033\Snippets\Windows SharePoint Services Workflow, press CTL
K + B to add this folder to the snippets; choose XML as the language).

Select the feature.xml file and use the feature snippet to insert the feature code:
Replace the Guid with a new Guid (use the create Guid tool in Visual Studio Tools Menu-Create Guid
menu, select registry format, click on “New Guid” Button, click on Copy and paste it in the feature.xml
file).

Replace the Title and Description attributes with some meaningful information:

Sign the assembly: project properties-Signing- Check sign the assembly, select new, create a new file and
rebuild the project.

The manifest file of the feature is workflow.xml: replace the existing content with the tags provided by
the workflow snippet; set the Id with a generating a new Guid, provide a Name, a Description, and
specify the CodeBesideClass.
Use Reflector to retrieve your assembly strong name and insert it into the CodeBesideAssembly attribute :

Remove any other tags.

The install.bat file will deploy our workflow by registering the assembly to the Gac by will installing and
activating our feature to the site collection.

We need to specify the site collection in the install.bat file: replace “http://localhost “ string with your site
collection url :
In my version of the install.bat, 3 occurrences have been replaced.

Next we need to specify our assembly name (that will also be our feature name here): replace the string
“MyFeature” with the string “U2U.ExpenseReport”.

In my version of the install.bat file, 13 occurrences have been replaced. Save the file.

Start the install.bat and check any potential error message.

You can verify your workflow has been activated: Site settings Menu-Galleries-Workflows
Let’s make an association between our ExpenseReports list and the workflow :

Select the ExpenseReports list, go the List Settings: in Permission and Management, select Workflow
settings:

Select Add a workflow and select the Expense report Workflow ; type a unique name for the association
like ApproveReject :

Click on Ok.

Go to the ExpenseReports list add a new Expense Report:


Start the workflow menu associated with the item:
Click on the ApproveReject button.

Normally a new column the association has been added to the list; if everything is ok, the workflow status
for the item is completed:

If you click on the Completed hyperlink, you’ll be redirected to the workflow history and you’ll see our
message:

Congratulations ! In the next article we will implement the Submit expense Report scenario.
Step 2: Extending the workflow: checking the amount and setting the status
Download the code

The scenario

Click here to get to the previous article.

What we want to implement here is retrieving the list item : the expense report informations; if the
expense report is greater than 1000 then the manager approval will be required, otherwise the expense
report will be accepted.

Retrieving the expense report details

This is straightforward in Sharepoint : we just need to bind the OnWorkflowActivated activity with a
workflow member:

Select the OnWorkflowActivated1 activity,

Select its workflowProperties in the property page and Click on the ellipsis button,

Select the tab Bind to a new member

Select Create Field, and set Workflowproperties as the new field name:
A new Data member of type Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties will be
generated and linked to the activity information. Among others this class will provide access to the list
item via its Item property.

Add 4 new public data members to the ExpenseReportWorkflow class :

public sealed partial class ExpenseReportWorkflow: SequentialWorkflowActivity


{
public float Amount;
public string Description;
public string SubmittedBy;
public string Status;
Double click on the Invoked event of the onWorkflowActivated1 activity :
A new event handler, onWorkflowActivated1_Invoked, will be generated: we will retrieve the expense
report values at this level.

if (WorkflowProperties.Item["Amount"] != null)
float.TryParse(WorkflowProperties.Item["Amount"].ToString(), out
Amount);
if (WorkflowProperties.Item["Description"] != null)
Description = WorkflowProperties.Item["Description"].ToString();

if (WorkflowProperties.Item["Status"] != null)
Status = WorkflowProperties.Item["Status"].ToString();
//Submitted By contains a space we need to use its internal name
//WorkflowProperties.Originator is the submitter
WorkflowProperties.Item["Submitted_x0020_By"] =
WorkflowProperties.Originator.ToString();

Debugging the workflow

Let’s compile your changes and call the install.bat file. Call your web site to start the w3p.exe process.

Set a breakpoint in the onWorkflowActivated_Invoked event handler and attach the assembly to the
w3p.exe process (Debug menu-Attach to Process, select w3p.exe).Go to the ExpenseReports list , start
the workflow until you hit your breakpoint in the debugger and watch your data members :
Checking the values and setting the status

Drag and drop an ifElse activity and rename the activities as following:

In the ifSmallAmount activity, select the Declarative Rule Condition for the condition property:
Enter SmallAmount as the ConditionName :

Click on the ellipsis (…) button and the rule editor will show up.

Type the following condition:


You’ll notice that a .rules file has been generated this file contains expressions describing or rules /

conditions. The language for these expressions is CodeDom. We’ll get back to this

Drag and drop 2 code activities and rename them as follows:


Double click on the Authorize activity and associate it with the following code:

private void authorize_ExecuteCode(object sender, EventArgs e)


{
WorkflowProperties.Item["Status"] = "Automatically Approved";
WorkflowProperties.Item.Update();
}

Double click on the requestManagerApproval activity and associate it with the following code :

private void requestManagerApproval_ExecuteCode(object sender, EventArgs e)


{
WorkflowProperties.Item["Status"] = "Automatically Approved";
WorkflowProperties.Item.Update();
}
Start install.bat and test the workflow on your list item:

Test the workflow with amounts greater than 1000.

Step 3: Extending the workflow: finding the manager and creating a custom
activity

Download the code

The Scenario :

Click here to get to the previous article.


If the expense report requests manager approval, we need to find the current user’s manager and assign
him a task. In this step, we will focus on finding the manager. We will code this in a custom code activity
and we will refactor and encapsulate our code in a custom activity.

Make sure the Sharepoint object model namespace has been specified in ExpensereportWorkflow.cs:

using Microsoft.SharePoint;

Let’s create a general function that Retrieves a manager from a list :

private string FindManager( string managerListTitle,


string managerColumnName,
string manageeColumnName,
string managee,
SPWeb webSite)
{

SPList managerList = webSite.Lists[managerListTitle];


string manager = string.Empty;
foreach (SPListItem managerItem in managerList.Items)
{
if (managerItem[managerColumnName] == null)
break;
if (managerItem[manageeColumnName] == null)
break;
//workflowProperties.Originator is the userId
if (managerItem[manageeColumnName].ToString().Contains(managee))
{
manager = managerItem[managerColumnName].ToString();
return manager;
}
}
return manager;
}

We can invoke this function from our requetsManagerApproval event handler:

SPWorkflowActivationProperties.Originator is the user id of the workflow caller.

SPWorkflowActivationProperties.Web is the web site associated with the workflow instance.


private void requestManagerApproval_ExecuteCode(object sender, EventArgs e)
{
string manager = this.FindManager( "Managers",
"Manager",
"Manager Of",
WorkflowProperties.Originator,
WorkflowProperties.Web);
WorkflowProperties.Item["Status"] = "Must be approved by " + manager;
WorkflowProperties.Item.Update();
}

Build the project, call install.bat and test the workflow by logging in with the “Serge” account (Serge's
manager is Jim)

There are still some extra characters we need to remove in the FindManager function

if (managerItem[managerColumnName].ToString().Contains(managee))
{

manager = manager.Remove(0, manager.IndexOf('#') + 1);
return manager;
}
Build the project, call install.bat and test the workflow :
Creating a Custom activity

If we need to access the FindManager function in several workflows it make sense to encapsulate it in an
Custom activity.

Add a new Workflow project the solution : select the Workflow Activity Library template; name it
U2U.ManagerActivity:

Reference Microsoft.Sharepoint.dll in order to use the Sharepoint Object Model :


Delete the file Activity1.cs and add a new Activity component (Select Add new Item-Activity).

Name the file RetriveManager.cs:


Go the activity code and derive RetrieveManager from the Activity class :

public partial class RetrieveManager: Activity


{
public RetrieveManager()
{
InitializeComponent();
}
Declare:

using Microsoft.SharePoint

Define a public property for each data that can be know at design time :

• ManagerListTitle,
• ManagerColumnName,
• ManageeColumnName

We have to define a dependency property for each information that can only be retrieved at runtime:
• Managee
• WebSite
Make sure the snippet for dependency property is available, otherwise click on CTRL K+B , select Visual
C# and add the workflow folder :

Define a dependency property for the WebSite :

Replace MyProperty with WebSite:


Replace the type with SPWeb:

Create another 2 other Dependency properties : Managee and Manager; keep string as the type.

Override Execute Method of the Activity:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)


{

return ActivityExecutionStatus.Closed;

}
Call the Findmanager function in Execute()
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
this.Manager = this.FindManager(
this.ManagerListTitle,
this.ManagerColumnName,
this.ManageeColumnName,
this.Managee,
this.WebSite);

return ActivityExecutionStatus.Closed;
}
}

Sign and rebuild your assembly.

If you display the ExpenseReportWorkflow in the Workflow Designer, the new Activity should show up
in the toolbox :

We can drag and drop it just before the RequestManagerApproval activity:


Set the properties known at design time: (pay attention to the case sensitivity):

Select the WebSite property , click on the ellipsis button (…) and bind it to an existing member : the Web
property the WorkflowProperties field :
Select the Managee property and bind it to an existing member : the Originator property of the
workflowProperties member :
The activity will retrieve the manager and we will store this maneger into a new workflow property :

Create a new public field in the workflow : Manager

public sealed partial class ExpenseReportWorkflow: SequentialWorkflowActivity


{
public string Manager;

Bind the activity Manager property to rthe workflow Manager property:


In the workflow, update the requestManagerApprovalexecuteCode function to display the Manager:

private void requestManagerApproval_ExecuteCode(object sender, EventArgs e)


{
WorkflowProperties.Item["Status"] = "Must be approved by " + this.Manager;
WorkflowProperties.Item.Update();
}

Set the copy local property of the reference to the U2UManagerActivity assembly to true:
Sign the new project, rebuild the solution and update install.bat in order to register the new assembly into
the Gac.
Make sure your account has a manager defined in the Managers list.

Build the project and test the workflow. If you get the error message "Failed on start", this probably
means that the activity assembly has not been registered into the Gac.

Step 4: Creating the tasks

• Download the code


Scenario

If the expense reports requires the manager approval, we want the manager to receive a task in his task
list in order to approve/reject the expense report.

Let’s drag and drop a CreateTask activity before the requestManagerApproval activity:
Set its Correlation token to ApproveRejectToken and the associated ownerActivityName to
ExpenseReportWorkflow:
Bind the Taskproperties property to a new member (click on he ellipsis button…):
Bind the TaskId to a new member (field): ApproveRejectTaskId.

Double click on the ApproveReject task to generate a new event handler and store a new Guid into
ApproveRejectTaskId, set the task title, and associate the task with the manager:

private void ApproveRejectTask_MethodInvoking(object sender, EventArgs e)


{
this.ApproveRejectTaskId = Guid.NewGuid();
this.ApproveRejectTaskProperties.ApproveRejecTaskProperties.Title = "Approve or Reject this
expense report";
this.ApproveRejectTaskProperties.AssignedTo = Manager;
}

Remove the requestManagerApproval activity.

OnTaskChange activity

Add a new OnTaskChange activity and rename it WaitForApprovalRejection:

Any event OnTaskChanged coming form the task will be handled by the WaitForApprovalRejection
activity if we group them with the same correlation token (you ‘ll have a better understanding of the
correlation token in the next tutorial).

Set the correlation token to ApproveRejectToken.


This is not enough, the event doesn’t carry the taskId you still need to associate this activity with the
taskId:

Bind the the TaskId property to an existing member: ApproveRejectTaskId.

If we consider that when the user replies, the task should complete, then we need to drag and drop a
CompleteTask activity.

CompleteTask activity

Name it ApproveRejectTaskComplete.

Set the correlation token to ApproveRejectToken.

Bind the TaskId property to an existing member: ApproveRejectTaskId.

Test

Rebuild the solution, call the install.bat and test your workflow : a new task should have neen assigned to
Jim:

The workflow status will be In progress which is a good indication that the workflow is waiting for an
event:

If we click on the status, we’ll get more details:


Indeed, a task has been assigned to Jim; if you click on the task Title, you’ll be redirected to the task list:
If you click on the task you’ll get more details (including the workflow association name: AcceptReject):
If Jim change the task and set it to completed:
The workflow will complete. There are many situations where the out of the box behavior is not enough,
for instance if you want to display more detailed informations about the expense report, or more specific
replies (approved or rejected).

Then we need to create either Infopath forms (only on MOSS) or aspx form (on Wss v3 or on MOSS).

Step 5: Creating and Using Infopath Tasks Forms


Download the code

Creating and linking the Infopath form

(start with Step 4 solution)

Let’s start Infopath 2007. Select Design a Form Template-Blank:


Check the option Enable browser-compatible features only.

Design the following form with 2 buttons :

We need to create a Data Connection; select the Design Tasks panel, select Data Sources and click on
Manage Data Connections:

Add a new data connection; select Create a new connection to Submit data :
Click Next to select to the hosting environment… :
Click Next and name it Submit.

On the Design task panel click on Data Source, right click on myfields and add a new field; name it Status
an keep the text data type:

Click on the Accept button, click on the Rules button and on the Add button.

We are going to add 3 actions :

setting the status field to Accepted or Rejected,


Submitting the data

Closing the form

Click on the Add Action button, select the Set a field’s value Action, select the field status and set the
Value to Accepted :

Follow the same procedure for the Reject button , but set the Value to Rejected.

On both the Accept and Reject button, click on the control, select Rules, select rule1, click on the Modify
button: add a new action :
Add another action :

Don’t forget to follow the same procedure for the Reject button.

Set the Security Level of the form to Domain (menu Tools-Forms Option-Security and Trust) :
Publish the infopath form : File Menu-Publish: click ok to save the form and publish the form to a
newtwork location:
Click Next, save the form in your project directory with the name ApproveReject.xsn.
Click on Next and keep the text box empty (very important) :
Click Next until the Wizard closes.

Close Infopath.

With Windows Explorer, go to the form, right click on it:


Select Design and go to the File menu, select Properties:
Copy the ID to to the clipboard.

Modify the manifest file (workflow.xml) in order to declare this form as the form to use to interact with
the task:

In the MetaData element, create a Task0_FormURN child element :


Several forms can be used (see part 6 of this tutorial) therefore the code in the workflow will refer to the
form by specifying the number provided in the Task<X> element name; for instance here <X> is 0, so in
the code we will refer to the form 0.

In this part of the tutorial we are going to use only one type of task to interact with the workflow, so
specify one task content type :

In the feature.xml file, add the following attributes to the feature element for handling the infopath form:

ReceiverAssembly="Microsoft.Office.Workflow.Feature, Version=12.0.0.0, Culture=neutral,


PublicKeyToken=71e9bce111e9429c"
ReceiverClass="Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver"
Register the Infopath form in the feature.xml file:

Modify the install.bat file in order to copy the Infopath Form :

In the same file, uncomment the lines following the Note :

::Note: Uncomment these lines if you've modified your deployment xml files or IP forms

In the workflow designer, double click on the ApproveRejectTask activity : in the


ApproveRejectTask_methodInvoking, set the link between the Task and the TaskForm0 (which references
our Infopath form):
private void ApproveRejectTask_MethodInvoking(object sender, EventArgs e)
{

this.ApproveRejectTask.TaskProperties.TaskType = 0;
}
Now, let’s retrieve the information coming from the Infopath Form : is our expense report approved or
rejected ?

We can retrieve these value at the level of the workflow by using the AfterPropertries property of the
OnTaskChange activity :

The type of AfterProperties is Microsoft.Sharepoint.Workflow.SPWorkflowTaskProperties.

The data coming from the Infopath form controls will be stored in its ExtendedProperties property which
is an Hashtable.

Let’s bind AfterProperties to a new field (AfterApprovRejectProps) in the workflow class:


Double click on the WaitForApprovalRejection activity ,r etrieve the Infopath fields value from the
HashTable (ExtendedProperties) and change the ListItem status :

private void WaitForApprovalRejection_Invoked(object sender,


ExternalDataEventArgs e)
{
string acceptedRejectedStatus =
this.AfterApprovRejectProps.
ExtendedProperties["status"].ToString();
this.WorkflowProperties.Item["Status"] = acceptedRejectedStatus;
this.WorkflowProperties.Item.Update();
}

Rebuild the solution, call install.bat, test the workflow; click on the new task in the task list and the
Infopath Form should show up; submit your choice and check the item status.

Step 6: Using Several Task Forms


Download the code
Scenario:

Even if it’s not the best choice here, imagine that we split the approve/reject process in 2 tasks : an
approving task and a rejecting task: this means having 2 tasks forms and refactoring the workflow to
make it listening to 2 possible events (the manager did approve or the manager did reject).

Hands-on

Make a copy of the existing Infopath form and rename it Reject.xsn.

Rename the original form to Accept.xsn.

Design reject.xsn , change the header and remove the accept button:

Go to the File-properties menu and change the form id:


Publish the form as we did in the previous step, but here rename the Form template to Reject and the
form template file to Reject.xsn :
Remember to keep the textbox empty in the next step…

Design Approve.xsn and remove the Reject button:

Publish this form as we did for the Reject form , but the form template should be Approve and the file
Approve.xsn.

Copy both published files to your project folder.


Drag and drop a ParallelActivity after the requestManagerApproval activity and rename the activities as
follows:

Move the ApproveRejectTask to the left branch of the ParallelActivity and drag & drop another
CreateTask activity to the right branch.Rename these activities respectively approveTask and rejectTask;
set their correlation token to ApproveToken and RejectToken and their invoked method to
ApproveTask_MethodInvoking and to RejectTask_MethodInvoking:
Note: using a ParallelActivity is not strictly necessary here, we could have put the approveTask and the
rejectTask activities in a sequence.

Now we need to listen to the manager’s decision : 2 events could be sent: approved or rejected; the usual
pattern for handling this in Workflow Foundation is to use a ListenActivity:

Drag and drop a ListenActivity just after the ParallelActivity and move the WaitForApprovalRejection
and the ApproveRejectTaskCompletete to its left branch; make a copy these activities to the right branch.

Rename WaitForApprovalRejection to onTaskApprovalChanged.

Rename onTaskChanged1 to onTaskRejectionChanged.

Rename ApproveRejectTaskComplete to approveTaskComplete.

Rename completeTask1 to rejecteTaskComplete.

Rename the ListenActivity to WaitForManager

Rename the left branch to WaitForApproval and the right branch to waitForRejection
Set the Invoked property of onTaskApprovalChanged to a new event handler :
WaitForApprovalTask_Invoked.

Set the Invoked property of onTaskRejectionChanged to a new event handler:


WaitForRejectionTask_Invoked.

We now have to change the correlation token : the left branch activities will be correlated with
ApproveToken and the right branch activities with RejectToken.

Changing the taskId

The activities on each branch must use the same taskID : we need to create 2 new taskId : ApproveTaskId
and RejectTaskId at the level of respectively approveTask and rejectTask activities (taskId property –Bind
to a new member-Create field).

Set the taskId property of the left branch activities to approveTaskId and to rejectTaskId for the right
branch activities.

Changing the TaskProperties

Since we’ll have 2 tasks, we need to stored the tasks data in 2 news TaskProperties ; bind the
Taskproperties property of the approveTask and rejectTask activities to 2 new taskproperties field
members : ApprovedTaskProperties and RejectTaskProperties
Changing the TaskProperties

Since we’ll have 2 tasks, we need to stored the tasks data in 2 news TaskProperties ; bind the
Taskproperties property of the approveTask and rejectTask activities to 2 new TaskProperties field
members : ApprovedTaskProperties and RejectTaskProperties .

Changing the AfterProperties

Bind the Afterproperties property of onTaskApprovalChanged and onTaskRejectionChangedand 2 new


TaskProperties field members : AfterApprovProps and AfterRejectProps.

Set the task Guid, task form and task properties

Code the invoking methods as follows:

private void RejectTask_MethodInvoking(object sender, EventArgs e)


{
this.RejectTaskId = Guid.NewGuid();
this.RejectTaskProperties.Title = "Reject this expense report";
this.RejectTaskProperties.AssignedTo = Manager;
this.rejectTask.TaskProperties.TaskType = 1;
}
private void ApproveTask_MethodInvoking(object sender, EventArgs e)
{
this.ApproveTaskId = Guid.NewGuid();
this.ApprovedTaskProperties.Title = "Approve this expense report";
this.ApprovedTaskProperties.AssignedTo = Manager;
this.approveTask.TaskProperties.TaskType = 0;

The TaskType property will be used to link the forms to the appropriate tasks.

Let’s modify the workflow.xml file :modify the task0_FormURN to the approval form template Id and
add define a new task form (task1_FormURN) for the rejection form .
Double click on the onTaskApprovalChanged and on the onTaskRejectionChanged activities and set their
event handler as follows:

private void onTaskApprovalChanged_Invoked (object sender, ExternalDataEventArgs e)


{
string acceptedRejectedStatus =
this.AfterApprovProps.
ExtendedProperties["status"].ToString();
this.WorkflowProperties.Item["Status"] = acceptedRejectedStatus;
this.WorkflowProperties.Item.Update();
}

private void onTaskRejectionChanged_Invoked (object sender, ExternalDataEventArgs e)


{
string acceptedRejectedStatus =
this. AfterRejectProps.
ExtendedProperties["status"].ToString();
this.WorkflowProperties.Item["Status"] = acceptedRejectedStatus;
this.WorkflowProperties.Item.Update();
}

We need to improve the workflow design: indeed 2 tasks (Approve and Reject) will be created, and the
manager can only perform one of them: for instance, if the manager approves the expense report, then the
reject task should automatically be deleted.

Drag and drop 2 delete DeleteTask activities at the end of each branch of the ListenActivity:

Rename the DeleteTask of the left branch to deleteRejectionTask an and bind its TaskId property to the
existing field RejectTaskId; set its correlationToken to RejectToken.

Rename the DeleteTask of the right branch to deleteApprovalTask an and bind its TaskId property to the
existing field ApproveTaskId set its correlationToken to ApproveToken.
The following picture illustrates a important part of the workflow left branch:
Build the solution, call install.bat and test the workflow; you’ll notice that 2 tasks have been created; if
you select the reject task and if you click on the reject button in the form, then the Approve task will be
removed and vice-versa.

Step 7: Using association and initialization forms


Download the code

Scenario:

What we want to illustrate here is the initialization form; the current user has a manager defined in the
Managers table, therefore most of the time the workflow must be able to retrieve the manager; but in
some situations the manager could be in vacation ->the user needs to specify a temporary manager;
we can achieve this by defining an initialization form which is a form that will show up when the user
will actually start the workflow.

Hands-On

We’ll start from step5 solution.

Let’s design a new blank Infopath form template:

Name the field Account and rename the Data Source group to TemporyManager (right click on the data
source- properties).

Add a new submit data source (Design tasks-DataSource-Manage Data Connections-Add-Create a New
Connection to Submit Data-To the Hosting Environment…); name it Submit.

Add a button, name it Submit and link it to the following actions:


Submit data using a data connection (select the Submit data connection)

Close the form

Set the security of the form to Domain and make sure the form can be opened in the browser.

Publish the template to the network and name it InitManager.xsn. Copy it later to your project folder.

Build the project, uncomment the following lines in the install.bat file, save and call install.bat.

::Note: Uncomment these lines if you've modified your deployment xml files or IP forms
stsadm -o deactivatefeature -filename U2U.ExpenseReport\feature.xml -url
http://wss.u2ucourse.com/sites/sergeworkflows/
stsadm -o uninstallfeature -filename U2U.ExpenseReport\feature.xml
http://wss.u2ucourse.com/sites/sergeworkflows/
Create a new workflow association and test the workflow; when you start it, the Initialization form should
show up :

We need to retrieve the manager user account (later on we’ll use a more specialized control, but in the
meantime keep the textbox .

One way is to generate a class based on the class schema that will be used to deserialize the xml
information stored in the InfoPath form : the .Net sdk tool xsd.exe provides such possibility.

Start the Visual Studio 2005 Command prompt (Visual Studio Tools menu):

Open the Infopath form in design mode, select the save as source File option in the file menu and save it
in a subfolder (e.g initformsources). The following files will be generated :
We need to run xsd.exe on the .xsd file :

At the VStudio 2005 console, type :

A new file (myschema.cs) will be generated; add to it to your project and rename it to
TemporyManagerInitForm (make sure the class is still TemporyManager) as well as the class inside
.Let’s retrieve the form informations in the onWorkflowActivated1 event handler:

XmlSerializer serializer = new XmlSerializer(typeof(TemporyManager));


XmlTextReader reader = new XmlTextReader(new
System.IO.StringReader(WorkflowProperties.InitiationData));
TemporyManager tempManager = (TemporyManager)serializer.Deserialize(reader);
this.Manager = tempManager.Account;

Lets modify our workflow design to use the provided manager or to retrieve it from the list.
If the manager is provided in the form, we will use it, otherwise we will retrieve it from the list.
Drag & drop and IfElse activity, move the retrieveManager activity to the left branch and rename it as
follows:
Set the ifUsualManager condition as a Code Condtion:

Here is the Conditional function code :

private void ifManagerNotProvided(object sender, ConditionalEventArgs e)


{
e.Result = this.Manager.Trim() == string.Empty;
}

Build your solution, call install.bat and test the workflow.

Using the Contact selector control to select the Temporary Manager

1°Install the Contact Selector control:

Open the InitManager Infopath form in design mode; go to the design task and click on Controls;Select
Add or Remove Custom Controls; click on Add, select ActiveX Control; click on next and select
ContactSelector:
Click next and select Don’t include a .cab file; For Binding Property select Value and click Next; from the
Field or group type box choose Field or group (any data type) and click Finish:
2°Create the data structure for the Contact Selector Control

The Contact Selector control needs to have a specific data structure to work

Spelling and capitalization must be exactly the same, starting with the “Person” group!

· Add a non-Repeating Group named: gpContactSelector

· Add a Repeating Group named: Person

· Add the following 3 text fields to the Person group: DisplayName, AccountId and AccountType :
Drag and drop the TemporyManager group to the form and design the form like this:

Save the form as sources, publish, copy the published template to the project folder and re-generate the
class with xsd.exe as we previously did.

The code to retrieve the manager in onWorkflowActivated1 event handler should look like this :

XmlSerializer serializer = new XmlSerializer(typeof(TemporyManager));


XmlTextReader reader = new XmlTextReader(new
System.IO.StringReader(WorkflowProperties.InitiationData));
TemporyManager tempManager = (TemporyManager)serializer.Deserialize(reader);

if (tempManager.gpContactSelector.Length > 0)
this.Manager = tempManager.gpContactSelector[0].AccountId;
Rebuild the solution , call install.bat and test the workflow with and without a temporary manager.

The association form is very similar to the Initialization form: it will show up when the workflow
association is being made.The firm template should be inserted in the Association_FormURN child
element of the <MetaData> element (in workflow.xml)

< Association_FormURN><insert association form templateId here></ Association_FormURN>

Step 8: Bringing data from the workflow to the task form


Download the code

Scenario

The manager needs all the necessary information to approve or reject the expense report; therefore we
need to display the expense report data into the task form.

Hands-on

Start from step 7 solution.

Open the Infopath task form ( ApproveReject.xsn) in design mode .

Update the form like this:


Rename the controls in order to have the following Data source:

The content of these extended properties will be sent to the Infopath Task form.

Create the following .xml file and name it ItemMetadata.xml (Spelling and capitalization must be exactly
the same here: both for the file name and for the extended properties name, each must be prefixed with
ows_):

Click on the ApproveRejectTask activity and add 3 new extended properties to the
ApproveRejectTaskproperties:

Add a new Data Connection to link the infopath form to this xml file:
Click on Next:
Click on Next and select the .xml file:
Click on Next and select the first option :
Keep the default options and click on Finish.

Now we are going to bind the form field to the new Data Connection fields:

In the task form, click on the submitter field :


In the data panel, click on the formula (fx) button:

Click on the insert field or group button and switch the Data source to ItemMetadata (secondary):
Select ows_submitter :

Click Ok.

Repeat this for the amount and the description fields.

Save and publish the form; make sur the published template is in your project folder.

Rebuild the solution, call install.bat and test the workflow by following the next scenario:

Add a new user account : Jon; Jon will be Bill’s manager.


Log in as Serge (Serge’s manager is Bill, not Jon) and submit the following expense report:

Start the workflow: normally Serge’s manager is Bill; if Bill is on vacation,serge will submit the expense
report to Jon:

A task will be assigned to Jon:


If Jon clicks on the task hyperlink:

You might also like