step by step tutorial - part 2

101
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>

Upload: semalaiappan

Post on 12-Nov-2014

1.944 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Step by Step Tutorial - Part 2

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:

 <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>

In design mode, the page will look like this: 

 

Page 2: Step by Step Tutorial - Part 2

 

 Let’s add a new class, InitManager in a file InitManager.aspx.cs (make sure the class is public !).

 

 

Derive  InitManager from the Page class :

  public   class InitManager : System.Web.UI.Page {

 

Add the page directive at the top of InitManager.aspx:

 

<%@ Page Language="C#" AutoEventWireup="true" ValidateRequest="False" Inherits="U2U.ExpenseReport.InitManager" %>

Page 3: Step by Step Tutorial - Part 2

 

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):

 <%@ Assembly Name="U2U.ExpenseReport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=338fc881ff6f0902"%><%@ Page Language="C#" AutoEventWireup="true" ValidateRequest="False" Inherits="U2U.ExpenseReport.InitManager" %> 

Implement the Page_Load event handler:

 

protected void Page_Load(object sender, EventArgs e){

}

 

 

Add a new protected member that will allow us to retrieve the textbox value of the .aspx page:

   public   class InitManager : System.Web.UI.Page{        protected TextBox TextBoxManager;

 

 

Add the corresponding using directive :

 

using System.Web.UI.WebControls;

 

Modify the Page_Load event handler to display the manager:

  protected void Page_Load(object sender, EventArgs e)   {

Page 4: Step by Step Tutorial - Part 2

       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).

Here is the pseudo-code:

Web.Site.WorkflowManager.StartWorkflow(<listItem>, <associationTemplate>, <InitializationData>);

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:

 private SPWeb _Web;private SPList _List;

Page 5: Step by Step Tutorial - Part 2

private SPListItem _listItem;private SPWorkflowAssociation _assocTemplate;

 

And the corresponding using directives:

 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:

Page 6: Step by Step Tutorial - Part 2

 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,

SPList List, SPListItem listItem, SPWorkflowAssociation assocTemplate)        {            try            {                Web.Site.WorkflowManager.StartWorkflow(listItem,

assocTemplate, InitData); 

Page 7: Step by Step Tutorial - Part 2

            }            catch (Exception ex)            {                SPException spEx = ex as SPException;                 string errorString;                 if (spEx != null && spEx.ErrorCode == -2130575205

/* SPErrorCode.TP_E_WORKFLOW_ALREADY_RUNNING */)errorString = SPResource.GetString(Strings.WorkflowFailedAlreadyRunningMessage;

                else if (spEx != null && spEx.ErrorCode == -2130575339 /*SPErrorCode.TP_E_VERSIONCONFLICT */)

                    errorString = SPResource.GetString(Strings.ListVersionMismatch);

                else if (spEx != null && spEx.ErrorCode == -2130575338 /* SPErrorCode.TP_E_LISTITEMDELETED */)                    errorString = spEx.Message;                else                    errorString =

SPResource.GetString(Strings.WorkflowFailedStartMessage);                 SPUtility.Redirect("Error.aspx",                    SPRedirectFlags.RelativeToLayoutsPage,                    HttpContext.Current,                    "ErrorText=" +

SPHttpUtility.UrlKeyValueEncode(errorString));            }             SPUtility.Redirect(List.DefaultViewUrl, SPRedirectFlags.UseSource, HttpContext.Current);        }                }

In InitManager,  add a new data member:

      public class InitManager : System.Web.UI.Page       {         …         private InitFormData _initData = new InitFormData();

 

Upgrade the Page_Load event handler to serialize the form data and to start the workflow:

         protected void Page_Load(object sender, EventArgs e)        {

            …

            _initData.Manager = TextBoxManager.Text;            string initData =

Page 8: Step by Step Tutorial - Part 2

AspxHelpers.SerializeFormToString(typeof(InitFormData),_initData;            AspxHelpers.InitiateWorkflow(initData, _Web, _List, _listItem,                                                             _assocTemplate);        }  

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.

 

Page 10: Step by Step Tutorial - Part 2

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”.

 

In the Action, select “Copy List Item”.

Page 11: Step by Step Tutorial - Part 2

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) :

Page 12: Step by Step Tutorial - Part 2

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 :

 

Page 14: Step by Step Tutorial - Part 2

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:

 

Page 15: Step by Step Tutorial - Part 2

<ns0:RootWorkflowActivityWithData x:Class="Microsoft.SharePoint.Workflow.ROOT" x:Name="ROOT" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ns0="clr-namespace:Microsoft.SharePoint.WorkflowActions;Assembly=Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">

      <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" />       </ns0:RootWorkflowActivityWithData.WorkflowFields>       <ns0:OnWorkflowActivated WorkflowProperties="{ActivityBind ROOT,Path=__initParams}" x:Name="ID1" EventName="OnWorkflowActivated">             <ns0:OnWorkflowActivated.CorrelationToken>                   <wf0:CorrelationToken Name="refObject" OwnerActivityName="ROOT" xmlns:wf0="http://schemas.microsoft.com/winfx/2006/xaml/workflow" />             </ns0:OnWorkflowActivated.CorrelationToken>       </ns0:OnWorkflowActivated>       <IfElseActivity x:Name="ID4" Description="Step 1">             <IfElseBranchActivity x:Name="ID3">                   <IfElseBranchActivity.Condition>                         <RuleConditionReference ConditionName="__Rule_ID3" />                   </IfElseBranchActivity.Condition>                   <ns0:CopyItemActivity ListId="{}{7DC0CBDB-1A73-4A9A-8F7C-7FF3EFC3807B}" x:Name="ID6" Overwrite="False" ToListId="{}{A3EBF037-9BAA-49DC-A8B6-87A8795DA41F}" __Context="{ActivityBind ROOT,Path=__context}" ListItem="{ActivityBind ROOT,Path=__item}" />             </IfElseBranchActivity>       </IfElseActivity> </ns0:RootWorkflowActivityWithData>

 

5.Xoml code

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 :

 

<ns0:RootWorkflowActivityWithData x:Class="Microsoft.SharePoint.Workflow.ROOT" x:Name="ROOT"

The workflow itself is an activity named Root and derived from the class RootWorkflowActivityWithData.

Page 16: Step by Step Tutorial - Part 2

 

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" />

Page 17: Step by Step Tutorial - Part 2

      </ns0:RootWorkflowActivityWithData.WorkflowFields>  

__list: the list Guid.

__item: the index of the ListItem.

__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:

Page 18: Step by Step Tutorial - Part 2

This will initialize the informations the CopyItemActity needs (see later).

 

Now, let’s take a look at the CopyItemActivity properties :

In order to copy one ListItem to an other list, the CopyItemActivity needs some informations:

Page 19: Step by Step Tutorial - Part 2

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:

Page 21: Step by Step Tutorial - Part 2

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) :

Page 23: Step by Step Tutorial - Part 2

Rebuild the solution , redeploy (install.bat) and test the workflow.

The trace will show up in the workflow status :

Remove the activity ID2 : this activity (class ApplyActivation) is the source of the error.

Let’s Click on the ID1 activity to generate the code behind and paste the following code in the generated handler:

        private void ID1_Invoked(object sender, ExternalDataEventArgs e)         {             __context = new WorkflowContext();             __context.Initialize(__initParams);             __list = __initParams.List.ToString();             __item = __initParams.ItemId;             __workflowId = __initParams.WorkflowId;         }

 

Recompile the workflow, and test it.

9.Debugging our workflow

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:

Page 24: Step by Step Tutorial - Part 2

Invoke the workflow by changing a value in the title property, you should hit your breakpoint.

Reusing the workflow in another site

The good news is that the CopyItemActivity can use both list Guid or list name.

Select its property page and set the ListId and ToListId properties respectively  to “Tasks” and “Issues”:

Recompile the workflow, deploy and test it to make sure it works again ;-)

The same workflow can be reused in another site if the appropriate list (“Tasks” and “Issues”) are present.

 

Page 25: Step by Step Tutorial - Part 2

10.Modify the .rules files

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 :

Page 26: Step by Step Tutorial - Part 2

That’s it ! Recompile, deploy and test the application .

 

Step 11: Modification Forms

Introduction

Modification forms allow us to reassign a workflow task to another user.

3 parts in this article:

1. What are Modification Forms 2. Introduction to the EventHandlingScope activity 3. Adding a Modification form to an existing workflow

Part 1. What are Modification Forms

To illustrate this concept, I will run a workflow provided with the downloadable version of the Sharepoint Sdk (1.3 in my case).

By default, the samples are installed in the following location :

 

Page 27: Step by Step Tutorial - Part 2

 

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).

Page 28: Step by Step Tutorial - Part 2

 

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).

 

Page 29: Step by Step Tutorial - Part 2

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:

Page 30: Step by Step Tutorial - Part 2

 

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

Reviewer is U2UCOURSE\serge :

Page 32: Step by Step Tutorial - Part 2

 

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 :

Page 34: Step by Step Tutorial - Part 2

Recompile, and (re)install the workflow.

 

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:

Page 37: Step by Step Tutorial - Part 2

 

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.

 

Page 38: Step by Step Tutorial - Part 2

To show the event side of the EventHandlingScope activity, right click on it and select "view Event Handlers" as following:

 

You'll see this :

Page 40: Step by Step Tutorial - Part 2

Lets drag and drop a Delay activity (and set its timeout property to 10 seconds, to make sure the event will fire before the main flow completes).

Just after the Delay activity, add a code activity and display the message "delay event fired in the background" in the associated code:

 

Run the application and you will get something like this

 

Part 3. Using a Modification form in an existing workflow

 

Page 41: Step by Step Tutorial - Part 2

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 :  

Page 42: Step by Step Tutorial - Part 2

 

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 :

 

Page 43: Step by Step Tutorial - Part 2

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):

Page 44: Step by Step Tutorial - Part 2

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:

Page 45: Step by Step Tutorial - Part 2

 

 

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

Page 46: Step by Step Tutorial - Part 2

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)))             {

Page 47: Step by Step Tutorial - Part 2

                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:

     private void onWorkflowModified1_Invoked(object sender, ExternalDataEventArgs e)      {             ModificationForm modForm = this.DeserializeFormData(this._ContextData);             this.newAssignee = modForm.taskOwner;      }

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)

Page 48: Step by Step Tutorial - Part 2

        {             afterProps.AssignedTo = this.newAssignee;           }

step 9. Testing

Build deploy and test the workflow; it is very important to make sure the task data are updated even when you redirect the form to another user:

 

 

Get to the Tasks list

Click on the title hyperlink and modify the instructions in the form as following (here I've added "modified by admin") :

Page 51: Step by Step Tutorial - Part 2

Step 12: Managing several approvers/reviewers

Introduction

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": 

Page 54: Step by Step Tutorial - Part 2

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 :

Run the application:

Page 55: Step by Step Tutorial - Part 2

You can download the code here.

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

Page 58: Step by Step Tutorial - Part 2

Link each of these activity to the same correlation token : TaskToken and ManagerApproval for the OwnerActivityName

Select the CreateTask activity and databind its TaskId and TaskProperties property to  new fields :

TaskId and TaskProperties

Databind the TaskId prperty of the OnTaskChanged and CompleteTask activities to the newly created taskId member.

On the OnTaskChanged activity, databind the AfterProperties and BeforeProperties to new fields:

AfterProperties and BeforeProperties

Define the following members (make sure Assignee is public since it will get the task owner from the Replicator activity) :

 

Double click on the CreateTask activity to generate a handler and add the following code:(most of the code can be copied from Workflow1.cs)

Page 59: Step by Step Tutorial - Part 2

Set the Condition property of the WhileActivity to Code Condition and name the function TaskNotFinished :

Define a bool data member, isFinished , and check its status in the TaskNotFinished function:

Double click on the TaskChanged activity to generate a handler and add the following code:

Sign you assembly Get back to the workflow project and double click on the workflow code to display it in the

designer; clean it up by deleting the activities you don't need anymore; you must have something like this:

Page 60: Step by Step Tutorial - Part 2

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

custom activity in it.

Page 61: Step by Step Tutorial - Part 2

In the workflow code, define a new member to host the assignees:

Store the assignees into this ArrayList: click on the OnWorkflowActivated activity and add the following code in the handler:

Bind the Replicator activity to the Assignee member. Add a handler to the ChildInitialized event of the Replicator and insert the following code:

Page 62: Step by Step Tutorial - Part 2

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":

Page 63: Step by Step Tutorial - Part 2

If the Administrator clicks on its task :

Then, if the user "Administrator" select the "I am finished reviewing" and click the Submit button, a new task will be created for the user "serge":

Page 64: Step by Step Tutorial - Part 2

 

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).

Page 65: Step by Step Tutorial - Part 2

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).

Page 66: Step by Step Tutorial - Part 2

[Fig2.Build options ]

Rebuild the solution.

Problem 2. Gacutil in install.bat

 

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).

Page 67: Step by Step Tutorial - Part 2

[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):

Page 68: Step by Step Tutorial - Part 2

[Fig 4.Custom task form]

 

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).

Page 69: Step by Step Tutorial - Part 2

[Fig 6. This Permission is not  used correctly in the sample]

To fix this, you need to add an additional hidden field in the WFAssoc.aspx page (Fig 7);

<input type="hidden" name="ManualPermManageListRequired" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["ManualPermManageListRequired"]),Response.Output); %>'>

(More details about this in my next tutorial)

Page 70: Step by Step Tutorial - Part 2

[Fig 7. Missing hidden field]

 

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:

public static SPWorkflowAssociation CreateListAssociation (SPWorkflowTemplate baseTemplate,string name,SPList taskList,SPList historyList)public static SPWorkflowAssociation CreateSiteContentTypeAssociation (SPWorkflowTemplate baseTemplate,string name,string strTaskList,string strHistoryList)public static SPWorkflowAssociation CreateListContentTypeAssociation (SPWorkflowTemplate baseTemplate,string name,SPList taskList,SPList historyList

Anyway, I will fix this problem in a next post.

Step 14 : Easy ASPX Association Forms-(my small generic framework)

Download the startup code

Download the final code

Download the framework code

Introduction

Page 71: Step by Step Tutorial - Part 2

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).

Page 72: Step by Step Tutorial - Part 2

[Fig 1. Basic workflow project]

Untick the “Automatically Associate…” option in the third form form” (Fig 2).

 

[Fig 2. Don’t automatically associate the workflow]

Add a reference to the SergeLuca.WorkflowTools.WorkflowAssociation.dll (Fig 3) (this dll can be grabbed from the startup files).

Page 73: Step by Step Tutorial - Part 2

[Fig 3.Add a reference to my small framework]

Set the Copy Local property of this assembly to true. Create the classic TEMPLATE-LAYOUTS folder with you custom sub folder:

DemoAssocFormWorkflow

Page 74: Step by Step Tutorial - Part 2

[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).

Page 75: Step by Step Tutorial - Part 2

[ Fig 6.Generic Page class]

GenericWFAssocPage is an abstract class; implements its methods :

[Fig 7.Implementing the Abstract class]

Remove the generated throw new NotImplementedException() statements and return null in the FromAssocDataToPage member :

Page 76: Step by Step Tutorial - Part 2

[Fig 8.Basic generic page implementation]

 

[Update 11/24 : in the next release the Function FromAssocDataToPage will only have an AssocFormData parameter]

 

Compile the project and use Reflector to retrieve the assembly strong name.Modify the assembly directive of WFAssoc.aspx (Fig 9).

Fig 9.Modify the @Assembly directive to point to your code behind assembly

Page 77: Step by Step Tutorial - Part 2

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).

The final code can be found here.

Page 78: Step by Step Tutorial - Part 2

Association forms testing

 

Normally the install.bat file activates the feature for the whole site collection.

We are going to test the 3 kinds of workflow associations

Association type 1 :   workflow associated with a list

 

Go to a list or document library and add the association.Since an association form has been provided, the next button will show up :

Page 79: Step by Step Tutorial - Part 2

[Fig 11. Standard workflow association form]

Click on the Next button and our form will show up :

[Fig 12. Custom workflow association form]

 

Click ok and the the association will be created. You can modify the association by asking to select new Task and History list (Fig 13):

Page 80: Step by Step Tutorial - Part 2

 

[Fig 13.Modification of a workflow association]

Modify the association and View the site content : you will see your new Task and History lists  (Fig 13):

Page 81: Step by Step Tutorial - Part 2

[Fig 14.New Task and History lists ]

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).

Page 82: Step by Step Tutorial - Part 2

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) :

[Fig 17.The final association form]

Page 83: Step by Step Tutorial - Part 2

Add the following control declaration in WFAssoc.aspx (get the code from snippet1.txt in the startup files).

<%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %> <%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %><%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

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).

Page 84: Step by Step Tutorial - Part 2

by implementing FromPageToAssocData(), you will transfer your association data from the association form to the workflow association.(snippet5.txt).

Now you can rebuild the project, your workflow will be deployed and you can test it on a list, on a content type and on a content type on a list.

 

Part 3.Inside Association Forms

 

The concept is pretty simple: when you add a workflow association, the Sharepoint framework provides a first generic page: AddWrkfl.aspx (Fig 19).

 

[Fig 19.Generic workflow association page (AddWrkfl.aspx)]

The query string parameters will depend on the association type.Association on a Content type:

query string :  ctype=<cont typeid>

[Fig 20.Query string generated by the association to a Content type]

Association on a List:

query string: List=<list id>

Page 85: Step by Step Tutorial - Part 2

[Fig 21.Query string generated by the association to a list]

 

Association on a Content type in a List :

query string: ctype=<content typeid>&List=<list id>

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).

 

Page 86: Step by Step Tutorial - Part 2

[Fig 22.variables generated in the context of the association of a workflow with a content type]

Page 87: Step by Step Tutorial - Part 2

[Fig 23. variables generated in the context of the association of a workflow with a list or with a content type in a specific list]

If you click on the Next button, the association form specified in your feature manifest file (here workflow.xml, (fig 8) will show up .

When you click on the Ok button, you will have to create the association by using one of the following methods of the SPWorkflowAssociation class :

 

public static SPWorkflowAssociation CreateListAssociation

(    SPWorkflowTemplate baseTemplate,    string name,    SPList taskList,    SPList historyList)

public static SPWorkflowAssociation CreateSiteContentTypeAssociation

(    SPWorkflowTemplate baseTemplate,    string name,    string strTaskList,

Page 88: Step by Step Tutorial - Part 2

    string strHistoryList)

public static SPWorkflowAssociation CreateListContentTypeAssociation

(    SPWorkflowTemplate baseTemplate,    string name,    SPList taskList,    SPList historyList

)

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

Page 89: Step by Step Tutorial - Part 2

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 :

 

WorfklowAssociationContext<TA> WorkflowInitiationContext<TI, TA>

 

1.Use the existing project in the startup folder

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).

Page 90: Step by Step Tutorial - Part 2

[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).

Page 91: Step by Step Tutorial - Part 2

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