Step 9: Using ASXP forms in workflows Download the code [24/11/2008] Update : I cover aspx association forms in step 14 with much more details. Scenario Since WSS doesn’t allow the use of Infopath Forms , we will write our initiation form in “pure” aspx. [Note: we will improve and extend this sample in the future] Hands-on We’ll start from step 8 solution. In the U2UExpenseReport project, add a text file with an aspx extension : InitManager.aspx. In source mode, copy and paste the following code fragment in the aspx page: <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> Temporary Manager <asp:TextBox ID="TextBoxManager" runat="server"></asp:TextBox> <asp:Button ID="ButtonSubmit" runat="server" Text="Submit" /> </div> </form> </body> </html>
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Step 9: Using ASXP forms in workflowsDownload the code
[24/11/2008] Update : I cover aspx association forms in step 14 with much more details.
Scenario
Since WSS doesn’t allow the use of Infopath Forms , we will write our initiation form in “pure” aspx.
[Note: we will improve and extend this sample in the future]
Hands-on
We’ll start from step 8 solution.
In the U2UExpenseReport project, add a text file with an aspx extension : InitManager.aspx.
In source mode, copy and paste the following code fragment in the aspx page:
We need to provide more details about the assembly of the InitManager class by adding an Assembly directive to the page; this directive has to know the assembly strong name, so recompile the project and retrieve U2U.ExpenseReport strong name (with Reflector for instance).
Add the Assembly directive before the page directive (don’t forget that my PublicKeyToken is different than your own):
if (Page.IsPostBack) { Response.Write("hello, the submitted manager is " + TextBoxManager.Text); } }
Specify that the aspx page is the Initialization Form:
in the workflow.xml file, remove the <Instantiation_FormURN> in <MetaData>.
Modify the InstantiationUrl:
InstantiationUrl="_layouts/InitManager.aspx"
Modify the install.bat file to copy the aspx form(s) to the Layouts directory:
xcopy /s /Y *.aspx "%programfiles%\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS\"
Build the solution, call install.bat and test the workflow; the instanciation form should show up and when we click on the submit button whe should have a feeback.
Linking the form to the workflow
We want the workflow to be started when we click on the submit button.
We won’t interact directly with the workflow Runtime in Sharepoint but we interact with the site collection workflow manager (SPWorkflowManager class).
When Sharepoint will call our Initilialization form, it will provide some useful parameters :
Request.QueryString["List"] which contains the List Guid. Request.Params["TemplateID"] which contain the association Guid. Request.Params["ID"] which contains the ListItem Guid.
Define the following members in the InitManager class:
using Microsoft.SharePoint;using Microsoft.SharePoint.Workflow;using Microsoft.SharePoint.WebControls;
Upgrade the Page_Load code:
protected void Page_Load(object sender, EventArgs e){ if (Page.IsPostBack) { // Get the web site _Web = SPControl.GetContextWeb(this.Context); // Get the List string strListID = Request.QueryString["List"]; if (strListID != null) _List = _Web.Lists[new Guid(strListID)]; // Get the WorkflowAssociation Guid assocTemplateId = new Guid(Request.Params["TemplateID"]); _assocTemplate = _List.WorkflowAssociations[assocTemplateId]; // Get the ListItem _listItem = _List.GetItemById(Convert.ToInt32(Request.Params["ID"])); _Web.AllowUnsafeUpdates = true; Response.Write("hello, the submitted manager is " +
TextBoxManager.Text); Response.Write("<BR> the list title is " + _List.Title); } }
Rebuild, call install.bat and test the workflow.
Serializating the Initialization form data
Usually the iitialization form has several controls; the form data must be serialized in Xml and provided to the workflow.
Create a new class to encapsulate the Initialization form data:
namespace U2U.ExpenseReport{ [Serializable()] public class InitFormData { private string _manager; public string Manager { get { return _manager; } set { _manager = value; } } }}
Add a new helper class, AspxHelpers, with the following static methods:
class AspxHelpers { public static string SerializeFormToString(Type aType, object
initData) { using (MemoryStream stream = new MemoryStream()) { XmlSerializer serializer = new XmlSerializer(aType); serializer.Serialize(stream, initData); stream.Position = 0; byte[] bytes = new byte[stream.Length]; stream.Read(bytes, 0, bytes.Length); return Encoding.UTF8.GetString(bytes); } }
public static void InitiateWorkflow( string InitData, SPWeb Web,
We need to upgrade the section of the workflow code which deserializes the data coming from the Initialization form.
Select the workflow in the Workflow Designer; double click on the first activity (onWorkflowActivated1) and replace the existing deserialization code with the following one:
XmlSerializer serializer = new XmlSerializer(typeof(InitFormData));XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(WorkflowProperties.InitiationData));InitFormData tempManager = (InitFormData)serializer.Deserialize(reader);this.Manager = tempManager.Manager;
Rebuild the solution, call install.bat and test the workflow.
[24/11/2008]Update : I cover aspx association forms in step 14 with much more details.
Step 10: Reuse, Modify and Debug a Sharepoint Designer workflow in Visual Studio
1.Scenario
Microsoft Sharepoint Designer 2007 is a very interesting tool that allows developers and users to create workflows running in Sharepoint 2007 without writing code.The workflows generated by Sharepoint Designer cannot be debugged and reused in another site unless we import it in Visual Studio.Net.This tutorial will show you how to achieve this.
I assume that your have the correct setup for creating workflows for Sharepoint .
2.Hands-on
• Create a team site. By default a task list ("tasks") is generated.• Create another task list and call it “Issues”.• Now start Sharepoint Designer and open your web site (Menu “File-Open Site”).• Let’s create a very simple workflow that will copy a task list item from the "Task" list to the "Issues" list if the Title column of the record contains the word “problem”.• In Sharepoint Designer, start the Menu “File”-“New Workflow”; keep the default workflow name and select the list you want the workflow to be associated with: Task; unselect the option “allow this workflow to be manually started from an item” and select the two others.
In the condition, click on the “field” hyperlink and select Title in the combobox.In the condition, click on the “value” hyperlink and type “problem” (without the quotes).In the condition, click on the “equals” hyperlink and select “contains”.
Click on the Finish button of the form; Sharepoint Designer will generate the xoml code (which is not xaml activated code !!!) , it will compile the code ( xaml activated code must not be compiled) and it will associate the workflow to the corresponding list.
3.Testing the workflow
Add a new item in the task list: set the Title to “problem in Belgium”: since the Task's Title column contains the word “problem”, it will be copied to the Issues list by the workflow .
4.Importing the workflow in Visual Studio
In Sharepoint Designer, go to the Workflows folder, select Workflow1 and right click on “Publish Selected Files” :
Select the option “File System” (specify a file location) :
Click on OK. The following files will be generated at the selected location:
Rename the file Workflow1.xoml.rules to Workflow1.rules.
In our specific case, the .rules file contains the test that will check if the title column contains the word “problem”; this test is expressed in the CodeDom language.
Create a new Sharepoint Workflow project that will host the generated files ; name it MySPWorkflow :
The first activity (ID1) is a Microsoft.Sharepoint.WorkflowActions.OnWorkflowActivated activity and makes the link between the workflow and the Sharepoint context (like the list and the list item among many other things).
The activity ID6 is a Microsoft.Sharepoint.WorkflowActions.CopyItemActivity .The workflow itself is derived from Microsoft.SharePoint.WorkflowActions.RootWorkflowActivityWithData which is derived from SequentialWorkflowActivity.
The following code shows the real content code of Workflow1.xoml:
1.We can notice that this not what Workflow Foundation afficionados called “xaml activated” code which is xaml code that can run on the fly without having to be compiled. This code must be compiled: indeed the x:class attribute provides the compiler with a class name for the workflow :
The workflow itself is an activity named Root and derived from the class RootWorkflowActivityWithData.
2. According to the Sharepoint sdk, the RootWorkflowActivityWithData class has a WorkflowFields property which is a collection of key-object pairs where the key is a string (describing the property name) and the object a type (the type of the property); this is an handy way to dynamically add properties of any type to a class.
The following xoml fragment shows how Sharepoint Designer feeds this WorkflowFields property : <ns0:RootWorkflowActivityWithData> … <ns0:RootWorkflowActivityWithData.WorkflowFields> <ns0:WorkflowDataField Name="__list" Type="System.String" /> <ns0:WorkflowDataField Name="__item" Type="System.Int32" /> <ns0:WorkflowDataField Name="__context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" /> <ns0:WorkflowDataField Name="__initParams" Type="Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties" /> <ns0:WorkflowDataField Name="__workflowId" Type="System.Guid" />
__context: a Microsoft.SharePoint.WorkflowActions.WorkflowContext which is a class that will encapsulate some elements of the well known SPWorkflowActivationProperties (stored in the __initParams field). In our example, the instance of this class will be used internally by the CopyItem activity to provide some necessary information like the SPWeb for instance.
__initParams: a Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties ; this class provides many information like the SPWeb, the SPList, the SPListItem etc…
The reason for using a WorkflowContext instead of directly a SPWorkflowActivationProperties object is not clear here.
Another way to visualize these properties is to select the workflow object in the workflow designer and to display the associated property page :
Here is what we get if we click on the collection:
1. The original list Guid: ListId property . 2. The destination list Guid: ToListid property. 3. The list item index: ListItem property ; databinding from the __item property of the workflow. 4. The SPWeb object : __Context property; databinding from the __context member of the
workflow.
6.Deployment and test of the workflow
We will deploy the workflow as a feature, therefore we need to modify the install.bat file :
1. Sign the project 2. Retrieve the strong name 3. In install.bat , replace http://localhost with your site collection url 4. in install.bat, replace myFeature with the assembly name 5. run install.bat 6. test the workfow
7.Error
This workflow generates an error :
8.Fault Handler
To get more detailed information about this error, it is necessary to add a Fault Activity (equivalent of a catch block) and to log it.
Select the workflow in the (Visual Studio) Workflow Designer, click on the lower left side of the Designer and click on the Fault Handler:
Another view of the Workflow Designer will show up:
In the property page of this last activity select the FaultType property by browsing the mscrolib assembly:
Select System.Exception:
Now we will log the StackTrace into the workflow history log: drag and drop a LogHistoryList activity into the handler (if you don’t find this activity drag and drop the microsoft.Sharepoint.WorkflowAction.dll assembly to a new Visual Studio toolbox tab) :
Reset the web server, refresh the web site, open the project in Visual Studio, set a breakpoint in ID1_Invoked and attach the browser to the w3wp.exe host:
The condition that evaluates if the list item contains the word "problem" is stored in a rule file which is compiled with the application (in another post I will show you how to store and retrieve it dynamically from a database, and how a user can create this rules). The rules generated by Sharepoint Designer are rather cryptic.
Select the activity ID3 in the Workflow designer and click on the Condition property in the property page:
Select the Expression property, you’ll see something like this:
Replace the whole content of this windows with the following code :
Try to open the ModificationSample project;if you try to compile it, you will get a compilation exception since this project references another assembly provided with the sdk as well: ECMActivities; recompile the ECMActivities project and resolve the reference in ModificationSample.
If you doubleclick on the Workflow1 class you'll get the following model(I will describe the different elements of this workflow in the second part of this post, don't worry).
If we compare this model to the workflows we did in the previous tutorials, we'll notice 2 new activities: EnableWorkflowModification (a Sharepoint activity) and EventHandlingScopeActivity (which is part of the base activity library of Workflow Foundation).
Let's click on the manifest file (workflow.xml), you will notice a couple of new tags :
Several Infopath forms are also provided with this example and one of them which is referenced in the Modification element of the manifest is the Modification form:
In the Install.bat file, replace http://localhost with our site collection url and run it. Associate the workflow with a Sharepoint list and start the workflow. You'll notice that the
If you get back to the task list, you will notice that the task owner has been changed appropriately:
Now, if John click on its task, the task form will show up; if he clicks in the "completes" field, the workflow will complete; there is a small bug in the application, however : john's task doesn't complete, it is still necessary to add a Sharepoint "CompleteTask" activity :
Part 2. Understanding the EventHandlingScopeActivity
The EventHandlingScopeActivity is an activity that allows a workflow (or part of a workflow) to run while listening to events; it is a kind of (but just a kind of) ListenActivity or ParrallelActivity.
Ok, I'll give you a quick tutorial on it:
Create a Workflow Console application (Sequential workflow) and drag and drop a EventHandlingScopeActivity from the toolbox into the workflow surface:
Set the TimeoutDuration of the delayActivity to 20 seconds and in the codeActivity1 associated code, display "Main Application Completed".
These activities can be considered as part of the main flow of the workflow sequence.
In the background however, the workflow can listen to events while the main flow is not completed: in the case of our Sharepoint workflow the potential event that could come up is the OnWorkflowModified event which is triggered when a task is reassigned to another owner for instance.
To keep it simple, I will use another delay activity (it must be any activity implementing the IEventActivity interface and that's the case of the delay activity but also the case of all "green" Sharepoint activities : these green activites are derived from HandleExternalEvent activity, which implement IEventActivity). The activity must be encapsulated in an EventDriven activity (the first activity of an EventDriven activity must implement IEventActivity); it is like a catch for an event (instead of a catch for an exception). Lets go for it.
I will assume that you know how to create sharepoint workflows and how to link Infopath Forms to the workflow association, Initiation and task changing state; if it is not the case, follow the previous versions of my sharepoint workflow tutorials.
Add an existing Sharepoint workflow project to the current solution
Step 1.Add to your solution the HelloWorldSequential workflow provided with the sdk ; in its install.bat file, replace http://localhost with your site collection url, deploy it and play with it.
Step 2.Add the ModSimpleModificationForm.xsn Infopath Form of ModificationSample (sdk) to the location of the HelloWorldSequential project.
Step 3.Register the Modification forms in workflow.xml:
-generate a Guid with Guidgen (Visual Studio-Tools-Create GUId menu)
-after the <Task0_FormURN> elements, add 2 elements: <Modification_xxx_FormURN> and <Modification_xxx_Name> where xxx is the generated Guid like this:
<Modification_bf35e820-5070-4e33-bc91-de30388e0b7e_FormURN> urn:schemas-microsoft-com:office:infopath:ModSampleModificationForm:-myXSD- 2006-05-17T01-22-06 </Modification_bf35e820-5070-4e33-bc91-de30388e0b7e_FormURN> <Modification_bf35e820-5070-4e33-bc91-de30388e0b7e_Name> Update task owner </Modification_bf35e820-5070-4e33-bc91-de30388e0b7e_Name> Step 4.Run Install.bat, associate the workflow with a list and run it on a list item. If you click on the workflow status (when the workflow is "in progress", you will notice that the "update task owner" message is not visible; we need to add another functionality (next step). Step 5.Add the EnableWorkflowModification activity just before the CreateTask activity :
set its correlation token to the current token, "modifToken" and the OwnerActivityName to HelloWorldSequential.
set its ModificationId property to the Guid defined before: bf35e820-5070-4e33-bc91-de30388e0b7e
compile, install and test the workflow : the workflow will crash and you will get an "an error has occured" message.
try to visualize the error message by setting up a FaultHandler activity in the Fault Handler view of the workflow and log the error message in the workflow history by using a LogToHistoryList activity :
Set the FaultType property of the FaultHandler activity to System.Exception and the HistoryOutCome property of the LogToHistoryList activity to the exception message property as following:
You will get a rather unclear exception (if you click on the workflow completed status hyperlink):
Step 6. Add a sequence activity in yhe EventhandlingScope activity and move the EnableWorkflowModification activity and the other activities that follow (disable the SendMail, LogToHistoryList and CodeActivity) into an EventHandlingScope activity (group them all in a sequence activity):
in the CorrelationToken property of the grouped activities, set the owner activity property to the EventHandlingScope activity name
Build and test, you will get the following (same) error:
In the EventHandlingScope activity Event Handler, add an EventDriven activity and a OnWorkflowActivated activity like this:
Set the OnWorkflowModified activity CorrelationToken property to "modifToken". Set the ModificationId to the value defined above. Build, run and test the workflow, and this time the workflow status will be "in progress" which is
a good sign ! If you click on the workflow status, you will see the "Update task owner" hyperlink:
However, if you click on the "Update Task owner" hyperlink, you will get an Infopath Form exception; indeed the contextData, which is supposed to contain the data to be transferred to/from the modification form is not yet ready. We will manage this in the next step.
Step 7. Sending data to the modification form
As we did it in one of the the tutorial, let's use xsd.exe to generate a class that will be mapped to the modification form.
Open the modification form with Infopath and use the "save as source" menu; this will generate (among others), an .xsd file.
generate the mapping .net class : xsd.exe myschema.xsd /c
rename the generated class file as "modificationForm.cs" add the file to the workflow project create an helper function to serialize an Infopath form schema:
private string SerializeModificationData(ModificationForm form) { using (MemoryStream stream = new MemoryStream()) { XmlSerializer serializer = new XmlSerializer(typeof (ModificationForm)); serializer.Serialize(stream, form); return Encoding.UTF8.GetString(stream.GetBuffer()); } }
select the EnableWorkflowModifcation actvity and bind its ContextDate property to a new workflow field that we will call _ContextData.
at the end of the OnWorkflowActivated event handler, add the following lines : ModificationForm modifForm = new ModificationForm(); modifForm.taskOwner = assignee; this._ContextData = this.SerializeModificationData(modifForm);
Build, install and test the workflow : click on the "Update Task owner" link and the modification form will show up:
We still need to be able to take the modification data into account.
Step 8. Sending data from the modification form
Everytime the modification form data will be submitted, the OnWorkflowModified activity will be triggered.
To get the new task owner, we need to deserialize to data from the Modification form. To manage this, create a function to deserialize Infopath form data:
private ModificationForm DeserializeFormData(string xmlString) { using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes (xmlString))) {
XmlSerializer serializer = new XmlSerializer(typeof (ModificationForm)); ModificationForm data = (ModificationForm)serializer.Deserialize (stream); return data; } }
Define a newAssignee data member in the workflow class : private string newAssignee = default(string);
Switch to the EventHandler of the EventHandlingScope activity Add an eventHandler to the Invoked event of the OnWorkflowModified activity Deserialize the Infopath form as following:
We still need to update the existing task(s) by using an UpdateTask activity: drag & drop an Update Task activity just after the onWorkflowModified1 activity:
Link the UpdateTask activity to the created task activity by "databinding" its TaskId property to the workflow taskId property and by "databinding" its TaskProperties to the workflow member afterProps (which is databound to the afterProperties property of the onTaskChanged1 activity).
Set also its correlation token to modifToken. Set an event handler to the updateTask1 activity with the following code that will modify the
Assigned to user : private void updateTask1_MethodInvoking(object sender, EventArgs e)
The Out-of the box approval workflow provided with MOSS allows to specify several approvers. In this post, I will illustrate how to do this. In a first time (part 1) , I will show you how to use the Replicator activity that you will need, and in a second time (part 2), you will apply this knowledge to our Sharepoint problem. If you know how the Replicator activity works you can directly go to part 2.
Part 1. Using the Replicator activity
The Replicator activity is part of the Workflow Foundation Base Activity Library, is somewhat similar to the While activity.
Create a Sequential Workflow console application project. Add a Replicator activity into the workflow surface
Add a custom Activity to the project; call it "ManageTask":
In the Replicator activity, double click on the ChildInitialized event to generate an event handler :
Each item in the collection will be associated with a child activity (the child activity here is ManageTask): each time a child activity will be created, the ChildInitialized event will be triggered, which give us the possibility to pass the associated data :
Part 2. Using the Replicator activity in the Approval workflow
as I did in the previous post, I will start by using the HelloWorldSequential sample provided with the MOSS sdk. Don't forget to replace the string http://locahost in the install.bat file with you own web site/site collection url. (See step 11 of this tutorial for more infos about this sample).
The strategy is to replicate the following selected activities in a Replicator activity :
We will group these activities in a custom sequential activity.
Add a new project (Sharepoint-Sequential Workflow Library ) to the current solution
You can either drag & drop the activities from Workflow1 to the new custom activity or adding new activities in the ManageApproval activity (my option) :
Recompile the solution to make sure your custom activity will show up in the toolbox. Add a Replicator activity just after the OnWorkflowActivated activity and drag & drop you
Modify the install.bat file in order to register the custom activity in the Gac :"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -uf MyApprovalActivities"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\MyApprovalActivities.dll
Test the workflow: rebuild, install the workflow, add the association and start a workflow ; assign to user separated with a ";" , for instance U2UCOURSE\Administrator;U2UCOURSE\serge, like this :
A first task will be created for the user "Administrator":
The reason Serge's tasks is created after the Administrator's task completes is that the ExecutionType property of the Replicator activity was set to Sequence. Let's change it to parallel, rebuild and test the workflow .
Congratulations !!!!
Step 13: Fixing and running the ASPXCollectFeedback sample (sdk)
Introduction
I’ve received tons of emails related to workflows & aspx forms.
Indeed, I’ve faced many situations where Infopath forms in Sharepoint workflows are not flexible enough or not stable enough; therefore we need the full power and stability of the Asp.net framework (and moreover, some applications need to be based on the free WSS without Infopath Form server).
For instance, many people want to see the history list in the task edit form, they want to use Silverlight in the workflow forms,… ; I’ve recently worked on a project where the association form must automatically associate several informations to each CreateTask activity, the form uses reflection to retrieve each CreateTask activity… I’ll show you some other examples in the next posts.
The first natural step for a Sharepoint developer is to check the WSS/MOSS sdk to find a good sample. There is a pretty good one , the ASPXCollectFeedback, which unfortunately doesn’t work as expected unless we fix it; that’s what I’m going to do in this post : fixing this sample. I’m very surprised that no one in the Google/LiveSearch universe has complained about this sample (this sample was provided since 2006…). Let’s fix this for the Sharepoint community.
Open the solution
Install the downloadable WSS sdk 1.4, backup the projects located in c:\Program Files\2007 Office System developer Resources\ASPXCollectFeedback\ and open the CollectfeedbackWorkflow.sln solution.(fig 1).
[Fig 1.ASPXCollectFeedback Project location in the WSS 1.4 sdk ]
The problems and the solutions
Problem 1. One project (TaskWorkflowContentType) is not compiled by default.
Solution: very easy, right click on the solution property page, select Configuration Properties-Configuration and check the Build option of the TaskWorkflowContentType project (fig 2).
The sdk sample is based on VS2005 where gacutil was in a different location. Edit the InstallAll.bat file to take the gacutil.exe location into account ("%programfiles%\
Microsoft SDKs\Windows\v6.0A\Bin\gacutil.exe"). Update installAll.bat with your site collection url to the stsadm instructions. Now, run the InstallAll.bat. Normally the workflow and its associated features haven been
installed and activated. You can associate the workflow with a list and start the workflow on a list item.Problem 3.The custom task form doesn’t show up in display mode
Indeed when you click on the workflow task, only the standard task list shows up instead TaskWCT.aspx (the custom form) (fig 3).
[Fig 3.The standard task form shows up instead of the custom form]
The reason for this is the Content type manifest file provides only a custom edit behavior , but no custom display behavior.The standard one is used instead.
Indeed, if you click on the Edit Item menu, the custom aspx form will show up (fig 4):
To fix this, edit the manifest file TaskWorkflowContentType.xml in the TaskWorkflowContentType project and add a display option (fig 5) :
<Display>_layouts/TaskWFCT.aspx</Display>
[Fig 5.Adding a custom Display behavior]
Make sure your new content type definition will replace the previous one by deactivating/uninstalling the existing content type and verify that the old content type is removed it by looking at the content type site gallery.
Problem 4. “Require Manage Lists permission to start the worklow” option is not used :
The association page doesn’t take the “Require manage Lists permissions to start the workflow” option into account (Fig 6).
Run InstallAll.bat and re-associate and test your workflow. In the next post I will describe the project.Problem 5. The List Content Type case is not handled
Indeed there are 3 kinds of workflow associations:
1. Workflow association to a list 2. Workflow association to a content type 3. Workflow association to a content type in a specific list (this one is missing in the sample)
In the SPWorkfowAssociation class provides the following methods:
There are many reasons to associate workflows with aspx forms in Sharepoint instead of Infopath forms (IP):
aspx forms are more robust more flexible than IP forms can be debugged support very well javascript/Ajax/Silverlight work with Windows Sharepoint Service which is free.The black sides of aspx forms in Sharepoint workflows are :
they are not integrated in Office 2007 rich client they are more complex to develop than IP forms the main sample in the Sharepoint sdk doesn’t work (see my previous post where I show how to
fix it).The first kind of form you need to create is the association form; I’ve encapsulated and abstracted the complexity of association forms in a small framework; this framework will be upgraded in the future to handle other kind of forms and will be available in CodePlex.
There will be 3 parts in this post :
In parts 1 and 2, I will show you how to quickly create an association form by using my framework.
In part 3, I will detail how association forms work under the cover.Part 1. Creating a basic association form
Creating the workflow
Let’s create a very basic Sharepoint workflow by using the Sharepoint sequential workflow template and name the project DemoAssocFormWorkflow (Fig 1).
[Fig 4.Create the “12 like” folders for the Layout page (association form)]
Add a FEATURES folder with a DemoAssocFormWorkflow custom folder under the TEMPLATE folder and move the feature.xml and workflow.xml over there :
[Fig 5.Add the Features folder and the features files]
Add the file WFAssoc.aspx into this folder; you can get it from my startup code; this page is very primitive WorkflowAssociation page; I’ve kept it simple on purpose. This page just displays an Ok button.
Add the WFAssoc.cs page(from the startup code) to your project root; this file is the association page code behind (fig 6).
You still need to specify somewhere that you want WFAssoc.aspx to become your association form: do it in workflow.xml :
[Fig 10. Defining the association forms in the feature manifest]
That’s it! Now it’s just a matter of installing the workflow, the page layout and the Framework assembly.
Installing the workflow
Add the install.bat file into the project root; double check the file to make sure everything is ok (in the Install.bat file we assume the application pool name is SharepointPool), change the url (which here is http://blog.redwood.com) ; add a Post-Build event to the project :
[Fig 10.Install.bat file in the Post-build event]
Rebuild the project and check the output windows for any error (if you don’t find this window, you can display it from the View menu).
Association type 2 : workflow associated with a content type
Create 2 item content types : Vehicule and Car which inherits from Vehicule Associate these content types to a generic list named Parking. Associate the DemoAssocFormWorkflow with the Vehicule content type (in the Site Content Type
gallery)
[Fig 15.Associate a workflow with a content type]
Add a new Vehicule in the parking list Start the workflow in the list item ECB (menu linked to the list item).
If you add a new Car (not just a Vehicule), the workflow association will aslo show up.Association type 3 : Workflow associated with a content type in a specific list
In the parking list, create a new association with the Vehicule content type (Fig 16).
[Fig 16.Workflow associated with a content type in a specific list]
This association will be available on every Vehicule in this list, but not on the cars !
Part 2. Extending the association form
We want to be able to create the following association form (Fig 17) :
In WFAssoc.aspx, replace the existing code in “Placeholdermain” just above the hidden fields with code provided in snippet2.txt in the starter files (keep the hidden fields, remove everything else).
Rebuild you workflow and test the association form to make sure it can show up. In WFAssoc.cs define the form fields as protected members (snippet3.txt). Define the fields namespace (Resolve) :using System.Web.UI.WebControls;using Microsoft.SharePoint.WebControls;
We need to add a class that will allow data transfer between the association form and the workflow; Add the file AssocFormData to your project from the starter files, and examine the code.
since AssocData is the data transfer class, replace the <object> generic with <AssocData>:
[Fig 18. specifying the AssocFormData class in the generic]
by implementing FromAssocDataToPage(), you will transfer your association data from the workflow association to the association form. (snippet4.txt).
The association form must be a layout page (derived directly or indirectly from Microsoft.SharePoint.WebControls.LayoutsPageBase).
This query string and others variables described below will be sent to your Association Form and your custom association page must handle them (my small WorkflowAssociationContext class will do that for you).
The picture below illustrates most variables (in red) you association form will have to manage (for a content type).
The SPWorkflowTemplate is actually a wrapper around the workflow type.
When you click on the Ok button of your custom association form, you need to make sure the http variables have been stored somewhere; it is mandatory to provide hidden fields that will keep a trace of all variables provided by the Sharepoint framework (AddWrkfl.aspx) between postbacks. That’s reason why I’ve added the hidden fields into the WFAssoc.aspx :
[Fig 24.Hidden field to keep workflow association variables between postbacks]
My WorkflowAssociation class hides all the details of getting/storing these variables values and creating/updating the corresponding Sharepoint objects.
That’s it! Congratulations! Don’t forget that I still have to fully test this code and that I will upgrade the framework to take the other kinds of forms into account
Step 15 : Easy ASPX Initiation Forms-(my small generic framework)
1. Download the startup project 2. Download the startup files
3. Download the framework code (optional) 4. Download the final code(optional)
Introduction
After the previous post, I’ve upgraded my generic framework to support Association and Initiation Forms. In this post I will illustrate how to create initiation forms.
The assembly and the main namespace have been renamed “SergeLuca.WorkflowTools.AspxForms”.
The framework now provides 2 main classes your association or initiation page must be derived from:
public abstract class GenericWFAssocPage<TA> : Microsoft.SharePoint.WebControls.LayoutsPageBase
(TA is the Association Data class )
public abstract class GenericWFInitPage<TI, TA> : Microsoft.SharePoint.WebControls.LayoutsPageBase
(TI is the Initiation Data class)
These classes make use internally of the classes :
This startup project is more or less the project you build in step 14.
The main differences are :
My generic aspx form framework has been compiled in an assembly called SergeLuca.WorkflwTools.AspxForms which has been referenced to the project. The framework code can be downloaded here and it is an upgraded version of the one described in step 14.
The FromAssocDataToPage function defined in WFAssoc.aspx provides the association data directly (the context has been hidden in the library).
[Fig 1.Generic framework assembly referenced in the project]
An initiation form, WFInit.aspx, has been provided in the LAYOUTS\DemoAspxFormWorkflow folder :
[Fig 2. WFInit.aspx Initiation form provided with the project]
Open this file, analyze the code : this page is very similar to the association page created in the previous post except that we don’t have the radSerial and radParallel checkboxes.
Observe the @Page directive : the code behind will be implemented in a class named WFInit that we are going to create.
2. Add and implement the Initiation form Page : the GenericWFInitPage
Add a new class to the project, call it WFInit and derive this class from a class provided by my framework: GenericWFInitPage; this class takes 2 generic parameters : the class storing the Initiation Form Data which will must implement and the class storing the association form data (AssocFormData provided in the previous post).
Add the file InitFormData.cs from the startup files folder and examine its content. Specify the generic parameters in the GenericWFInitPage definition like this:public class WFInit : GenericWFInitPage<InitFormData,AssocFormData>
In WFInit, define members for the aspx controls (grab the code from snippet1.txt) GenericWFInitPage is an abstract class defined in my framework: you must implement 2 abstract
methods:
1. FromPageToInitData() (grab the code from snippet2.txt) 2. FromAssocToInitPage(AssocFormData assocFormData) (grab the code from
snippet3.txt)
Submit the Initiation data to the workflow by providing an event handler to the OK button public void BtnOK_Click(object sender, EventArgs e) {
this.SubmitInitiation();
}
Modify the provided install.bat file to make sure your feature is activated at your site collection url.
Rebuild your project and test the workflow.3.Using the Initiation or Association form data from the workflow
In the workflow double click the OnWorkflowActivated1 activity to generate an event handler. In this event handler add the code provided in snippet4.txt. Rebuid and test your workflow