SharePoint 2010 State Machine Workflows using Visual Studio 2010 Part 1


Part 1 – Building a State Machine Workflow using Visual Studio 2010

Demo project

Introduction

Workflows are systems that manage the execution of a business process. They are primarily described as a process that manages the flow of work among various individuals, or divisions, or multiple organizations.

As compared to Sequential Workflows which always progresses forward and never goes back (although using a while loop you can reiterate through same steps multiple times), the State Machine Workflows moves from one state to another, until the workflow concludes by reaching the completed state.

In this example we will simulate the hiring process for an organization. Our workflow will have following steps.

  1. Interview Application Received.
  2. Initial Clearance.
  3. Technical Clearance.
  4. HR Clearance.
  5. Accepted.
  6. Rejected.
  7. Completed.

Before starting with workflow we must have a list (or a library) named Candidates on in our SharePoint Application. Metadata related to the list will have following columns.

  1. Title
  2. Technology
  3. Experience (in months)
  4. Status

You can add columns as required.

State Machine Workflows are essentially event driven workflows. We create a task and attach an event handler to it. We then wait till the task is performed, and after the task is completed; we make a decision based on the user input.

Implementation

Step 1


In Visual Studio, select a SharePoint 2010 State Machine Workflow template. Fill in the details related to Name and Solution etc., and click Ok. Refer Fig 1.1.

Fig 1.1

In next window enter the URL of the site we want our workflow to run on, and click on “Next” button. Workflows can only be deployed as a Farm solution, so we do not have an option to deploy it as a Sandbox Solution.

In the next Window give the name for the workflow, and select a type of workflow we want to create. We will select a List Workflow for this example.

The next window gives us an option of associating our workflow to a list or a document library. We can choose not to associate our workflow by unchecking the association check box. However, we will have to associate the workflow to a list or library manually later on in that case. In our example we will associate the workflow with the Candidates list.

The next window allows us to set a trigger for our workflow. In our example we want our workflow to automatically start when an item is created in the Candidates list. So we will choose that as an option and click on “Finish” button.

Step 2


Let us have closer look at the Solution Explorer.

The references required for the workflow are automatically added into the solution by the IDE. We may manually add references as and when required.

Feature1 is a feature in which our workflow will be deployed. This feature can be activated and deactivated from Site Settings

Elements .xml will be used to configure various properties of our workflow. We will have a detailed look at this file later on in the tutorial.

Workflow1.cs is the code behind file of our workflow. All the server side code goes into this file. Refer Fig 1.2

 

Fig 1.2

Before we go any further we need to configure the Feature Receiver of our workflow. To do that click on the Workflow Design file and press ‘F4’. This opens up the properties window of workflow. In the properties window we can see Feature Receiver Property. Open the tree node of the property and it has two properties viz. Assembly and Class Name.

Type in the following values for these properties. Please make sure that you look up the latest version of the assembly available for Microsoft.Office.Workflow.Feature and the Public Key Token related to it.

For Assembly: Microsoft.Office.Workflow.Feature, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c

For Class Name: Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver

Step 3


We will design our workflow now. On the designer surface of the workflow, drag drop “State” activity. As discusses earlier, we have 9 activities in our workflow, drop in total of 7 “State” activities from the toolbox and give them appropriate names. Refer Fig 1.3.

Fig 1.3

We need to set an initial state and a completed state for our workflow. As we want “InterviewApplicationReceived” state to be our initial state, right click on it and choose “Set as Initial State” option in context menu. Similarly right click “Completed” state and choose “Set as completed state”. This is makes “Completed” state as the final state of our workflow.

Step 4


We need to initiate our workflow. For initiating the workflow we drop an “EventDrivenActivity” in the initial state of the workflow, i.e. “InterviewApplicationReceived” state. There are two ways of add the event driven activity to our state. Either you can drag-drop the activity from the toolbox or simply right click the state and click “Add EventDriven” in the context menu.

After adding the activity, double click on the activity to go to the details of activity, give it an appropriate name from properties window. We name it as “eventDrivenActivityInitial” in our workflow.

Next step is to drop “OnWorkflowActivated” Event handler to our event driven activity. Drag and drop the “OnWorkflowActivated” event handler from the toolbox onto the activity canvas. The properties window shows properties associated with the event handler.

Fig 1.4

Let us go through the properties of the OnWorkflowActivated event handler. Most of these properties are common to all the event handler activities. Refer Fig 1.4.

A workflow instance interacts with several SharePoint objects (task lists, and documents or items) during its lifetime. In SharePoint you can have many workflow instances running at the same time and interacting with several objects at the same time. To be able to identify which workflow instance is interacting with which object, you must use an identifier that links one particular workflow instance to one particular SharePoint object. Each workflow instance must be bound to its own items.  Such an identifier is called a Correlation Token. A correlation token links a workflow instance to its documents, items, and tasks. It prevents confusion between different workflow instances. We need have one correlation token for the workflow and one for each task we create during the workflow, so that when a user completes a task we have a unique identifier with which to receive any input from the user into the workflow. Make sure correlation token names are not repeated in different task. Type “InitialToken” (though the name of token can be anything you want, we generally prefer nomenclature of Task name suffixed by Token). Choose the Owner Activity as “InterviewApplicationReceived” State.

Invoked event of the OnWorkflowActivated will be fired when the workflow is activated. We can write the logic related to workflow activation in this event. In this example we use “onWorkflowActivatedInitial_Invoked” as the method name. The Visual Studio IDE will create write an event handler block in the code behind file once you press tab key after typing in the method name.

Next, click on the ellipsis in WorkflowProperties property. This opens a pop-up, choose workflowProperties from “Bind to an existing member” tab. If workflowProperties cannot be found in this tab, move to the next tab i.e. “Bind to a new member”, select “Create Field” radio button and type “workflowProperties” in new member name textbox, and press Ok.

Next drop “SetState” Activity (choose from 3.0 tab of toolbox, there is SetState under SharePoint tab as well) just below the event handler. Set the target state name as “Initial Clearance” State. This completes the configuration of our initial state.

Step 5


Now let us configure the Initial Clearance State. Here we are going to add the “StateInitialization” activity. In StateInitialization activity, we will create the task, and configure the details like participants, due date, etc.

So drag a “StateInitialization” activity (from 3.0 tab of the toolbox) and drop it inside the Initial Clearance State. We will name this activity as “stateInitializationInitialClearanceActivity”. Double click the activity and drag “Create Task” activity (from SharePoint tab of the toolbox) and drop it inside the state initialization activity. Rename the Create Task activity to “createTaskInitialClearance”. Create a new correlation token by typing token name in the properties window. We will name the token as “InitialClearanceToken” and set owner activity as Initial Clearance State.

We need to bind a method with “Method Invoking” event. The nomenclature for method invoking event will be task name followed by an underscore (_) appened by methodInvoking, so for Initial Clearance task the method name will be “createTaskInitialClearance_MethodInvoking”.

Next we need to set the values to both TaskId and TaskProperties. Click the ellipsis in the TaskId property. It opens up a pop-up window. In “Bind to a new member” tab, choose “Create Field” radio option. Enter the name in textbox and click Ok. We create a TaskId with name “createTaskInitialClearanceTaskId”. Similarly for TaskProperties create a new field (not property) with name “createTaskInitialClearanceTaskProperties”. We are done with configuration of Create Task and State Initialization.

Step 6


Drag an Event Driven activity (from 3.0 tab of the toolbox) and drop it inside the Initial Clearance State. Name it as “eventDrivenActivityInitialClearance”. Double click the Event Driven activity, and drop “OnTaskChanged” activity (from SharePoint tab of the toolbox) and name it “onTaskChangedInitialClearance”. Create fields for AfterProperties and BeforeProperties (follow the process similar to creating TaskId and TaskProperties) with names “onTaskChangedInitialClearanceAfterProperties” and “onTaskChangedInitialClearanceBeforeProperties” respectively.

Now in Correlation Token field, choose the same token that we had associated the Create Task activity with. That allows the workflow instance to understand with which task this event handler is associated with. In our case we will choose “InitialClearanceToken” as the correlation token. Most of the workflow errors are related to incorrect assigning of the correlation token. Hence we need to be extra careful when working with the correlation tokens.

Associate a method with Invoked event of the activity, we type in “onTaskChangedInitialClearance_Invoked” as the method name. We also need to bind the TaskId for this activity but we won’t be creating a new TaskId, instead we will bind the TaskId that we created in the Create Task activity. For that choose “Bind to an existing member tab” in the pop-up, select the TaskId that we had created in Create Task activity, and click Ok.

So to re-cap of what we have achieved so far, we created State, added a StateInitialization activity. Inside the StateInitialization Activity, we created a Task and gave it a correlation token. On MethodInvoking event of the CreateTask activity we have associated a method. We will write some logic in this method. Then we added an EventDriven activity to the State. In the EventDriven activity we have dropped an OnTaskChanged activity. We created the required fields for this activity, and gave it the same correlation token that we created in CreateTask activity. We associated a method with Invoked event of the OnTaskChanged activity. This method will be fired when user performs the task assigned to him.

Step 7


Now once the user performs a task, in this scenario, when the user indicates whether the received resume is approved or rejected, we need to perform some action based on it. A default task list item looks like Fig 1.5. We can customize the task list item, and use InfoPath form or an ASPX form instead of the default task list item.

                              Fig 1.5

For now if user types in “Approved” in the description column of the task, then we will proceed with next step of the workflow; else we will stop the workflow. So we drop an IfElse Activity (From 3.0 tab of the toolbox) below the OnTaskChanged activity, inside the EventDriven activity.

Select IfElseBranchActivity1, in properties window we have a Condition Property, select Code Condition for condition. Open the Condition tree node, in the left node property, type the name of the method which will have the logic to handle the condition. For this example we type in “InitialClearanceApprovalProcess” as the code condition. Now drop SetState Activity (from 3.0 tab of the toolbox) inside the IfElseBranchActivity1 and select the next state as the TargetStateName. In this example, “TechnicalClearance” will be the target state. Leave the IfElseBranchActivity2 as it is.

Step 8


We are done with the designing part of the workflow for this state, now we need to write the logic in code-behind file. So right click on the designer and select View Code from the context menu, or simply press F7 on the designer surface.

You can see all the fields and methods that we created from designer surface have been created in the code-behind file.

The first method is associated with OnWorkflowActivated activity. We won’t add any implementation logic in this method as of now.

Next, the method associated with Initial Clearance State -> Event Driven -> OnTaskChanged. We have associated a method “onTaskChangedInitialClearance_Invoked” with this event. In this method we will write the logic of the process we want to follow once the user has completed the task.Next, the method associated with Initial Clearance State -> State Initialization -> Create Task. We have associated a method “createTaskInitialClearance_MethodInvoking” with this event. In this event we will configure the title of the task, the users or group that will perform the task, due date of the task, and other such details.

private void createTaskInitialClearance_MethodInvoking(object sender, EventArgs e)
{
            try
            {
                        //Create a new TaskId for the Task
                       this.createTaskInitialClearanceTaskId = Guid.NewGuid();
                        //TaskProperties field is used to configure the Task Details.
                       this.createTaskInitialClearanceTaskProperties.Title = “Initial Clearance”;
                        //Assign a Task to a user or to a group. We assign the task to HR-Group
                        this.createTaskInitialClearanceTaskProperties.AssignedTo = “HRGroup”;
                        this.createTaskInitialClearanceTaskProperties.DueDate = DateTime.Today.AddDays(5.0);
             }
             catch (Exception ex)
             {
                         //throw ex;
             }
}

If we have a look at onTaskChangedInitialClearanceAfterProperties and onTaskChangedInitialClearanceBeforeProperties field, they are objects of SPWorkflowTaskProperties Class. As the name suggest this class represents the properties of a Workflow Task. We will assign the values from task to these objects so that we can use them other methods.

In this example we will set the task as complete, by setting the value for  PercentComplete Property to 1.0 .

private void onTaskChangedInitialClearance_Invoked(object sender, ExternalDataEventArgs e
{
try
{
                    this.onTaskChangedInitialClearanceAfterProperties =
                     this.onTaskChangedInitialClearance.AfterProperties;
                     this.onTaskChangedInitialClearanceBeforeProperties =
                     this.onTaskChangedInitialClearance.BeforeProperties;
                      //Set the PercentComplete property to 1.0 (i.e. 100%) to indicates task completion.
                      this.onTaskChangedInitialClearanceAfterProperties.PercentComplete = (float)1.0;
}
catch (Exception ex)
{
//throw ex;
}
}

Now we will implement the logic for Code Condition. The IfElse Activity for the task was associated with the InitialClearanceApprovalProcess method.

private void InitialClearanceApprovalProcess(object sender, ConditionalEventArgs e)
{
         try
        {
        if (this.onTaskChangedInitialClearanceAfterProperties.PercentComplete == 1.0)
       {
             if (this.onTaskChangedInitialClearanceAfterProperties.Description.Contains(“Approved”))
            {
                  e.Result = true;
            }
            else
           {
                 e.Result = false;
           }
      }
      else
     {
           e.Result = false;
      }
  }
catch (Exception ex) {}
}

We have set the result of if-else to true if user types in “Approved” in the description field of the task item, else the result is set to false. In later part of this tutorial we will see how to use a custom task form to get these values. So we can have a drop-down control for user to indicate whether the candidate it approved or rejected.

Step 9


That’s all the code we have to write to completely configure one task. Perform Step 5 to Step 8 for the remaining tasks.

You can see below a snapshot of the completed workflow.

Build and deploy the workflow.

Advertisements

About Rupesh

.NET and SharePoint 2010 developer
This entry was posted in Workflows and tagged , , . Bookmark the permalink.

24 Responses to SharePoint 2010 State Machine Workflows using Visual Studio 2010 Part 1

  1. I have two questions…

    1. Why would we assign a variable to itself (this.value = this.value) like you do in the MethodInvoking part of the code?

    2. Your diagram has each state moving on to the Rejected state, but you never cover where we should include the set state for that, is it in the other branch of the ifElse, or is there a third branch that covers that part?

    Thanks!
    Joshua
    CPS student

    • Rupesh says:

      Hello Joshua,

      Thank you for the comment.

      If you are talking about the

      this.onTaskChangedInitialClearanceAfterProperties = this.onTaskChangedInitialClearance.AfterProperties;
      this.onTaskChangedInitialClearanceBeforeProperties = this.onTaskChangedInitialClearance.BeforeProperties;

      in the onTaskChangedInitialClearance_Invoked function, then it is not as you see it.

      We are assigning a value to the global variable onTaskChangedInitialClearanceAfterProperties from the AfterProperties of onTaskChangedInitialClearance.
      The names look same, but they are not.

      For the second part of your question, yes you do it in the else branch of the workflow. It really depends on the business logic that you are implementing.

      For the ease of understanding I have attached a demo project to the post.

      Let me know if you have anymore queries.

      • Thanks for your quick reply. Now that I see it in non-italics, the difference is clear. I’m using your example to better learn the state machine for a project I’m going to be starting soon, so I appreciate that your example isn’t the same cookie-cutter document approval workflow that everyone else is using from TechNet 🙂

      • Arun says:

        Please post above example in Sequential workflow…

  2. Kishu says:

    Thanks Rupesh for the nice post.
    It helped me a lot. but still some screen shots are missing. Can i have a Source Code of this Example.

    Thanks
    Kishan Srivastava
    SharePoint Consultant

  3. Mark says:

    Hi Rupesh,

    Thanks for the great article. I’m buidling a State Machine also, but have one question though. What if you want to complete a state not 100% but for instance 50%, based on the # fields filled out in a certain form (I’m working with custom forms and custom content types). This would imply the state remains ‘open’ until the other 50% is complete. Problem is.. If I do that, the 50% is updated okay, but the next time I change the task to complete it to 100% I get an error while doing an AlterTask in my form codebehind and the wotkflow says the task is “locked by a running instance”. It looks like you can only update a task in one step to completion and only after that it would be ok to get to that state again in the future. Is this true?

    Thanks,
    Mark

    • Rupesh says:

      Hello Mark

      I think updating the task twice should work fine, in fact I tried doing it and worked fine. The reason why there is percentage complete in workflow is for updating the task multiple times. Will it be possible for you to copy-paste here, the Alter task code that is throwing the exception?

  4. Murad Meghani says:

    Great article. I am developing a State Machine Workflow for a client. I have two questions:
    1. How do you take care of versioning?
    2. How can state machine WF do parallel task – say using your example above after Initial Clearance I want both the Technical and HR Clearance to work in parallel?
    Thanks

  5. MOHAMMED says:

    Thanks Rupesh for the nice post.

  6. Ana Petrić says:

    Hi!
    This was very helpfull.. What I need more are forms – initialization form and custom task forms. For task forms, I’m using CreateTaskWithContentType activity instead of CreateTask, with my custom ContentType inherited from WorkflowTask content type. It works in a way that form does show all the fields defined in content type, but I would like to use my custom task forms created for content type, but I’m not being able to do that. Can you help me with that? Also, what about initialization form, I don’t even know where to start with that..
    Thanks, Ana

  7. srikanth says:

    Hi,
    The download link is no more working. Can you please provide the working link?

  8. Rupesh says:

    Thank you for the link. I couldn’t update the link for this article.

  9. DT says:

    I have a requirement to create a list and mutiple divisional employees need to enter data.

    However, they should not be able to see each other (divisions) data.
    I was able to do this by adding a criteria to the personal view (created by = ME). Then I created a web-part with this view and permission given to that group of employees.

    Now there is a supervisor group who need to see the data entered by their employees only and so
    I am inserting a division value by looking up a divisionlookup list by matching created by in the current list.

    The problem with this approach is that it only looks at the first row in the lookup list so whoever is listed at the top of the list gets a division name inserted in the current list. For everyone else the division column is blank because their name does not match the first row in the division lookup list,

    Lastly a user from any group can create a new view to see anyone’s data.
    Do you know how to resolve this?
    *** We do not have server access, we have SPD 2010 and VS 2010 will be installed soon.

    • DT says:

      I also tried to follow this which something similat to what I had done with the webpart and view permissions.
      http://sharesilver.wordpress.com/2012/07/01/permission-restricting-options-in-sharepoint-list-views/

    • Rupesh says:

      Hello DT,

      I am unable to understand the exact issue that you are facing. Can you elaborate, or may be send your code or snippet of what you are trying to so?

      • DT says:

        I am using out of the box functionality so no coding involved yet.

        Basic need is to have a list where all employees must enter data daily. These employees are from different divisions and they should not be able to see each others (divisions) data.
        I was able to do this by adding a criteria to the personal view (where created by = ME). Then I created a web-part with this view and permission was given to each (division) group of employees.

        Now there is a supervisor group which needs to see the data entered by “their” employees only and so I am inserting a division value in a column of the current list in discussion. This is done by looking up a divisions list by matching “created by” in the current list to the divisions list.

        The problem with this approach is that it only looks at the first row in the divisions list so whoever is listed at the top of the list gets a division name inserted in the current list. For everyone else the division column is blank because their name does not match the first row in the division lookup list,

        Another problem a user from any group can create a new view (unfiltered) to see anyone’s data.
        Do you know how to resolve this?
        *** We do not have server access, we have SPD 2010 and VS 2010 will be installed soon.

  10. DT says:

    Rupesh,
    I rephrased my question so hope that you can follow it now. If not then I am not sure how I would be able to explain this.

  11. Hello would you mind stating which blog platform you’re working with? I’m going to start my own blog
    in the near future but I’m having a tough time making a decision between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your design seems different then most blogs and I’m looking for something
    unique. P.S Sorry for getting off-topic but I had to ask!

  12. Suresh Manakal says:

    Dear Rupesh,

    Indeed,it’s one of the best SharePoint 2010 custom workflow development article using InfoPath forms as well.

    Thank you for the nice articles.
    Regards,

  13. Aya Badawy says:

    Thanks very much , that is really helps me

  14. samolpp@gmail.com says:

    hello
    thnx for the great article. but can you pls help, how to create custom task forms in vs workflow?
    i need to create custom approval buttons like approve,reject,send back to requestor, cancel .
    on click of these, i need to perform appropriate actions.
    any idea how to achieve this?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s