Friday, November 18, 2011

Creating Workflow Association Pages for Windows SharePoint

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.
<%@ Assembly Name="RegistrationsWorkflow, Version=1.0.0.0, 
  Culture=neutral, PublicKeyToken=5f88da336c1c0e26" %>
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" 
  EnableSessionState="true" ValidateRequest="False" 
  Inherits="U2U.Workflows.RegistrationsAssociation" %>
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.
<wssuc:InputFormSection Title="Discount Input" Description=
 "Specify the two discount percentages to be used in the workflow."
 runat="server">
 <template_inputformcontrols>
  <wssuc:InputFormControl runat="server" 
   LabelText="Discount percentages:">
     <Template_Control>
       <div>Enter discount for less than 10 tickets:
         <SharePoint:InputFormTextBox CssClass="ms-input" 
          ID="textBoxDiscount1" runat="server" Text="10"/>
        </div>
        <div>Enter discount for more than 10 tickets: 
          <SharePoint:InputFormTextBox CssClass="ms-input" 
           ID="textBoxDiscount2" runat="server" Text="20"/> 
        </div>
      </Template_Control>
   </wssuc:InputFormControl>
 </template_inputformcontrols>
</wssuc:InputFormSection>

<wssuc:ButtonSection runat="server" ShowStandardCancelButton="false">
 <template_buttons>
   <asp:PlaceHolder runat="server">
    <asp:Button UseSubmitBehavior="false" runat="server" 
       class="ms-ButtonHeightWidth" OnClick="cmdSubmit_OnClick" 
       Text="OK" id="BtnSubmit" /> &nbsp;
    <asp:Button UseSubmitBehavior="false" runat="server" 
       class="ms-ButtonHeightWidth" OnClick="cmdCancel_OnClick" 
       Text="Cancel" id="cmdCancel" causesvalidation=false />
     </asp:PlaceHolder>
   </template_buttons>
  </wssuc: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.

protected InputFormTextBox textBoxDiscount1;
protected InputFormTextBox textBoxDiscount2; 
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;
    }
}
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:

protected void PopulateFormDataFromString(string AssociationData) {
   XmlSerializer serializer = 
      new XmlSerializer(typeof(RegistrationsWorkflowData));          
   XmlTextReader reader = 
      new XmlTextReader(new StringReader(AssociationData));
   RegistrationsWorkflowData formData = 
      (RegistrationsWorkflowData)serializer.Deserialize(reader);
   textBoxDiscount1.Text = formData.Discount1;
   textBoxDiscount2.Text = formData.Discount2;
}
protected string SerializeFormDataToString()  {
   RegistrationsWorkflowData formData = _
      new RegistrationsWorkflowData();
   formData.Discount1 = textBoxDiscount1.Text;
   formData.Discount2 = textBoxDiscount2.Text;
   using (MemoryStream stream = new MemoryStream())  {
      XmlSerializer serializer = _
         new XmlSerializer(typeof(RegistrationsWorkflowData));
      serializer.Serialize(stream, formData);
      stream.Position = 0;
      byte[] bytes = new byte[stream.Length];
      stream.Read(bytes, 0, bytes.Length);
      return Encoding.UTF8.GetString(bytes);
   }
}
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);
 }
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.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   <Workflow Name="Registration Workflow"
    Description="This workflow calculates the total amount for a 
    registration done by a company"
    Id="{7EC50015-B1A5-406b-A385-43AF8B296916}"
    CodeBesideClass="U2U.Workflows.WFDiscount"  
    CodeBesideAssembly="RegistrationsWorkflow, Version=1.0.0.0, 
    Culture=neutral, PublicKeyToken=5f88da336c1c0e26"
    AssociationUrl=
     "_Layouts/Registrations/RegistrationsAssociation.aspx" >
    <Categories/>
    <MetaData />
  </Workflow>
</Elements>

No comments:

Post a Comment

Thank you for Commenting Will reply soon ......

Featured Posts

Open Hardware Monitor A Handy Tool for System Monitoring

#Open #Hardware #Monitor: A Handy Tool for #System #Monitoring #OpenHardwareMonitor is a free, #opensource #software designed to monitor t...