Creating Workflow Association Pages for Windows SharePoint Services 3.0

Summary:  With a workflow association page, users can provide values for workflow settings in Windows SharePoint Services 3.0. Learn how to use an ASPX page to create an association between a custom workflow template and a container or a content type.

Applies to:  Windows SharePoint Services 3.0

Patrick Tisseghem, U2U

February 2008

A custom workflow template for Windows SharePoint Services 3.0 can be accompanied by various pages that enable administrators and users to provide values for the workflow settings. A workflow association page is one of those pages. It is displayed whenever administrators create an association between the workflow template and a container (list or document library) or a content type. This Visual How To explains and demonstrates the steps for creating this interaction by using an ASPX page.

To learn how create an ASPX page that you can use as a workflow association page, watch the video and study the following steps and code.

Code-behind an ASPX Workflow Association Page

A custom ASPX association page for a workflow is an application page that is deployed in the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS folder. The code-behind class inherits from the Microsoft.SharePoint.WebControls.LayoutsPageBase class. You can embed all the code directly in the ASPX page, but a better approach is to encapsulate the code in a separate class. You can then associate the ASPX with the code-behind class by using the Assembly directive and the Page directive with the Inherits attribute pointing to the code-behind class within the .NET assembly.

User Interface Controls within the ASPX Workflow Association Page

Controls to be displayed to the administrators in the browser must be dropped inside the content placeholder that has the ID PlaceHolderMain. You can use various controls; this sample works with a simple page that contains two sections: InputFormSection and ButtonSection.

Working with the UI Controls in the Code-Behind Class

Handling the events and working with the UI controls in your code-behind class requires you to declare a variable for each control by using the same name and type.

Preparing the Association

Administrators use the Add a Workflow page (AddWrkfl.aspx) to select workflow templates they want to associate. In the page, they also enter various parameters settings for the workflow (such as the decision to reuse an existing task list or have one created, the choice for a history list, and the start mode for the workflow instances). All these settings need to be processed within your custom association page.

protected override void OnLoad(EventArgs e)   {
  string paramWorkflowName = Request.Params["WorkflowName"];
  string paramWorkflowDefinition = 
       Request.Params["WorkflowDefinition"];
  string paramList = Request.Params["List"];
  string paramCType = Request.Params["ctype"];
  string paramUpdateLists = Request.Params["UpdateLists"];
  string paramTaskList = Request.Params["TaskList"];
  string paramHistoryList = Request.Params["HistoryList"];
  string paramAllowManual = Request.Params["AllowManual"]; 
  string paramAutoStartCreate = Request.Params["AutoStartCreate"]; 
  string paramAutoStartChange = Request.Params["AutoStartChange"];
  string paramManualPermManageListRequired = 
       Request.Params["ManualPermManageListRequired"];
  string paramGuidAssoc = Request.Params["GuidAssoc"];
  WorkflowAssociationName = paramWorkflowName;
  WorkflowTemplateId = new Guid(paramWorkflowDefinition);
  WorkflowTemplate = Web.WorkflowTemplates[WorkflowTemplateId];
  if (!string.IsNullOrEmpty(paramGuidAssoc)) {
     WorkflowAssociationExists = true;
     WorkflowAssociationId = new Guid(paramGuidAssoc);
   }
   UpdateContentTypesOnLists = (paramUpdateLists == "TRUE");
   if (!string.IsNullOrEmpty(paramList))  {
     ListId = new Guid(paramList);
     List = Web.Lists[ListId];
   }
   if (string.IsNullOrEmpty(paramCType))  {
     AssociationType = AssociationTypeEnum.List;
     if (WorkflowAssociationExists)  {
     WorkflowAssociation = 
         List.WorkflowAssociations[WorkflowAssociationId];
     }
   }
   else  {
     if (List == null)  {
        AssociationType = AssociationTypeEnum.ContentType; 
        CTypeId = new SPContentTypeId(paramCType);
        CType = Web.AvailableContentTypes[CTypeId];
        if (WorkflowAssociationExists)  {
           WorkflowAssociation = 
                CType.WorkflowAssociations[WorkflowAssociationId];
        }
      }
      else   {
         AssociationType = AssociationTypeEnum.ContentTypeOnList;
         CType = List.ContentTypes[new SPContentTypeId(paramCType)];
         if (WorkflowAssociationExists)  {
            WorkflowAssociation = 
                 CType.WorkflowAssociations[WorkflowAssociationId];
         }
      }
   }
   if (paramTaskList[0] == 'z')  {
      NewTaskListRequired = true;
      NewTaskListName = paramTaskList.Substring(1);
   }
   else  {
      if (AssociationType == AssociationTypeEnum.ContentType) {
         TaskList = Web.Lists[paramTaskList];
      }
      else  {
          TaskList = Web.Lists[new Guid(paramTaskList)];
      }
    }
    if (paramHistoryList[0] == 'z')  {
       NewHistoryListRequired = true;
       NewHistoryListName = paramHistoryList.Substring(1);
    }
    else  {
       if (AssociationType == AssociationTypeEnum.ContentType) {
          HistoryList = Web.Lists[paramHistoryList];
       }
       else  {
          HistoryList = Web.Lists[new Guid(paramHistoryList)];
       }
    }
    if (WorkflowAssociationExists)  {
       PageTitle = "Update Workflow Association";
       PageTitleInArea = "Update Workflow Association '" + 
            WorkflowAssociationName + "'";
       PageDescription = 
          "Click OK to update the workflow associated named " + 
          WorkflowAssociationName;
    }
    else  {
       PageTitle = "Create Workflow Association"; 
       PageTitleInArea = "Create Workflow Association '" + 
           WorkflowAssociationName + "'";
       PageDescription = 
           "Click OK to create a new workflow association named " + 
           WorkflowAssociationName;
    }
}
Protected Overrides Sub OnLoad(ByVal e As EventArgs)
  Dim paramWorkflowName As String = Request.Params("WorkflowName")
  Dim paramWorkflowDefinition As String = _
     Request.Params("WorkflowDefinition")
  Dim paramList As String = Request.Params("List")
  Dim paramCType As String = Request.Params("ctype")
  Dim paramUpdateLists As String = Request.Params ("UpdateLists")
  Dim paramTaskList As String = Request.Params ("TaskList")
  Dim paramHistoryList As String = Request.Params("HistoryList")
   Dim paramAllowManual As String = Request.Params("AllowManual")
   Dim paramAutoStartCreate As String = _
         Request.Params("AutoStartCreate")
   Dim paramAutoStartChange As String = _
         Request.Params("AutoStartChange")
   Dim paramManualPermManageListRequired As String = _
         Request.Params("ManualPermManageListRequired")
   Dim paramGuidAssoc As String = Request.Params("GuidAssoc")
   WorkflowAssociationName = paramWorkflowName
   WorkflowTemplateId = New Guid(paramWorkflowDefinition)
   WorkflowTemplate = Web.WorkflowTemplates.(WorkflowTemplateId)
   If Not String.IsNullOrEmpty(paramGuidAssoc) Then
        WorkflowAssociationExists = True
        WorkflowAssociationId = New Guid(paramGuidAssoc)
   End If
   UpdateContentTypesOnLists = (paramUpdateLists Is "TRUE")
   If Not String.IsNullOrEmpty(paramList) Then
        ListId = New Guid(paramList)
        List = Web.Lists(ListId)
   End If
   If String.IsNullOrEmpty(paramCType) Then
        AssociationType = AssociationTypeEnum.List
        If WorkflowAssociationExists Then
            WorkflowAssociation = _
              List.WorkflowAssociations.(WorkflowAssociationId)
        End If
    ElseIf (List Is Nothing) Then
        AssociationType = AssociationTypeEnum.ContentType
        CTypeId = New SPContentTypeId(paramCType)
        CType = Web.AvailableContentTypes.(CTypeId)
        If WorkflowAssociationExists Then
            WorkflowAssociation = CType.WorkflowAssociations _
               (WorkflowAssociationId)
        End If
    Else
        AssociationType = AssociationTypeEnum.ContentTypeOnList
        CType = List.ContentTypes(New SPContentTypeId(paramCType))
        If WorkflowAssociationExists Then
            WorkflowAssociation = _
               CType.WorkflowAssociations(WorkflowAssociationId)
        End If
    End If
    If (paramTaskList.Chars(0) = "z") Then
        NewTaskListRequired = True
        NewTaskListName = paramTaskList.Substring(1)
    ElseIf (AssociationType = AssociationTypeEnum.ContentType) Then
        TaskList = Web.Lists(paramTaskList)
    Else
        TaskList = Web.Lists(New Guid(paramTaskList))
    End If
    If (paramHistoryList.Chars(0) = "z") Then
        NewHistoryListRequired = True
        NewHistoryListName = paramHistoryList.Substring(1)
    ElseIf (AssociationType = AssociationTypeEnum.ContentType) Then
        HistoryList = Web.Lists(paramHistoryList)
    Else
        HistoryList = Web.Lists(New Guid(paramHistoryList))
    End If
    If WorkflowAssociationExists Then
        PageTitle = "Update Workflow Association"
        PageTitleInArea = ("Update Workflow Association '" & _
           WorkflowAssociationName & "'")
        PageDescription = _
          ("Click OK to update the workflow associated named " & _
          WorkflowAssociationName)
    Else
        PageTitle = "Create Workflow Association"
        PageTitleInArea = ("Create Workflow Association '" & _
          WorkflowAssociationName & "'")
        PageDescription = _
           ("Click OK to create a new workflow association named " & _
           WorkflowAssociationName)
    End If
End Sub

Preparing the Association Data for Processing

Data that is entered in the association form must be stored as a string. The string must be passed to the workflow engine so that the engine can make the data available for further processing within your workflow code. A best practice is to serialize and deserialize the data by using a generated .NET class that is based on an XSD schema defining the data. The following two methods of your code-behind class handle the reading and writing:

Handling the Submit

The final piece of code to discuss is the event handler that submits the form data. It must create or connect to the task list and history list and create an SPWorkflowAssociation instance, passing the appropriate parameter values. The instance must then be added to the SPWorkflowAssociationCollection object of either SPList or SPContentType.

public void cmdSubmit_OnClick(object sender, EventArgs e)  {
   string RedirectUrl = "";
   if (NewTaskListRequired) {
     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];
   }
   switch (AssociationType)
     case AssociationTypeEnum.List:
        if (WorkflowAssociationExists)  {
         UpdateAssociation(WorkflowAssociation, TaskList, HistoryList); 
         List.UpdateWorkflowAssociation(WorkflowAssociation);
        }
        else  {
         WorkflowAssociation =
           SPWorkflowAssociation.CreateListAssociation
             (WorkflowTemplate, WorkflowAssociationName, TaskList,
              HistoryList);
           UpdateAssociation(WorkflowAssociation, TaskList, 
              HistoryList);
           List.AddWorkflowAssociation(WorkflowAssociation);
        }
        RedirectUrl = "WrkSetng.aspx?List=" + ListId.ToString();
        break;
      case AssociationTypeEnum.ContentType:
        if (WorkflowAssociationExists)  {
          WorkflowAssociation = 
              CType.WorkflowAssociations[WorkflowAssociationId];
              UpdateAssociation(WorkflowAssociation, 
                                TaskList, HistoryList);
              CType.UpdateWorkflowAssociation(WorkflowAssociation);
              if (UpdateContentTypesOnLists) {
                CType.UpdateWorkflowAssociationsOnChildren
                      (true, true, true);
              }
         }
         else  {
           WorkflowAssociation =     
               SPWorkflowAssociation.CreateSiteContentTypeAssociation
               (WorkflowTemplate, WorkflowAssociationName,  
                TaskList.Title, HistoryList.Title);
           UpdateAssociation(WorkflowAssociation, TaskList, 
                             HistoryList);
           CType.AddWorkflowAssociation(WorkflowAssociation); 
           if (UpdateContentTypesOnLists)  {
            CType.UpdateWorkflowAssociationsOnChildren
               (true, true, true);
           }
         }
         RedirectUrl = "WrkSetng.aspx?ctype=" + CTypeId.ToString();
         break;
       case AssociationTypeEnum.ContentTypeOnList:
         if (WorkflowAssociationExists) {
           WorkflowAssociation = 
               CType.WorkflowAssociations[WorkflowAssociationId]; 
           UpdateAssociation(WorkflowAssociation, TaskList, 
                             HistoryList);
           CType.UpdateWorkflowAssociation(WorkflowAssociation);
         }
         else  {
           WorkflowAssociation = 
              SPWorkflowAssociation.CreateListContentTypeAssociation
              (WorkflowTemplate, WorkflowAssociationName, TaskList, 
               HistoryList);
           UpdateAssociation(WorkflowAssociation, TaskList, 
                             HistoryList); 
           CType.AddWorkflowAssociation(WorkflowAssociation);
         }
         RedirectUrl = "WrkSetng.aspx?List=" + 
             ListId.ToString() + "&ctype=" + CTypeId.ToString();    
         break;
     }
     SPUtility.Redirect(RedirectUrl, 
       SPRedirectFlags.RelativeToLayoutsPage, HttpContext.Current);
 }
Public Sub cmdSubmit_OnClick _
   (ByVal sender As Object, ByVal e As EventArgs)
    Dim RedirectUrl As String = ""
    If NewTaskListRequired Then
        Dim TaskListId As Guid = Web.Lists.Add _
         (NewTaskListName, "Workflow Tasks", _
          SPListTemplateType.Tasks)
        TaskList = Web.Lists(TaskListId)
    End If
    If NewHistoryListRequired Then
        Dim HistoryListId As Guid = Web.Lists.Add _
         (NewHistoryListName, "Workflow History", _
          SPListTemplateType.WorkflowHistory)
        HistoryList = Web.Lists(HistoryListId)
    End If
    Select Case Me.AssociationType
        Case AssociationTypeEnum.List
            If Not WorkflowAssociationExists Then
                WorkflowAssociation = _   
                   SPWorkflowAssociation.CreateListAssociation _
                       (WorkflowTemplate, WorkflowAssociationName, _
                        TaskList, HistoryList)
                UpdateAssociation _
                      (WorkflowAssociation,TaskList, HistoryList)
                List.AddWorkflowAssociation(WorkflowAssociation)
                Exit Select
            End If
            UpdateAssociation _
                      (WorkflowAssociation, TaskList, HistoryList)
            Me.List.UpdateWorkflowAssociation(WorkflowAssociation)
            Exit Select
        Case AssociationTypeEnum.ContentType
            If Not WorkflowAssociationExists Then
              WorkflowAssociation =     
               SPWorkflowAssociation.CreateSiteContentTypeAssociation _
                 (WorkflowTemplate, WorkflowAssociationName, _
                  TaskList.Title, HistoryList.Title)
                UpdateAssociation _
                  (WorkflowAssociation, TaskList, HistoryList)
                CType.AddWorkflowAssociation(WorkflowAssociation)
                If UpdateContentTypesOnLists Then
                   CType.UpdateWorkflowAssociationsOnChildren _
                        (True, True, True)
                End If
            Else
                WorkflowAssociation = _
                   CType.WorkflowAssociations(WorkflowAssociationId)
                UpdateAssociation(WorkflowAssociation, TaskList, _
                                  HistoryList)
                CType.UpdateWorkflowAssociation(WorkflowAssociation)
                If UpdateContentTypesOnLists Then
                    CType.UpdateWorkflowAssociationsOnChildren _
                        (True, True, True)
                End If
            End If
            RedirectUrl = ("WrkSetng.aspx?ctype=" & CTypeId.ToString)
            Exit Select        
        Case AssociationTypeEnum.ContentTypeOnList
            If Not WorkflowAssociationExists Then
              WorkflowAssociation =
               SPWorkflowAssociation.CreateListContentTypeAssociation _
                      (WorkflowTemplate, WorkflowAssociationName, _
                       TaskList, HistoryList)
                UpdateAssociation _
                   (WorkflowAssociation, TaskList, HistoryList)
                CType.AddWorkflowAssociation(WorkflowAssociation)
            Else
                WorkflowAssociation = _
                   CType.WorkflowAssociations(WorkflowAssociationId)
                UpdateAssociation _
                  (WorkflowAssociation, TaskList, HistoryList)
                CType.UpdateWorkflowAssociation(WorkflowAssociation)
            End If
            RedirectUrl = ("WrkSetng.aspx?List=" & ListId.ToString & _
                           "&ctype=" & CTypeId.ToString)
            Exit Select
        Case Else
               
    End Select
    SPUtility.Redirect(RedirectUrl, _
             SPRedirectFlags.RelativeToLayoutsPage,HttpContext.Current)
End Sub

Custom Association Page Registration in Workflow Feature

Now let's examine the definition within the element manifest file of the workflow Feature. You indicate to SharePoint that it should include your custom association ASPX page by adding the AssociationUrl attribute to the Workflow element, with the value of the attribute pointing to the URL of the page.

With a workflow association page, users can provide values for workflow settings in Windows SharePoint Services 3.0. This Visual How To showed you how to use an ASPX page to create an association between a custom workflow template and a container or a content type.

Watch the Video

Length: 11:26 | Size: 10.5 MB | Type: WMV