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
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.
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.
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.
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" %>
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" /> <asp:Button UseSubmitBehavior="false" runat="server" class="ms-ButtonHeightWidth" OnClick="cmdCancel_OnClick" Text="Cancel" id="cmdCancel" causesvalidation=false /> </asp:PlaceHolder> </template_buttons> </wssuc:ButtonSection>
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;
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; } }
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); } }
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); }
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 ......