SharePoint Workflow Custom Input Forms

This article explains about custom input forms for the SharePoint workflow and show you how to implement, integrate and use those forms with the associated workflow template. The input forms are implemented as an application page. The article also tells you about some important aspects of the custom input forms for SharePoint Workflows.

Many of use have worked with SharePoint workflow in one or another to fulfill our requirements. Also, many of us have requirements of customizing the workflow input forms to meet our requirements. So, in this article we will explore the custom input forms for SharePoint workflow and learn how to develop, associate and use those forms.

Solution Overview

With our scenario of custom input forms for workflow, we will develop an application that associates the different input forms for different processes of the workflow. So, when the workflow is associated with any of the List or Document Library of SharePoint site, the workflow uses these custom developed forms instead of the default one. Each input for has different working process. When any user associates our workflow with document library or list, the association form comes up and asks to the user to provide the approver, to set the approval scope and to provide instruction to approver. When the user provides these values and clicks Ok, the workflow will be associated to the list or document library. The workflow can be configured to start automatically when the new item is created or to start it manually.  When a user creates an item and starts the workflow manually, the workflow initiation form comes up and show the approver set at the time of the association of the workflow. When the user clicks Ok button, the workflow starts on the item or file. Also creates the task for the approver and sends the notification to him and places the workflow in the InProcess state. When the approver opens his task, the custom task edit form comes up. There the approvers can approver, reject or cancel the operation. Also the approver can provide his comments for his action as well. The solution also provides the Modification form for modifying the properties for the associated workflow as well.

Solution Walkthrough

Download the solution project HelloWorkflow. Open the solution named HelloWorkflow in Visual Studio 2008. Remember that this project requires the WSPBuilder already installed on the development machine. As you can see from the below image, a file named HelloSharePointWorkflow.cs contains the definition for the WF program itself. Three XML files are used to define the feature named HelloSharePointWorkflow that installs the workflow template within a WSS farm. We will now focus on how the other souce files inside the HelloWorkflow project are used to integrate workflow input forms.

The HelloWorkflow project contains four different .aspx files to supply each of the four available types of input forms: an association form, an initiation form, a modification form and a task edit form. Remember that all these forms are developed as custom application pages which are deployed in the LAYOUTS directory. It is recommended that you don’t deploy your custom application pages directly inside the LAYOUTS directory. Instead, you should create a project specific or organization specific directory inside the LAYOUTS directory to deploy your custom application pages. The HelloWorkflow project is configured to deploy its four custom application pages within a custom HelloWorkflow directory inside the LAYOUTS directory.

Now open the elements.xml file and have a look at its content and specifically see the definition of Workflow element that defines the workflow template. You should see the attributes named AssociateUrl, InstantiationUrl, and ModifiecationUrl that contain references to three of the workflow input forms.

Description="Description for HelloSharePointWorkflow"
CodeBesideAssembly="HelloWorkflow, Version=, Culture=neutral, PublicKeyToken=d91c6ff34a688573"
        Modify workflow instance with a custom hello form.

There are attributes containing references to three of four of the workflow input forms, there is no reference to the task edit form because you do not integrate a task edit form in the same way as the other three types of workflow input forms. If you want a custom form instead of the standard WSS task edit form, you must create a custom content type that inherits from the standard workflow task content type. When you create a custom content type for a custom workflow task, you can define it to use its own custom edit form, which is the approach used in the HelloWorkflow project. The TaskListContentTypeID attribute of Workflow element in elements.xml is configured with the ID content type, which identifies the custom content type defined inside workflowTaskContentType.xml.

Walking through using the Hello Approval Workflow input forms

To test the workflow template and workflow custom input forms defined by the HelloWorkflow project, you must follow the steps. Download the HelloWorkflow source code. Open it in Visual Studio 2008. Remember that, as said above, you need WSPBuilder to successfully open this project in Visual Studio 2008. Build the project by right clicking the Project name in Solution Explorer. After successful build, again right click the project and click Build WSP from the context menu. This will build the .wsp file which will be installed on the SharePoint Server. Now to deploy the solution or .wsp file, again right click the project name and click Deploy. The both Build WSP and Deploy options are available as the sub items of the WSPBuilder menu item.

Once the HelloWorkflow project deployed successfully, you should then be able to activate the feature with the Hello Approval workflow template within any site collection within the farm. Next, create a new site collection or navigate to an existing site collection so that we can test workflow template. Once navigated to desired site’s top-level site of site collection, go to the Site Collection Features page from Site Settings page. You should see the feature titled "HelloSharePointWorkflow”. Activate this feature to make its workflow template available within the site collection. Now you are able to use the workflow template to create a new workflow association.

Custom Association Forms

It’s now time to create a new list or document library so that we can have a place to create a new workflow association from the Hello Approval workflow template. Once new list is created, go to the List Settings page and click the Workflow Settings link to page on which you can view the existing workflow associations. Click the Add A Workflow link to navigate to the standard application page named AddWrkfl.aspx, which allows you to create a new workflow association for the list or document library. If you have not any existing workflow association, then you will be directly redirected to the AddWrkfl.aspx page. You can see the HelloSharePointWorkflow as an available workflow template as shown in below image.

The AddWrkfl.aspx page allows the user to select a workflow template, provide a unique name and parameterize other standard aspects of the workflow association. Here user can parameterize the workflow association to use an existing task and history lists or can select to create a new task/history list. Continuing the walk-through, select the HelloSharePointWorkflow template, give the name Hello Approval Workflow, and then click next.

What happens when a user clicks the Next button on the AddWrkfl.aspx page, is the important question. The depending answer is that whether your workflow template is configured with an associated form. If your workflow is not associated with an association form, the Next button execution does all the required logic to create a new workflow association and create a new task list and history list if necessary. If your workflow is associated with an association form, clicking the Next button simply redirects the use to that association form. When you click Next to create the Hello Approval workflow for our example, you will be redirected to the custom association form named HelloAssociation.aspx, as shown in the below image.

An important point to be considered here is that when you configure a workflow template to use a custom association form, then you have to take of supplying the code to create new workflow association as well as to create a new task list or history list if that is what the user selected.

Think what is the purpose of custom association form? It’s used to prompt the user form extra data input whenever they create a new workflow association. In the case of our example, the association form is used to get the default values for the approver, the approval scope (internal or external) and instructions to the approver. When a user enters data into the association form and clicks Ok, your code must serialize the user input values into a string that is saved as association data. This association data then used by the initiation form each time a user starts a new workflow instance from the workflow association.

Implementing a Custom Association Form

We will now see some implementation details of the association form. Open the HelloAssociation.aspx and inspect what’s in. You should notice that this source file does not contain any code inside. Instead, it simply contains input controls, command buttons for Ok and Cancel, and layout details. The actual code for this form is writer into a class named HelloWorkflowAssociationForm.cs file which is compiled into the same assembly DLL as the WF program itself, i.e. HelloWorkflow.dll. The Page directive within HelloAssociation.aspx indicates that it is defined to inherit from the code-behind class named HelloWorkflowAssociationForm.

Open the HelloWorkflowAssociationForm.cs file and examine the code that defines the behavior of the aspx page. The HelloWorkflowAssociationForm class inherits from LayoutsPageBase class, which follows the best practice for developing custom application pages. Though this is a simple example about creating custom association form, it contains couple of hundred lines of code. The high-level structure of the class is as the following code:

public class HelloWorkflowAssociationForm : LayoutsPageBase {
  // control fields
  // form-level variables
  protected override void OnLoad(EventArgs e) {...}
  protected void PopulateFormDataFromString(string AssociationData){...}
  protected string SerializeFormDataToString(){...}
  protected void UpdateAssociation(SPWorkflowAssociation wfa,
                                   SPList TaskList, SPList HistoryList) {...}
  public void cmdCancel_OnClick(object sender, EventArgs e) {...}
  public void cmdSubmit_OnClick(object sender, EventArgs e) {...}
  protected override void OnPreRender(EventArgs e) {...}

The HelloAssociation.aspx page defines several input controls to collect input from user. For example, page has a WSS PeopleEditor control that provides a handy technique when you need a user to select another user, such as the approver. The following example shows what the control tag looks like.

When you have an .aspx file and a code-behind class, there is a handy technique in ASP.NET programming in which code-behind class defines a control field by using the same name as a control on the .aspx page. When the ASP.NET runtime compiles the .aspx file, it adds support to create the control instance and assign a reference to the control field in the code-behind class. For example, HelloWorkflowAssociationForm class define a control field named pickerApprover to match the control instance defined in HelloAssociation.aspx page.

public class HelloWorkflowAssociationForm : LayoutsPageBase {
  // define control field with name that matches .aspx file
  protected PeopleEditor pickerApprover;

The benefit of this technique is that all of the methods in the HelloWorkflowAssociationForm class have direct access to all the control instances defined on the .aspx page.

Now examine the form-level variables in the HelloWorkflowAssociationForm class and go through the code of OnLoad method. You can see that the parameters passed by WSS are read in this event handler and used them to initialize strings, GUIDs, and various objects from the WSS object model. In this code, the association of workflow is checked for whether it is for list or document library. If the new workflow association is being created on a list, conditional code in the OK button click event handler executes this code:

WorkflowAssociation = SPWorkflowAssociation.CreateListAssociation(WorkflowTemplate,

Also, when the workflow association is being created for content type, the code behind OK button must be conditionally programmed to call either CreateSiteContentTypeAssociation or CreateListContentTypeAssociation instead of CreateListAssociation. The code must also take care of deal with the scenario in which the user is updating an existing workflow association instead of creating new one.

The other important aspect of implementing an association form is how to handle user input values. The input values provided by a user must be serialized into a string and then saved to the AssociationData property of the SPWorkflowAssociation object. At the time of modification of existing workflow association these value are deserialized and used to populate the form input controls. See the way we used for this example project to serialize and deserialize the input data using the DataSet named WorkflowData. This dataset can be used with the XmlSerializer class to create strong-typed .NET objects that can be converted back and forth for data store.

The HelloWorkflowAssociationForm class provides two methods, SerializeFormDataToString and PopulateFormDataFromString. These methods are used to move input data back and forth between the form’s input controls and a serialized string to be saved in AssociationData property.

The final implementation details that needs attention is the logic in the association form that is responsible for creating a new task list and history list when required. So, who to know that a new task list or history list need to be created on our custom association form? The WSS places ‘z’ character at the beginning of the incoming list name to inform that it is the new of new list to be created. To identity this, in the OnLoad method, two Boolean variables named NewTaskListRequired and NewHistroyListRequired are initialized based on the input parameters and the Ok button executes the code based on this variable values.

if (NewTaskListRequires) {
                Guid taskListId = Web.Lists.Add(NewTaskListName, "Workflow Tasks", SPListTemplateType.Tasks);
                TaskList = Web.Lists[taskListId];
             if (NewHistoryListRequired)
                Guid historyListId = Web.Lists.Add(NewHistoryListName, "Workflow history", SPListTemplateType.WorkflowHistory);
                HistoryList = Web.Lists[historyListId];

As you can see this association forms require a good deal of code, but also, the code can be reused across association forms for many different workflow templates.

Custom Initiation Forms

It’s now time to see the implementation of a custom initiation form for workflow template. Let’s for our example, once the user creates the Legal Approval workflow association from the Hello Approval workflow template, the user should then be able to see the association from the standard application page named Workflow.aspx, as shown in below image. The user can navigate to this Workflow.aspx page by selecting the Workflow menu item from the ECB menu for an item or document.

From the Workflow.aspx page, by clicking the link to any available associated workflow to start a new instance. If the workflow template is not configured to use an initiation form, WSS automatically starts the workflow instance. If the workflow template is configured to use an initiation form, the user is redirected to it.

In our case, click the Hello Approval workflow link, which takes you to HelloInitiation.aspx page as shown in below image. You can see that the initiation form input controls are initially filled with the default values from the AssociationData property of the workflow association. This gives the user to keep them as it is or to change them.

The most important point here is that custom initiation forms can be used only with workflow associations that allow the user to manually start workflow instances through the Workflow.aspx page. You can’t present the user to initiation form in the case when you have configured a workflow to start automatically when item list item or document library item is created or updated. These types of scenarios do not provide a natural flow and therefore not supported by WSS.

Implementation of Custom Initiation Form

If you look in the HelloInitiation.aspx page, you observe the input control tags and page layout are almost identical to those in HelloAssociation.aspx page. This makes sense because the association form is used to obtain default values for all workflow instances, and the initiation form then gives the user the opportunity to see these default values and optionally change them when starting a particular workflow instance.
The HelloInitiation.aspx page also relies on a code-behind base class named HelloWorkflowInitiationForm. The high-level structure of the class is as the following code:

public class HelloWorkflowInitiationForm : LayoutsPageBase {
  // control fields
  // form-level variables
  protected override void OnLoad(EventArgs e) {...}
  protected void PopulateFormDataFromString(string AssociationData){...}
  protected string SerializeFormDataToString(){...}
  public void cmdCancel_OnClick(object sender, EventArgs e) {...}
  public void cmdSubmit_OnClick(object sender, EventArgs e) {...}
  protected override void OnPreRender(EventArgs e) {...}

The new SPWorkflowAssociation object is created in the OnLoad method by using the incoming parameters passed by WSS. Then this object is used in the PopulateFormDataFromString method to get the current workflow association data. When user changes the values of the input controls and clicks OK to start a new workflow instance, the SerializeFormDataToString method is called to serialize these data into a form-level variable InitiationData. After this, the actual call to start a workflow instance is done.

The interaction with SPWorkflowManager is required when we need to start a workflow instance. The SPWorkflowManager object is exposed through the WorkflowManager property on the SPSite object that represents the current site collection. This SPWorkflowManager object provides a method named StartWorkflow that dose exactly what its name says.


When the user clicks OK button on the initiation form, the call to StartWorkflow is made and that the WF program comes into action. At this point, the code in the event handler of the OnWorkflowActivated activity of HelloSharePointWorkflow.cs is able to retrieve the initiation data, deserialize them and store them in fields defined within the WF program.

private void onWorkflowActivated1_Invoked(object sender, ExternalDataEventArgs e)
            workflowId = workflowProperties.WorkflowId;
            userID = workflowProperties.OriginatorUser.ID;

             if (workflowProperties.Item.File != null)
                ItemName = workflowProperties.Item.File.Name;
                ItemName = workflowProperties.Item.Title;

            string initiationData = workflowProperties.InitiationData;
            XmlSerializer serializer = new XmlSerializer(typeof(WorkflowData));
            XmlTextReader reader = new XmlTextReader(new StringReader(initiationData));
            WorkflowData data = (WorkflowData)serializer.Deserialize(reader);

            HelloWorkflow.Resources.WorkflowData.DataTable1Row row = data.DataTable1.Rows[0] as HelloWorkflow.Resources.WorkflowData.DataTable1Row;
            Approver = row.Approver;
            ApprovalScope = row.ApprovalScop;
            ApproverInstructions = row.Instrucations;


Custom Modification Forms

Once a workflow instance is in progress, you can supply a modification form that allows users to modify its state. When modification form is integrated properly, the user is able to see a link on the WrkStat.aspx workflow status page as shown in below image.

When you design a workflow modification form, you can display whatever input controls make sense for the modifications a user might want to make to a workflow instance in progress. The modification form used for our example provides an opportunity to change the approver, approval scope and instructions while the workflow instance is still running and waiting for approval.

Implementing a Custom Modification Form

Let’s open the elements.xml file again and examine the Workflow element and see what is needed to configure a workflow with modification form. The ModificationUrl attribute in the Workflow element references a specific .aspx file, which in this case is HelloModification.aspx. Another important element is nested in the MetaData element that defines a specific workflow modification, as shown in the following XML snippet.

       <!-other attributes are omitted for clarity -->
        Modify workflow instance with a custom hello form.

A workflow template can have only one workflow modification form, it can have more than on modification. Each modification is identified through a unique GUID and is defined using an element nested in the MetaData element. The text provided in the Modification element is used as the caption for the link that the user will see on WrkStat.aspx page. Till this we have seen what needs to be set up in terms of declarative logic within the feature for the workflow template. Now, next discussion is little bit tricky because you must add two different activities to you WF program to make the workflow modification work properly.

Due to a bug in this version of WSS, any GUID used for a Modification ID must contain only lowercase letters. Things will not work properly if you use a GUID that contains uppercase letters.

First of all, you must add a method activity of type EnableWorkflowModification to you WF program. This activity is used to make the modification active at a specific point within the life cycle of each workflow instance. Secondly, you must add an event activity named OnWorkflowModified. This activity listens for the occurrence of any modifications by users and responds by firing an event handler that executes you provided logic.

You must need to decide at what point in time the modification become active in the life cycle of workflow instance. In our example, the modification is designed to become active after the approval task is created so that workflow instance initiators can access the modification form while waiting for the approvers to do their work.

To make modification active requires creating an activity from the EventHandlingScope activity type supplied by the WF base activity library. Then you need to add Sequence activity so that you can add several child activities inside the EventHandlingScope activity. Next, you need to add EnableWorkflowModification activity as the first activity that executes within the scope of EventHandlingScope activity as shown in below image.

The next step is to set up the listener by using the OnWorkflowModified event activity type. If you right click the EventHandlingScope activity in the workflow designer. It will show you different viewing options. You need to select the View Event Handler and switch over the view to configure the OnWorkflowModified activity. Now in the view of Event Hanlders for EventHandlingScope activity, add the EventDriven activity to the EventHandlingScope activity. This makes possible to add an event activity that acts as listener. In our example, only one EventDriven activity contains the OnWorkflowModified activity. The event handler for OnWorkflowModified activity fires when the user updates information in the modification form.

Each modification relies on a GUID and a correlation token. The GUID for modification is hard-coded into the workflow template inside the elements.xml file. When EnableWorkflowModification and an OnWorkflowModified activity are added to WF program to activate the modification, you must configure ModificationId property of both activities to this GUID. Also, create a new correlation token and assign it to both activities. It is also important to see that the OwnerActivityName for the correlation token named modificationToken is configured as the activity named eventHandlingScopeActivity and not the workflow program named HelloSharePointWorkflow.

When modification is created, you need to add code to the WF program to pass any initialization data to the modification form that it needs. In our example, the WF program must pass the same data that the user enters on the initiation form. These data exists only within the fields of the WF program, the event handler of the EnableWorkflowModification must serialize the form data again and assign them to the field named modificationContextData that is data bound to the ContextData property of EnableWorkflowModification activity.

The OnLoad method of HelloWorkflowModificationForm class supplies the logic to initialize the fields and retrieve content data passed by the WF program by accessing the ContextData property of the SPWorkflowModification object.

// Get Form Parameters
            string paramList = Request.Params["List"];
            string paramID = Request.Params["ID"];
            string paramWorkflowInstanceID = Request.Params["WorkflowInstanceID"];
            string paramModificationID = Request.Params["ModificationID"];

             // create WSS object
            _List = Web.Lists[new Guid(paramList)];
            _ListItem = _List.GetItemById(Convert.ToInt32(paramID));
            _WorkflowInstance = _ListItem.Workflows[new Guid(paramWorkflowInstanceID)];
            _ModificationID = new Guid(paramModificationID);
            _Modification = _WorkflowInstance.Modifications[_ModificationID];
            _ContextData = _Modification.ContextData;

            _WorkflowAssociation = _List.WorkflowAssociations[_WorkflowInstance.AssociationId];

Input controls in the modification form as initialized in the same way that used in thee association and initiation forms. As the final step, when the OK button is clicked, call to ModifyWorkflow method on the WorkflowManager is done to pass the updated context data back to the WF program in a serialized form.

ContextData = SerializeFormDataToString();

At this stage, the control is returned to WF program. The event handler for the OnWorkflowModified activity fires and allows your code to do whatever it needs to do with the updated context data. The ContextData property must be data-bound on the OnWorkflowModified activity to a field within the WF program to obtain access to the data that are passed back from the modification form. Once this is done, the event handler can write the updated data from the context data into the fields of the WF program that are used to persist the data for the current running workflow instance.

Custom Task Edit Forms

We have now reached the final input form: the task edit form. You should develop and design a custom task edit form when you want to take over the user experience when it’s time for a user to complete a task. In our example, a custom task edit form named HelloTaskEdit.aspx is developed for the users to approve or reject an approval request.

The benefit of using a custom task edit form is that you can add extra command buttons, direct the user’s attention to the data and input values that matter to complete the task at hand.

Implementing a Custom Task Edit Form

To Implement custom task edit for requires to create a custom content type that inherits from the workflow task content type that is part of WSS. The custom content type created for Hello Approval Workflow template is defined in side workflowTaskContentType.xml as follows:

<Elements xmlns="">
Name="Jatin's workflow task"
Group="Jatin's Content Types"
Description="Create a workflow task"
      <FieldRef ID="{1CA81E60-3104-4f32-A2AF-9AF74DAA80C1}" Name="Notes" DisplayName="Instructions" />
      <FieldRef ID="{7615D4C9-4539-440e-A56B-A6D5B7AC8324}" Name="Comments" DisplayName="Comments" />

      <XmlDocument NamespaceURI="">
        <FormUrls xmlns="">

0x010801 is the content type ID of the standard workflow task content type. There for any content type that inherits this must use a vale of 0x010801 as the first part of its content type ID. You can also see that we’ve added to extra fields named Notes and Comments to this custom content type.
The XmlDocument section at the bottom of the content definition give the view of how we can integrate the custom task edit for into a workflow template. When a user selects to edit an approval task, WSS takes a user to the HelloTaskEdit.aspx page instead of the standard workflow task edit form.
The instructions that were written into the task when it was created, are retrived in the OnLoad method of the HelloWorkflowTaskEditForm class. The static method AlertTask of the SPWorkflowTask class is called in the Approve and Reject button clicks. This method takes a HashTable of named proeprties as parameter and causes the event handler for the OnTaskModified activity within the WF program to fire.

Hashtable taskHash = new Hashtable();
             taskHash["Status"] = "Completed";
            taskHash["Notes"] = "Approved";
            taskHash["Comments"] = txtComments.Text.Trim();
            SPWorkflowTask.AlterTask(TaskItem, taskHash, true);

Confusion what to choose: .aspx Forms or InfoPath Forms

We walked through the implementation details required to integrate the all four different types of custom workflow input forms into a workflow template using .aspx files. But as you see, this is quite hard to integrate custom workflow input forms also to manage the code for those .aspx pages.

If you are developing workflow templates that run exclusively in a MOSS environment, you have the much easier option of building you custom workflow input forms by using the forms designer of Microsoft InfoPath 2007. The InfoPath forms designer provides you with a great more pleasant experience for creating the user interface for a workflow template also, it relieves you from writing any code behind .aspx pages. MOSS provides standard .aspx pages and code for you. The only code need to be done for this is the serialization of data that goes into WF program and passes back and forth with the InfoPath forms.

While using InfoPath forms for workflow, that workflow templates have a dependency on MOSS as WSS does not support this InfoPath forms. Creating the workflow templates that integrate workflow input forms using .aspx is more flexible because these templates can be deployed within any WSS farm whether MOSS is installed or not.


Hope that you have enjoyed and learned a lot about the custom input forms for workflow template and how to integrate them with the workflow template and how to use.

You can download the source code from here.

By Jatin Prajapati   Popularity  (14267 Views)