Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / SharePoint

SharePoint 2010 State Machine Workflows with Custom Task Forms (InfoPath) using Visual Studio 2010: Part 2 of 3

4.92/5 (13 votes)
3 Dec 2012CPOL9 min read 205.4K   2.5K  
A tutorial on SharePoint 2010 Custom State Machine Workflows with custom task forms in InfoPath 2010. An example based tutorial where I simulate the recruitment process of an organization.

Part 1: Building Custom SharePoint 2010 Workflow

Part 2: Configuring and Deploying Custom InfoPath Task Form 

Part 3: Sending Data from SharePoint Workflow to InfoPath Form 

Introduction 

As mentioned in part 1 of the tutorial, we can use custom task forms instead of the default task forms provided by SharePoint. Default forms have fixed number of fields which cannot be altered. Using custom forms, we can gather the required information from the user and they also make the process more dynamic and flexible.

In this part of the tutorial, we will see how to use InfoPath 2010 to design custom workflow task forms. InfoPath provides a richer interaction experience in which the user can interact with the workflow.

To handle the custom task forms in InfoPath, SharePoint 2010 links the hyperlink that displays a workflow form to an ASPX page that contains an Office Forms Services Web Part. The logic to handle the forms is written in this Web Part. This Web Part is configured such that it can receive as well as submit data. So, if we want to show some information on the InfoPath form, from a SharePoint List (or library) we create a Receive data connection in the InfoPath form. Similarly, to copy data from the InfoPath form to the SharePoint List (or library), we create a Submit data connection.

We specify the custom forms we want to use in the workflow template definition (i.e., Elements.xml) rather than the workflow itself. This involves setting two elements: the form URL and the URN of the custom InfoPath 2010 form. The form URL needs to be associated with a proper workflow process, e.g., association, initiation, modification, etc. This linking is important so that the appropriate ASPX hosting page is included in SharePoint. Next, you add an element specifying the URN for the custom InfoPath 2010 form for that type of workflow process.

Before we proceed any further, we will add a few more columns to the SharePoint List. The updated list has the following columns:

  1. Title
  2. Technology
  3. TotalExperience
  4. CandidateStatus
  5. InitialRemarks
  6. TechnicalInterviewRemarks
  7. HRRemarks
  8. InitialClearanceBy
  9. InitialClearanceOn
  10. TechnicalClearanceOn
  11. HRInterviewBy
  12. HRClearanceOn
  13. TechnicalInterviewAssignedTo

Design

Let us start by designing an InfoPath form for our first task, i.e., Initial Clearance.

Open Microsoft InfoPath Designer 2010; double-click on a blank form in the new tab. This opens a window with a blank form. For the Initial Clearance Task, we will give the user an option to approve or reject a candidate. If the user chooses to approve the candidate, then he/she will have to assign a Technical Interview Panel and an HR Interview Panel, else the user enters the comments for rejection and the workflow is terminated.

Design the table structure you want to use for your task form. You can use predefined structures available in Page Layouts of the Page Design tab. Click on the Show Fields button in the Data tab of the InfoPath Designer.

Drag-drop the controls on the form as required. At the bottom of the page, we will have a button control which will fire the Submit rule in InfoPath.

To add values to the Status dropdown list, right click on the Status dropdown list and select Drop-Down List Box Properties. Using Properties, you can add or modify the values as required. Rename the fields appropriately as we will be using these names in our code-behind file in Visual Studio.

We also have a Contact Selector on our InfoPath form. To configure the Contact Selector to use the user available in SharePoint, right click the control and go to Properties. In the SharePoint Server tab, enter the URL of the SharePoint site. This way the control will be able to pick up the users from SharePoint.

Image 1

Fig 1.1

We will have to close the window after the user clicks on the Complete Task button. So, in Rules Explorer, click the Add button once again and select the “Close the form” option. You can preview the form and make changes accordingly. Refer to Fig. 1.1 for the completed InfoPath Form.

Implementation

Step 1

In Visual Studio 2010, open the Solution we created in the previous part. Right click on the workflow (RecruitmentWorkflow) folder and click Add -> New Item. Select module from the SharePoint 2010 node. We will name it Forms. Now right click the newly created module and add an existing item. Browse the InfoPath form we created and add that to the Forms module. If you observe the Elements.xml file inside the Forms module, Visual Studio has added a new node under the module corresponding to the newly added InfoPath form.

Image 2

Fig 1.2

In the main feature manifest file (Feature1.Template.xml), refer Fig. 1.2, add the following nodes inside the property node, if they are missing.

XML
<Property Key="GloballyAvailable" Value="true"/>
<Property Key="RegisterForms" Value="Forms\*.xsn"/>

Step 2

We need to configure the Elements.xml file of the workflow, add details like URN of the InfoPath Form, Association Data, etc. Before doing that, we need to change the InfoPath Form and publish our InfoPath Form.

To configure the settings, in File menu, select the Info tab. In the “Form Information” panel, select “Advanced form options”. In the “Security and Trust” tab, uncheck the “Automatically determine security level” checkbox and select the “Domain” radio button. Click OK and close the form.

To publish the InfoPath Form, in the File menu, in the Publish tab, click on the “Network Location” button. Browse the path on the file system, and click the Next button. In the next window of the wizard, clear the textbox (very important as SharePoint does not recognize the form as published if this textbox is not empty, hence it will not render the form). This will pop-up an alert message; click OK and continue. Complete the publishing process by clicking the “Publish” button.

To find the URN of the InfoPath Form, go to the File menu of the published InfoPath Form (make sure it is the published InfoPath Form), on the right hand side panel, click the “Form Template Properties” button. It opens up a pop-up window, the ID field in this window is the URN of the InfoPath Form.

In the Elements.xml file, we need to set TaskListContentTypeId to the following:

TaskListContentTypeId="0x01080100C9C9515DE4E24001905074F980F93160"

This content type, included in Microsoft SharePoint Server 2010, specifies the custom task display and edits the forms that include an Office Forms Server control for rendering the InfoPath Forms.

Next, add an element to the Metadata element of the workflow template definition. This element is in the form of <TaskN_FormURN>URN Value</TaskN_FormURN>, where N represents the integer value we assign to that task type within the workflow, and URN Value is the URN string we get from the InfoPath Form.

In addition to this, we also need to add the Association Data to our elements file. We will pass the dummy data to the workflow as we do not have any data to be passed to the workflow during association. Add the below code after the Categories node in Elements.xml. Refer Fig. 1.3 for Elements.xml after configuration.

XML
<AssociationData>
              <Data></Data>
</AssociationData>

Image 3

Fig 1.3

Step 3

If you are well versed with InfoPath 2010, you can create views in the same form for the various tasks that we have in our workflow. Alternatively, you can create a new form for each task and add it to the solution in a similar manner. Adding new forms will only change the MetaData element of the Elements.xml file (additional URNs will have to be added corresponding to the new InfoPath Forms).

We have created different forms for the tasks in our example. The next task (if Initial Clearance is approved) will be the Technical Clearance Task. Now, we want to show the comments of Initial Clearance in the Technical Clearance task, so that the Technical Panel has additional information about the candidate.

To achieve this, we must be able to send information from a Workflow to the InfoPath Form. This can be done using the Data connection in the InfoPath. We need to create an XML file named “ItemMetadata.xml” which will contain the names of the fields we want to be available in InfoPath. Then we create a new Data Connection in InfoPath for receiving the data. The name of columns in ItemMetaData.xml should start with “ows_”. You pass multiple values to the InfoPath form using a single ItemMetaData.xml file. Refer Fig. 1.4.

Image 4

Fig 1.4

To add this XML file to the InfoPath Form, in InfoPath Designer, select the Data tab and add a new data connection by clicking the Data Connection button. In the pop-up window, click the Add button, select Receive Data in the next window, and click the Next button. Select the XML Document option, in the next window, browse the XML file, click Next, and finish the configuration with the default values selected.

In the Fields Explorer window, the drop-down shows two connections now. You can choose to drag and drop fields from the newly configured data connection onto the InfoPath form. Refer Fig. 1.5.

Image 5

Fig 1.5

Step 4

In Visual Studio, the MethodInvoking event of the Create Task activity in the Initial Clearance State will be called when the task is created. We specify the details of the task in this event. For the task to be associated with a specific InfoPath Form, we need to configure the TaskType property of the TaskProperties field of this task. For our example, we will have the following code. Here, we correspond the TaskType to the number we have associated with the URN while entering it in Elements.xml.

C#
//Task Type corresponds to TaskURN specified in Elements.xml
this.createTaskInitialClearanceTaskProperties.TaskType = 1;

Make sure that the URN specified in Elements.xml maps to the same task in the code-behind file; else the workflow will encounter an error while opening the task form.

To send data from SharePoint to the InfoPath form, we have setup a receive data connection in InfoPath Forms. We can achieve this by setting up the ExtendedProperties of the Task property field in the same event, i.e., the MethodInvoking event. ExtendedProperties is a hashtable (Key-Value pair) and the key in our case will be the name of field we have used in ItemMetaData.xml and the value will be the value of the remarks of the previous stage.

The Invoked event of the EventDriven activity will be called after the user has performed the task. Once the task has been completed, we need to access the values entered by the user on the InfoPath Task forms. We can access these values by using the ExtendedProperties property of the AfterProperties field of this task. Below are the code snippets for all the events and methods associated with the Technical Clearance task.

Here is the MethodInvoking Event:

C#
private void createTaskTechnicalClearance_MethodInvoking(object sender, EventArgs e)
{
    try
    {
        //Create a new TaskId for the Task
        this.createTaskTechnicalClearanceTaskId = Guid.NewGuid();

        //TaskProperties field is used to configure the Task Details.
        this.createTaskTechnicalClearanceTaskProperties.Title = "Technical Clearance";

        //You can assign a Task to an user or to a group.
        //Here we assign the task to HR-Group

        SPListItem listItem = this.workflowProperties.Item;
        //Write the remarks value to the list item
        if (listItem != null)
        {
            //Check if TechnicalInterviewAssignedTo Field has
            //a value, and Assign this Task to the value of this Field
            if (listItem.Fields.ContainsField("TechnicalInterviewAssignedTo"))
            {
                this.createTaskTechnicalClearanceTaskProperties.AssignedTo = 
                   listItem["TechnicalInterviewAssignedTo"].ToString();
            }

            //To send InitialRemarks to the InfoPath Form,
            //we add it to the ExtendedProperty of the TaskProperties
            //ExtendedProperties is a hashtable (Key-Value pair). 
            //The Key will be the name of field we have used
            //in ItemMetaData.xml and Value will be the value of remarks
            if (listItem.Fields.ContainsField("InitialRemarks"))
            {
                if (this.createTaskTechnicalClearanceTaskProperties.
                     ExtendedProperties.ContainsKey("InitialComments"))
                {
                    this.createTaskTechnicalClearanceTaskProperties.
                      ExtendedProperties["InitialComments"] = 
                      listItem["InitialRemarks"].ToString();
                }
                else
                {
                    this.createTaskTechnicalClearanceTaskProperties.
                      ExtendedProperties.Add("InitialComments", 
                      listItem["InitialRemarks"].ToString());
                }
            }
        }

        //Task Type corresponds to TaskURN specified in Elements.xml
        this.createTaskTechnicalClearanceTaskProperties.TaskType = 2;

        this.createTaskTechnicalClearanceTaskProperties.DueDate = 
                       DateTime.Today.AddDays(2.0);
    }
    catch (Exception ex)
    {
        //throw ex;
    }
}

Invoked Event:

C#
private void onTaskChangedTechnicalClearance_Invoked(
        object sender, ExternalDataEventArgs e)
{
    try
    {
        this.onTaskChangedTechnicalClearanceAfterProperties = 
           this.onTaskChangedTechnicalClearance.AfterProperties;
        this.onTaskChangedTechnicalClearanceBeforeProperties = 
           this.onTaskChangedTechnicalClearance.BeforeProperties;

        //Set the PercentComplete property to 1.0 (i.e. 100%)
        //to indicate that the task has been completed.
        this.onTaskChangedTechnicalClearanceAfterProperties.PercentComplete = (float)1.0;

        //Get the value of Remarks Column (InfoPath Form)
        //by using the ExtendedProperties property
        string remarks = this.onTaskChangedTechnicalClearance
                 BeforeProperties.ExtendedProperties["Remarks"].ToString();

        SPListItem listItem = this.workflowProperties.Item;

        //Write the remarks value to the list item
        if (listItem != null && 
             listItem.Fields.ContainsField("TechnicalInterviewRemarks"))
        {
            listItem["TechnicalInterviewRemarks"] = remarks;
        }
    }
    catch (Exception ex)
    {
        //throw ex;
    }
}

Condition handling - TechnicalClearanceApprovalProcess:

C#
private void TechnicalClearanceApprovalProcess(object sender, ConditionalEventArgs e)
{
    try
    {
        if (this.onTaskChangedTechnicalClearanceAfterProperties.PercentComplete == 
           (float)1.0 && this.onTaskChangedTechnicalClearanceAfterProperties.
                         ExtendedProperties["Status"].ToString().Contains("Approved"))
        {
            e.Result = true;
        }
        else
        {
            e.Result = false;
        }
    }
    catch (Exception ex)
    {
        //throw ex;
    }
}

That's all!

History

  • 19 Jan 2012: Updated source code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)