Creating a SharePoint Sequential Workflow Using a Custom Task Approval Field

Summary:  Learn how to programmatically create and test a SharePoint sequential workflow.

Applies to: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Justin Joyce, LANtek Computer Services

Contents

  • Introduction to Sequential Workflows

  • Creating a New Site Collection

  • Creating the Columns and Content Types

  • Creating the SharePoint Sequential Workflow in Visual Studio

  • Testing the Sequential Workflow

  • Conclusion

  • Additional Resources

Introduction to Sequential Workflows

Microsoft SharePoint Server 2010 sequential workflows developed in Microsoft Visual Studio 2010 enable you to create powerful tools for streamlining business processes and task approvals. A key component to designing an effective and efficient workflow is to align it as closely as possible to the business process it is augmenting, both in logical function and user experience interaction. Central to this is the use of language and input validation that accurately reflects the progressive routine and meets the expectations of the end users.

To illustrate this type of functionality, this article demonstrates creating a SharePoint 2010 sequential workflow in Visual Studio 2010 that stops task completion until the text in a list item field exceeds 100 characters. In addition, the workflow leverages a custom approval field on the workflow task. Instead of the standard Note Stated or Completed or In Progress text contained in the built-in Status field, the task adds a Business Approval field containing either I Approve or I Do NOT Approve. The workflow programmatically sets the Status field to Completed when the item and task meet the required criteria. If the description field is not filled properly, the task reverts to the In Progress status and the workflow logs to the history list that a required field is missing.

Creating a New Site Collection

In the following steps, you create a new site collection.

Note

For more detailed instructions, see Create a site collection.

To create the new site collection

  1. Open Central Administration and then click the Application Management link in the left navigation pane. The Application Management home page appears.

  2. Under Site Collections, click Create Site Collections.

  3. In the Create Site Collections page, ensure that the yellow box in the upper-right corner of the page (below the OK and Cancel buttons) displays the name of the new Web Application created in Step 1.

  4. After selecting the correct Web Application, type a title. You may leave the description field blank if desired.

  5. For Web Site Address, select a valid address and record it for later use in the Visual Studio 2010 project. An example of a valid address is http://servername:port/.

  6. Under the Template Selection area, in the Select a template box, click the Publishing tab, and then select Publishing Portal. Figure 1 shows the Select a template box.

    Figure 1. Choosing the site template

    Choosing site template

    Although you can use other site template types, the publishing portal comes with the Workflow Tasks list already provisioned. If your instance of SharePoint does not have the Publishing Portal template available, you can use a Team Site template and provision the Workflow Tasks list.

  7. Under Primary Site Collection Administrator, type the Active Directory account name of the primary site-collection administrator. If desired, also type a secondary site-collection administrator in the next box. Leave the Quota Template option set to No Quota.

  8. Verify that all options are correct and then click OK.

Creating the Columns and Content Types

In the following steps, you create the custom columns, content types, custom list, and workflow task list.

To create the custom columns

  1. Navigate to the newly created site collection and ensure that the site is up and running.

  2. When the site is loaded, click the Site Actions menu in the upper-left corner and then click Site Settings.

  3. Under the Galleries heading, click Site Columns.

  4. In the upper-left corner of the Site Columns page, click Create to create a new choice column and then name it Business Approval.

  5. Format this column as a drop-down list containing the following values:

    • Undecided (Default)

    • I Approve

    • I Do NOT Approve

    Leave the default value as Undecided (Default), the group as Custom Columns, and the description field blank.

  6. Next, create another site column to act as the description field that is required to be filled in for the workflow to progress. Name the field Request Description and format it to be a multiline plain text field.

  7. To provide better clarity to the end user, type a description in this field stating that it should be longer than 100 characters.

Now that the supporting fields are created, you need to make two content types for use during the workflow progression. In the next section, you create the custom content types.

To create the custom content types

  1. On the Site Actions menu, click Site Settings.

  2. Under the Galleries section, click Site content types.

  3. Click Create in the upper-left corner of the page to make a new content type and name it Initial Request.

  4. In the Parent Content Type section, set Select the parent content type from to List Content Types and set Parent Content Type to Item, as shown in Figure 2.

    Figure 2. Creating a new content type

    Creating new content type

  5. In the Put this site content type into section, select Custom Content Types, and then click OK. The content type management screen is displayed.

  6. Click the Add from existing site columns option to enable you to add the Request Description field to the Initial Request content type.

  7. After adding the Request Description field, click its title under the Columns heading on the content type management screen. The Change Content Type Column screen is displayed; set the column as Hidden, as shown in Figure 3.

    Figure 3. Marking the column as hidden

    Marking column as hidden

  8. Using the previous steps, create a second content type named Request and ensure that it contains the same fields. Because the Request Description column is validated via the workflow, make it a required field to provide more clarity to the end user.

  9. Instead of using Item as the parent content type, select Initial Request.

In the next steps, you create a custom list to hold the two request content types and act as the target for the sequential workflow.

To create the custom list

  1. Return to the Site Actions menu and then click View All Site Content.

  2. Click Create in the upper-left corner of the page to launch the Create dialog box.

  3. Locate the Custom List template (see Figure 4) and name it Requests.

    Figure 4. Finding the custom list template

    Finding custom list template

  4. After the list is created, add your custom content types to it by clicking List Settings on the ribbon.In the General Settings section, click Advanced Settings.

  5. Set the Allow Management of Content Types option to Yes, and then save your changes by clicking OK.

  6. To add your request content types to the list, in the Content Types section, click the Add from existing site content types link. Locate both of the Initial Request and Request content types and add them. Click OK to save your changes.

  7. In the Content Types section, click the Item type and then click Delete this content type to remove it from your list. Only the two custom content types remain.

  8. Finally, click Change new button order and default content type and clear the Visible option next to your Request content type, as shown in Figure 5. This action ensures that end users are restricted to creating only the Initial Request type when adding new list items.

    Figure 5. Setting the default content type for the list

    Setting default content type for list

In the next steps, you create a workflow task list. If you chose to use a template other than Publishing Portal, you are required to create a Workflow Tasks list on your site. If you are using the Publishing Portal template, you can continue on to the section titled Creating the SharePoint Sequential Workflow in Visual Studio.

To create the Workflow Tasks list

  1. Return to the Site Actions menu and then click View All Site Content.

  2. Click Create in the upper-left corner of the page and then locate the Tasks template, as seen in Figure 6.

    Figure 6. Find the Tasks list template

    Find the Tasks list template

  3. Name the new list Workflow Tasks, and then click Create in the right column (see Figure 7).

    Figure 7. Naming and creating the Workflow Tasks list

    Naming and creating the Workflow Tasks list

Creating the SharePoint Sequential Workflow in Visual Studio

In the following steps, you create a sequential workflow project in Visual Studio 2010, and then add events and other properties to the workflow.

To create a SharePoint sequential workflow project

  1. Start Visual Studio 2010. In the left column of the start page, click New Project.

  2. In the Visual C# section of the Installed Templates tree, select SharePoint.

  3. In the project type list, select Sequential Workflow.

  4. Create a new directory for the solution and then name it CustomTaskApproval, as shown in Figure 8.

    Figure 8. Creating a new SharePoint Sequential Workflow project

    Creating SharePoint Sequential Workflow

  5. After the project is created, the SharePoint Customization Wizard is displayed. In the first page, use the URL of the site collection you created previously as the local site for debugging. Ensure that the solution is set to be deployed as a farm solution (see Figure 9), and then click Next.

    Figure 9. Starting the SharePoint Customization Wizard

    Starting SharePoint Customization Wizard

  6. On the next page of the SharePoint Configuration Wizard, type the name of the workflow; in this case, use CustomTaskApproval - Workflow1 as shown in Figure 10.

    Figure 10. Choosing the workflow name and type

    Choosing workflow name and type

  7. Select List Workflow as your template type and then click Next.

  8. On the next page of the SharePoint Customization Wizard, Visual Studio 2010 automatically associates the workflow with the Requests list (see Figure 11). Leave the default values for the history and task lists.

    Figure 11. Setting up the target list and task and history lists for the workflow

    Setting up target list and task and history lists

  9. On the final configuration page, ensure that only the The workflow starts automatically when an item is created option is selected, as shown in Figure 12.

    Figure 12. Selecting the workflow start options

    Selecting workflow start options

  10. With the workflow setup complete, begin adding the events and associated code.

    Note

    By default, Visual Studio adds the onWorkflowActivated1 event to the design surface. This event is not used for this example; however, you can leave it on the design surface.

    From the Toolbox, drag the following activities onto the design surface:

    • Code   Name this activity InitializeItem. The Code activity is responsible for changes and checks of the item on its initial entry into the workflow.

    • CreateTask   Name this activity createRequestTask. CreateTask is responsible for setting some of the properties on the initial creation of the task such as the description, to which person the task has been assigned, and so forth.

    • While   This activity contains another activity that watches for the task to change. It is contingent upon a code condition (more on this later).

    • OnTaskChanged   Name this activity onRequestTaskChanged. The OnTaskChanged activity is responsible for monitoring the task and ensuring any changes that the user makes to the task meet the validation rules—in this case, a description containing more than 100 characters in the item, and the custom approval field set correctly.

    • CompleteTask   Name this activity completeRequestTask. Place this activity directly after the While activity. This activity is responsible for handling the actual completion of the task; that is, setting the built-in status field to Completed.

    With all the activities in place, the design surface should now resemble Figure 13.

    Figure 13. Workflow activities on the design surface

    Workflow activities on design surface

  11. Next, create properties for the activities and binding correlation tokens. The properties need to be created in the code-behind first, as follows.

    public Guid workflowId = default(System.Guid);
    public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();
    
    // these properties are for the workflow task that will be created.
    public SPWorkflowTaskProperties RequestTaskProperties = new SPWorkflowTaskProperties();
    public SPWorkflowTaskProperties RequestTaskBeforeProperties = new SPWorkflowTaskProperties();
    public Guid RequestApproveTaskId = default(Guid);
    public int RequestApproveTaskItemId;
    public bool RequestApproveComplete = false;
    

Then begin associating the correlation tokens with their corresponding activities by perform the following steps:

To associate the correlation tokens with their corresponding activities

  1. For the onWorkflowActivated1 activity, create a new correlation token named workflowToken. Set the owner activity for this correlation token to Workflow1. In a larger scenario, this token would be used for many more activities and events on the design surface, but, for this exercise, it is used only on this activity. The task activities require their own correlation token to be shared between them to ensure that they are working on the same task.

  2. For the createRequestTask, onRequestTaskChanged, and completeRequestTask activities, create a correlation token named RequestTaskToken with an OwnerActivity of Workflow1. You can create this by typing it into the CorrelationToken box on the property pane for the first task event and then selecting it for the other activities.

    Figure 14 shows what the properties pane for the createRequestTask activity looks like.

    Figure 14. Setting the activity properties

    Setting activity properties

  3. Assign the correct public members from the code-behind to the ListItemId, TaskId, and TaskProperties fields by clicking the activity on the design surface and then selecting the property name from the Properties pane. Using the ellipses (), select the member from the Bind to an existing member tab, and then click OK.

    This will need to be done for each task activity on the design surface to ensure that they all have the same correlation token and the public members assigned correctly. The public members differ slightly for each of the task events. For example, onRequestTaskChanged adds a BeforeProperties attribute and completeRequestTask requires only TaskId (see Figure 15).

    Figure 15. Binding the activity property to an existing member

    Binding activity property to existing member

Now that all correlation tokens and properties are set accordingly, you need to generate the associated methods.

To generate method signatures and add the code

  1. Double-click each of the activities on the design surface. Visual Studio 2010 automatically displays the code and generates the method signature required for each activity. The exception to this is the While activity—you must create this method manually.

  2. Create the associated method for the While activity by adding the following code.

    private void notRequestTaskApproved(object sender, ConditionalEventArgs e)
    {
        e.Result = !RequestApproveComplete;
    }
    

    In the properties pane for the While activity, set the Condition attribute to Code Condition and the Condition attribute contained under it to notRequestTaskApproved, as shown in Figure 16. These settings cause the While activity to continue looping until the method returns the appropriate response.

    Figure 16. Setting up the Code Condition property for the whileActivity

    Setting up Code Condition prop for whileActivity

  3. Next, add the following two helper methods.

    private void ResestTask(SPListItem task, SPListItem currentItem, string message)
    {
        LogComment(message + currentItem.Title);
    
        // This line is very important. It keeps you from receiving 
        // the error "This task is currently locked by a running workflow."
        task[SPBuiltInFieldId.WorkflowVersion] = 1;
        task["Status"] = "In Progress";
        task["Business Approval"] = "Undecided";
        task.Update();
    }
    
    private void LogComment(string logMessage)
    {
        SPWorkflow.CreateHistoryEvent(workflowProperties.Web, this.WorkflowInstanceId, 0, workflowProperties.Web.CurrentUser, new TimeSpan(), "Update", logMessage, string.Empty);
    }
    

    The first helper method, ResetTask, is called only when a task needs to be reverted to an In Progress state, such as when a user attempts to complete it before filling in the required description field on the list item. This rollback method is required for the workflow solution to prevent the This task is currently locked by a running workflow error that occurs when a task is rolled back to In Progress via code, if a user tries to change it again later. This error message is related to the workflow version on the task item; hence, explicitly setting the WorkflowVersion property to 1.

    The second method is a simple logging function that enters updates to the Workflow History list.

  4. With the setup of the workflow project in place, you can begin filling in the logic for the associated methods in the code behind file. Add the following code to the InitializeItem_ExecuteCode method:

    private void InitializeItem_ExecuteCode(object sender, EventArgs e)
    {
        // Change the content type from Initial Request to simply Request.
    
        // This change makes Request Description a required field.
        SPListItem currentItem = workflowProperties.Item;
    
        currentItem["ContentTypeId"] = workflowProperties.List.ContentTypes["Request"].Id;
        currentItem.Update();
    }
    

    When a request is entered, it is picked up by the workflow upon item creation. With the list set to allow users to enter requests with the content type of only Initial Request, the workflow can now change the content type of the item to Request. Changing the item to the Request type enables the Request Description field to display on the edit form as a required field.

  5. Next, add the following code to the createRequestTask_MethodInvoking method. This is where you set up all the applicable properties for the workflow task.

    private void createRequestTask_MethodInvoking(object sender, EventArgs e)
    {
        // Now create the approval task.
        SPListItem currentItem = workflowProperties.Item;
    
        // Set up some of the properties.
        RequestApproveTaskId = Guid.NewGuid();
        RequestTaskProperties.Title = workflowProperties.Item["Title"].ToString() + " is ready for review";
        RequestTaskProperties.Description = "Please review the request and ensure it is valid.  If it is valid, then please select 'I Approve' on this task and save it.";
        LogComment("Request Task Created.");
    }
    
  6. Finally, add the following code into the onRequestTaskChanged_Invoked method.

    private void onRequestTaskChanged_Invoked(object sender, ExternalDataEventArgs e)
    {
        int tid = onRequestTaskChanged.AfterProperties.TaskItemId;
        SPListItem task = workflowProperties.Web.Lists["Workflow Tasks"] .GetItemById(RequestApproveTaskItemId);
    
        SPListItem currentItem = workflowProperties.Item;
    
        try
        {
            // Ensure the field exists on the item.
            if (task["Business Approval"] != null && currentItem["Request Description"] != null)
            {
                // Evaluate the value of the field.
                    if (task["Business Approval"].ToString() == "I Approve" || task["Business Approval"].ToString() == "I Do NOT Approve")
                        {
                            if (currentItem["Request Description"].ToString().Length >= 100)
                            {
                                RequestApproveComplete = true;
                            }
                            else
                            {
                                RequestApproveComplete = false;
                                ResestTask(task, currentItem, "Request Description is not long enough.");
                            }
                        }
                        else
                        {
                            RequestApproveComplete = false;
                            ResestTask(task, currentItem, "Business Approval is not set.");
                        }
            }
            else
            {
                // This should never happen because there is a default, but always good just in case.
                RequestApproveComplete = false;
                ResestTask(task, currentItem, "Business Approval status is null or no Request Description.");
            }
        }
        catch (Exception ex)
        {
            RequestApproveComplete = false;
            ResestTask(task, currentItem, "Exception.");
            LogComment(ex.ToString());
        }
    }
    

    This method evaluates the Request item, ensures that the Request Description field is properly filled in, and that the associated workflow task has been marked with the correct status code. If all criteria are met, RequestApproveComplete returns True, causing the While activity to exit.

  7. Finally, add the following code to the completeRequestTaskApproved activity.

    private void completeRequestTask_MethodInvoking(object sender, EventArgs e)
    {
        // Grab the task item so the workflow can complete it.
        SPListItem task = workflowProperties.Web.Lists["Workflow Tasks"] .GetItemById(RequestApproveTaskItemId);
    
        // This line is very important. It keeps you from receiving 
        // the error "This task is currently locked by a running workflow."
        task[SPBuiltInFieldId.WorkflowVersion] = 1;
        task["Status"] = "Completed";
        task.SystemUpdate(false);
    
        SPListItem currentItem = workflowProperties.Item;
        if (task["Business Approval"].ToString() == "I Do NOT Approve")
        {
            string currentTitle = workflowProperties.Item["Title"].ToString();
            workflowProperties.Item["Title"] = "DECLINED: " + currentTitle;
            workflowProperties.Item.Update();
        }
        else
        {
            string currentTitle = workflowProperties.Item["Title"].ToString();
            workflowProperties.Item["Title"] = "APPROVED: " + currentTitle;
            workflowProperties.Item.Update();
        }
    }
    

    Again, you are setting the workflow version to keep the task from being locked when the update is performed from the code. The workflow sets the built-in Status field to Completed, which ends the task. A quick evaluation is then performed on the state of the task (approved or declined), and the item is updated accordingly to reflect the decision.

The final step in this process is the configuration of the custom Business Approval field on the Workflow Task content type.

To perform the final site configuration for the custom task approval field

  1. Before you are able to edit the fields associated with the Workflow Task, it needs to be added to the Workflow Tasks list. On the Debug menu in Visual Studio 2010, click Start Debugging or press F5, as shown in Figure 17. This launches a new browser window and attaches the debugger to the w3wp process.

    Figure 17. Starting the workflow in debug mode

    Starting workflow in debug mode

  2. When the site is loaded, go to the requests list and create an item. This causes the Workflow Task content type to be automatically added to the Workflow Tasks list. To verify this, on the Site Actions menu, click View All Site Content. Select the Workflow Tasks list, and then click List Settings.

    On the List Settings page, you see the Workflow Task content type in the list of content types, as shown in Figure 18.

    Figure 18. Viewing the content types on the Workflow Tasks list

    Viewing content types on Workflow Tasks list

  3. Click the Task content type and use the Delete this content type option to remove it from the Workflow Tasks list. Repeat this procedure with the Summary Task content type.

  4. Now, click the Workflow Task content type to go to the settings page.

  5. Click the Status field, and then change its Column Settings from Optional to Hidden. Click OK to save the changes.

  6. On the settings page for the Workflow Task content type, under the Columns section, click Add from existing site or list columns.

  7. Locate the Business Approval column in the list and then double-click it to move it to the Columns to add list (see Figure 19). Click OK to save the changes.

    Figure 19. Adding custom columns to the Workflow Task content type

    Adding custom columns to Workflow Task type

Testing the Sequential Workflow

In this section, you test the workflow. You do this by creating a request and then updating the request to see the results.

To create a request

  1. In the Requests list, click the Add new item link in the lower-left corner of the list view. The New Item dialog box appears, as shown in Figure 20.

    Figure 20. New item page for the requests list

    New item screen for requests list

  2. Type a title for the request and click Save.

  3. The associated task is created by the workflow in the Workflow Tasks list. To view the task, in the Site Actions menu, click View All Site Content, and then select the Workflow Tasks list under the Lists heading. The task appears at the top of the main view, as pictured in Figure 21.

    Figure 21. List view of the newly created task

    List view of newly created task

  4. Click the title of the task to display the full task form. Click Edit in the top-left corner of the form to place the task in edit mode (see Figure 22).

    Figure 22. Workflow Task edit form with custom approval field

    Workflow Task edit form with custom approval field

    Clicking Save with the Undecided value still in the Business Approval field causes the built-in Status field to be set to In Progress and logs the following into the workflow history (see Figure 23).

    Figure 23. Viewing the Workflow History list

    Viewing Workflow History list

    Likewise, if you omit the Request Description field on the list item while trying to approve or decline the task, the Status field is set to In Progress.

  5. Now, go back to your Request item and edit it. You see that the Request Description field is now showing as required. Type a value of more than 100 characters (see Figure 24) and save your changes. You can then approve or decline the task.

    Figure 24. Request list item edit form

    Request list item edit form

  6. Now that the Request item is properly filled in, go back to the Workflow Task list, edit the task associated to the Request item, and then select I Do NOT Approve in the Business Approval field. This action causes the workflow to set the Status field to Completed, as seen in Figure 25.

    Figure 25. View of the completed (and rejected) task in the Workflow Task list

    View of completed task in Workflow Task list

    You can see the change in the title for the Request item in Figure 26

    Figure 26. Title change performed by the workflow on a rejected task

    Title change made by workflow on rejected task

    If the workflow task is approved, the title of the Request item is altered to reflect the approval action (see Figure 27)

    Figure 27. Title change performed by the workflow on an approved task

    Title change made by workflow on approved task

Conclusion

Often the needs of the business process with which you are working require a more flexible solution that better aligns to the operation than is provided by the built-in functionality. In these cases, you need to create your own custom approval and validation criteria for tasks in SharePoint sequential workflows. Expanding upon these techniques provides a powerful mechanism to enforce business rules and integrate efficiently with existing logical processes.

Additional Resources

For more information, see the following resources: