Walkthrough/Solution for Workflow Error in SharePoint 2010 State Machine Workflow: Event “OnWorkflowActivated” on interface type “Microsoft.SharePoint.Workflow.ISharePointService” for instance id “” cannot be delivered.

Today I got the following error and did not find a clear description and solution for it:

image

Error message: “Event “OnWorkflowActivated” on interface type “Microsoft.SharePoint.Workflow.ISharePointService” for instance id “<guid>” cannot be delivered.”

 

The Solution: …in my case was a missing “EventDriven” activity.

My “Initialization” state looks like this:

image

I’ve added a “StateInitialization” Workflow Activity and added some code to it. But this is a SharePoint Workflow and it needs at least an “OnWorkflowActivated” event driven activity.

Let’s walk through the solution:

1. Drag a “EventDriven” activity to the “Init” state. Its name may defer in your project.

image

2. Name the EventDriven activity “onWorkflowActivatedHandler”. (You can use an other name too!)

3. Double click the EventDriven activity.

4. Drag a “OnWorkflowActivated” activity from the Toolbox pane into the “onWorkflowActivatedHandler” activity:

image

5. Add a correlation token to “onWorkflowActivated1”!!

6. Add a binding for “WorkflowProperties” !!!

image

7. That’s it. Now you can add activities behind “handleExternalEventActivity1”… As you like.

Walkthrough: Add List Event Receiver dynamically at runtime in SharePoint 2010

This time a tiny neat walkthrough of how to add an Event Receiver at runtime in SharePoint 2010.

Let’s say you have a SharePoint site that your colleagues already use. In this site you have an existing list. Now you want to add some automation to this existing list. – You cannot deploy the list as List Definition w/ List Instance again in a VS 2010 SharePoint project, because the list exists and the data must not be touched.

One solution is to add an List Event Receiver that is contained in a VS2010 solution package.

1. You create a Empty SharePoint 2010 project in Visual Studio 2010.

2. Now you add an “Event Receiver” project item

image

3. Now you add the events you want to handle. Select “List Item Events” and “Custom List”.

image

4. Implement some functionality in the newly created Event Receiver class.

5. Now create or open an Feature Event Receiver for the SharePoint feature that will configure the event receiver. – You have to create a new feature or use an existing feature… If you create a new feature event receiver you have to uncomment the methods “FeatureActivated” and “FeatureDeactivating”.

6. Add this code to the “FeatureActivated” method:

try {
    SPWeb web = (SPWeb)properties.Feature.Parent;
    SPList l = web.Lists["My SharePoint List"];
    if( l != null )
    {
        bool found = false;
        foreach( SPEventReceiverDefinition er in l.EventReceivers )
        {
             if( er.Class == "Full.Namespace.Qualified.Class.Name.Of.Your.Event.Receiver.Class")   {
                 found = true;
                 break;
             }
        }

        if( !found )
        {
            SPEventReceiverDefinition newERD = l.EventReceivers.Add();

//the next line is only valid if the event receiver class is in the same assembly as the feature event receiver!!!

            newERD.Assembly = System.Reflection.Assembly.GetExecutingAssembly().FullName;
            newERD.Class = "Full.Namespace.Qualified.Class.Name.Of.Your.Event.Receiver.Class";
            newERD.SequenceNumber = 1000;

//you may add more “received” events in the following line.

            newERD.Type = SPEventReceiverType.ItemUpdated | SPEventReceiverType.ItemAdded;
            newERD.HostId = l.ID;
            newERD.HostType = SPEventHostType.List;
            newERD.Update();
            l.Update();
        }
    }
}
catch {
}

This installs the event receiver when the feature gets activated.

7. Add this code to the “FeatureDeactivating” method:

try {
    SPWeb web = (SPWeb)properties.Feature.Parent;
    SPList l = web.Lists["My SharePoint List"];
    if( l != null )
    {
        SPEventReceiverDefinition d = null;
        foreach( SPEventReceiverDefinition er in l.EventReceivers )
        {
            if( er.Class == "Full.Namespace.Qualified.Class.Name.Of.Your.Event.Receiver.Class" )
            {
                d = er;
                break;
            }
        }

        if( d != null )
        {
            d.Delete();
            l.Update();
        }
    }
}
catch {
}

This will remove the event receiver when the feature gets deactivated.

8. Now remove the “Elements.xml” file in the Event Receiver project item in the Solutions Explorer:

image

9. For me this works very well.

Problem and Workaround: Organization Browser Silverlight Web Part is empty on other site than “MySite”

At first: This is a common problem. Using the "Organization Browser" Web Part on another page than on the MySite host web application results in an empty view. This means, the "Organization Browser" does not have any content. – As I said: This is a common problem.

First step to fix this is to create a “clientaccesspolicy.xml” file in the IIS directories of the SharePoint Web Applications.

See this blog post of Adam Preston:

http://www.tcscblog.com/2011/04/11/using-the-sharepoint-2010-organization-browser-in-another-web-application/

BUT:

In my current case it remains empty!!!

I used Fiddler to analyse the problem.

The Silverlight App “Organization Browser” executes a Web Service request but the response is empty. Not like an error but the Web Service does not find any data for the given account. Please see this screenshot for the request and its response:

image

I modified the request in Fiddler and removed the claim info “i:0#.w|” in the request. – And now it works. The Web Service does respond correct data!!!

image

I checked the authentication mode of both sites:

The MySite Web Application uses “Classic Authentication” and the Web Application from within I call the Organization Browser App is “Claims Based Authentication”. This results in bad request data for the Web Service. The “Claims Based” Web Application sends the user name in “claim format” but the MySite Web App cannot handle it. So I have to migrate the MySite Web App to Claims Based Authentication.

For the Migration of the MySite Web App from Classic Authentication to Claims Based Authentication I’ve written this script:

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") 


######################################################################################################

    
  $url = "http://personal.sharepoint.local"
  $webapp = Get-SPWebApplication $url -ErrorAction SilentlyContinue
  if( $webapp -ne $null) {
     Write-Host "Web Application: $($webapp.Url)"
  
     Write-Host "  Active Claim Based Authentication"
     $webapp.UseClaimsAuthentication = "TRUE"
     Write-Host "  Update Web Application"
       $webapp.Update()
     Write-Host "  Provisioning Web Application"
       $webapp.ProvisionGlobally()
  
     #Claims Migration

     Write-Host "  Set Authentication Provider"
     $webapp = Get-SPWebApplication $url -ErrorAction SilentlyContinue
     Set-SPwebApplication $webapp -AuthenticationProvider (New-SPAuthenticationProvider) -Zone Default

     Write-Host "  Migrate Users to Claim Based Authentication"
     $webapp = Get-SPWebApplication $url -ErrorAction SilentlyContinue
     $webapp.MigrateUsers($true)
  }

After that I realized that the personal site collection does not have correct Site Collection Admin settings any more: There the original “Classic Mode” users are registered not the “Claim” user (login) names.

I’ve written this script to fix this:

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") 


######################################################################################################

    
  $url = "http://personal.sharepoint.local"

  $webapp = Get-SPWebApplication $url -ErrorAction SilentlyContinue
  if( $webapp -ne $null) {
     $sites = Get-SPSite -Limit all
     $sites | % {
         $site = $_
        if( $site.Url.StartsWith("http://personal.sharepoint.local/sites/domain_", [System.StringComparison]::InvariantCultureIgnoreCase) ){
            Write-Host "$($_.Url)" -ForegroundColor Green
            $site.RootWeb.SiteUsers | ? { $_.IsSiteAdmin } | % {
                $user = $site.RootWeb.EnsureUser("i:0#.w|" + $_.LoginName)
                $user.IsSiteAdmin = $true
                $user.update()
            }
        } else {
            Write-Host "$($_.Url)" -ForegroundColor Red
        }
     }
  }

BUT: It does not work eighter Trauriges Smiley!!!

It seems to be a known limitation of the Organization Browser not to work at “Claims Authentication” enabled Web Applications.

BUT: I could create a wolkaround for this!!!

You need to edit the page where you want to use the “Organization Browser” in SharePoint Designer 2010 in Advanced Mode. – In my case I created a new Page Layout for my page derrived from the Page Layout “Welcome Links – Table Of Content”. In this case I modified this custom Page Layout.

This is the JavaScript code including the Content Placeholder ASP.NET tag for the code:

<asp:Content ContentPlaceHolderID="PlaceHolderUtilityContent" runat="server">
    <script type="text/javascript">
        var oldCreateHierarchyChartControl = CreateHierarchyChartControl;

        function CreateHierarchyChartControl(parentId, profileId, type) {
            var i = profileId.indexOf("|");
            //alert(i);
            if(i >=0 )
               profileId = profileId.substr(i+1,profileId.length-i-1);
            //alert(profileId);

                    var initParam = profileId + ',' + type;
                    var host = document.getElementById(parentId);

                    host.setAttribute('width', '100%');
                    host.setAttribute('height', '100%');

                    Silverlight.createObject('/_layouts/ClientBin/hierarchychart.xap',
                                            host,
                                            'ProfileBrowserSilverlightControl',
                                            {
                                                top: '30',
                                                width: '100%',
                                                height: '100%',
                                                version: '2.0',
                                                isWindowless: 'true',
                                                enableHtmlAccess: 'true'
                                            },
                                            {
                                                onLoad: OnHierarchyChartLoaded
                                            },
                                            initParam,
                                            null);

        }
    </script>
</asp:Content>

I’ve inserted this JavaScript code that overrides a JavaScript function created by the “Organization Browser” SharePoint Web Control. – This customized function removes the “Claim part” of the user name that is send to the Web Server by the Silverlight Application.

NOW IT WORKS!!! SmileySmileySmiley – On the Claim Authentication based Web Application the “Organization Browser” can be used!!!

Error while publishing an InfoPath 2010 form to SharePoint: “The following URL is not valid”

Today I got this error:

image

I tried to deploy a simple form to the SharePoint location http://sharepoint.local. – This location is a root site collection!!!

I got the error:

The following URL is not valid: “http://sharepoint.local”

I swear: It is valid!!! Smiley

 

The URL uses a host header name. Specifying the Central Administration URL (using the machines name) did work at the same time.

Than I activated the network adapter of the virtual machine to have Google available in the machine to research into the problem. – In this moment the problem was gone! – I turned off the network adapter and the problem was back. – OK. That’s it. – Deactivating the “System Event Notification Service” (net stop sens at command line) solved the problem. But you should restart the service as soon as possible!

How to deploy conditional formatting in a SharePoint 2010 list definition using Visual Studio 2010

This time it’s not a walkthrough. Only a description of what you have to do. – It’s “experimental”!!!

The need is to deploy a conditional formatting in a list definition that was created in a SharePoint 2010 Visual Studio (2010) project.

1. You need to design the list definition. Create a list instance for the list definition. This instance you can remove later if you want.

2. Deploy the project. It’s without conditional formatting at this point.

3. Create the conditional formatting in SharePoint Designer.

Open the list instance.

Open the list view you want to modify.

Select the cells that should have a conditional formatting.

Create the conditional formatting.

4. Open the “Code” view of the list view page.

5. Look for the “<xsl>” tag of the XsltListViewWebPart that renders the list data.

Copy the content of the <xsl> tag.

6. In Visual Studio open the “Schema.xml” file of the list definition.

7. In the <views> tag look for the view you want to modify. E.g. “AllItems.aspx”. Look for the “Url” attribute of the view tag that contains the Web Part Page name (e.g. “AllItems.aspx”).

8. Before the closing “view” tag add this:

        <Xsl>
              <![CDATA[
              ...
              ]]>
        </Xsl>

Replace the “…” through the copied content of the web part pages “xsl” tag content.

9. Deploy the project.

10. It’s done! – BUT: You may be unable to edit the conditional formatting in SharePoint Designer! As I said: It’s experimental.

Walkthrough: Create SharePoint 2010 Workflow Association Form and Initiation Form in Visual Studio 2010 by using Application Pages (Part 2 of 2)

This is Part 2 of 2 that describes the steps 18 to 27 . You will find Part 1 here: https://blog.kenaro.com/2011/04/24/walkthrough-create-sharepoint-2010-workflow-association-form-and-initiation-form-in-visual-studio-2010-by-using-application-pages-part-1-of-2/

The followings steps describe how to create a new sequential workflow in a Visual Studio 2010 project and associate a Initiation and Association Form with the workflow. Furthermore we will test the project. – In Part 1 we have created:

  • a Workflow Task list definition: “Workflow 2 Tasks”
  • a Workflow Host List definition: “Workflow 2 Host List”
  • a Data class: “Workflow2Data”
  • a base class "Workflow2DataPages” for the association and initiation form.
  • a Association Form
  • a Initiation Form.

Now we will go on.

Let’s start…

18. Deploy the project Smile – This will create  the list instances we need to create the workflow and assign them to lists for debug purpose. Of course you do not need to assign lists (such as Workflow Task List, History List, …) to the workflow at design time in Visual Studio. You could do this later, e.g. in a Feature Receiver.

19. Add a “Sequential Workflow” project item named “Workflow 2”.

image

Set “Workflow 2” as display name and “List workflow” as type.

image

Set “Workflow 2 Host List” as “library or list to associate with”, choose “Workflow History” in the second dropdown and choose “Workflow 2 Tasks” as list for “workflow tasks” in the third dropdown.

image

Now choose only “Start manually” in the next dialog.

image

20. Now the workflow designer opens.

image

Double click on “onWorkflowActivated1”. A new class member will be created in the workflows “codebehind” file.

Now add the following “usings” at the beginning of the file:

using System.IO;
using System.Xml;
using System.Xml.Serialization;

Above the previously created class member “onWorkflowActivated1_Invoked” add the following code. This will create a dependency property that will be persisted in the workflows data.

(You can use Visual Studio IntelliSense: type “propdp” and press “tab” two times. Then the structure of a new dependency property will be created.)

public Workflow2Data WorkflowDataAssociation
{
    get
    {
        return (Workflow2Data)GetValue(WorkflowDataAssociationProperty);
    }
    set
    {
        SetValue(WorkflowDataAssociationProperty, value);
    }
}

// Using a DependencyProperty as the backing store for WorkflowDataAssociation.  
//   This enables animation, styling, binding, etc...
public static readonly DependencyProperty WorkflowDataAssociationProperty =
    DependencyProperty.Register("WorkflowDataAssociation", typeof(Workflow2Data), typeof(Workflow_2));
 

Now add another dependency property of the “Initiation” data:

public Workflow2Data WorkflowDataInitiation
{
    get
    {
        return (Workflow2Data)GetValue(WorkflowDataInitiationProperty);
    }
    set
    {
        SetValue(WorkflowDataInitiationProperty, value);
    }
}

// Using a DependencyProperty as the backing store for WorkflowDataInitiation.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty WorkflowDataInitiationProperty =
    DependencyProperty.Register("WorkflowDataInitiation", typeof(Workflow2Data), typeof(Workflow_2));
 

We need to do this because Association and Initiation data are stored separatly.

In the method “onWorkflowActivated1_Invoked” enter this code:

using( StringReader stringReader = new StringReader(workflowProperties.AssociationData) )
{
    using( XmlReader reader = XmlReader.Create(stringReader) )
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Workflow2Data));
        WorkflowDataAssociation = (Workflow2Data)serializer.Deserialize(reader);
    }
}

using( StringReader stringReader = new StringReader(workflowProperties.InitiationData) )
{
    using( XmlReader reader = XmlReader.Create(stringReader) )
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Workflow2Data));
        WorkflowDataInitiation = (Workflow2Data)serializer.Deserialize(reader);
    }
}

This will deserialize the workflows association data for using them inside the workflows code.

21. Now we need tho modify the “Elements.xml” file of the “Workflow 2” project item.

We add the attribute “AssociationUrl” and set it’s value to “Workflow2Forms/Workflow2AssociationForm.aspx” and add the attribute “InstantiationUrl” with value “Workflow2Forms/Workflow2InitiationForm.aspx”.

image

22. Now we can deploy the project and see the first results of our (hard) work.

23. Open the URL “http://sharepoint.local/sites/workflow/Lists/Workflow2HostList” in the browser.

Then open the “List” tab in the Ribbon and click “Workflow Settings”.

Now you should see the associated “Workflow 2”:

image

Click them.

At the bottom of the next page you will find the “Next” button that will open the association form. Click this button.

You see your association form page:

image

Super! – Press “OK”.

24. Now we add a new list item to the “Workflow 2 Host List” and start the “Workflow 2” manually:

image

“OK”.

image

Choose “Workflows” from the context menu of the created list item.

image

Click “Workflow 2”.

image

You see your Initiation Form!!

Click “OK” and the workflow will start – and will be “Completed”.

25. Let’s define a Code Activity to use the initiation data.

In the workflow designer drag a “LogToHistoryListActivity” activity from the Toolbox pane into the Workflow designer view of “Workflow 2” and drop it behind “onWorkflowActivated1”.

image

Now select the “logToHistoryListActivity1”. In the Properties pane select the property “HistoryDescription” and click the button beside the edit box of this property. In the upcoming dialog we will bind property “Data1” of the WorkflowDataAssociation object to the activities property:

image

Click “OK”.

Now we add a “Delay” activity behind “logToHistoryListActivity1” and set the delay “TimeDuration” to “00:01:00”. This forces the workflow to delay the execution of the next activities for the defined amount of time.

Now we add another “LogToHistoryListActivity” and bind it’s “HistoryDescription” property to “WorkflowDataInitiation.Data3” as described above.

image

(The workflow after adding the two activities)

image

(Properties of the “delayActivity1” activity.)

 image

(Properties of the “logToHistoryListActivity2”.)

26. Deploy the project and open the “Workflow 2 Host List” in the browser. Modify the association of the “Workflow 2” of the list and enter some information on the association form:

image

Now add an item to the list, start the “Workflow 2” on this item and enter some data in the initiation form:

image

After the workflow starts the information stored in “Data1” you entered in the association form will be written to the workflows history:

image

After about 1 minute the workflow will add another workflow history entry with the content of the “Data3” property entered in the initiation form:

image

27. That’s it! – Everything works as expected. – Thanks for reading! –

Please post your comments or question!

Walkthrough: Create SharePoint 2010 Workflow Association Form and Initiation Form in Visual Studio 2010 by using Application Pages (Part 1 of 2)

Because of some requests by my blog readers I’d like to show you how to create Workflow Association and Initiation (“Instantiation”) forms in Visual Studio 2010 for use with a sequential or state machine workflow in SharePoint 2010.

This is Part 1 of 2 that describes the steps 1 to 17. You will find Part 2 here: https://blog.kenaro.com/2011/04/24/walkthrough-create-sharepoint-2010-workflow-association-form-and-initiation-form-in-visual-studio-2010-by-using-application-pages-part-2-of-2/

For this demo I’ll use my sample project on Codeplex: http://spworkflowdemo.codeplex.com/

There I’ve shown how to create Workflow Task Forms with Visual Studio. See my blog posts on this topic: Part 1, Part 2 and Part 3.

To follow this walkthrough you need to create a blank SharePoint 2010 site collection. I used this URL for the site collection: “http://sharepoint.local/sites/workflow”.

The code of this walkthrough is published in the demo project at Codeplex. If you would like to follow the walkthrough step by step you can start with the demo projects source code bundled in release 0.1.0.0. Or you just download the package 0.2.x.0 where the complete (working) code is included.

If you have difficulties to deploy the project in Visual Studio 2010 please read the “Part 3” post of my previous blog series.

There is a article on MSDN that I used years ago to do this for MOSS. The code of this article is adapted from the MSDN article. I can’t find them at the moment. I’ve modified the code and “upgraded” them form SPS2010.

Let’s start…

1. You need to have a site collection “http://sharepoint.local/sites/workflow”.

2. Now start Visual Studio 2010 and open the downloaded package of my Codeplex hosted project http://spworkflowdemo.codeplex.com/

Use project version 0.1.0.0 !!! Deploy the project. If you have difficulties to deploy it please read this blog post:

3. In this walkthrough we will create a sequential workflow named “Workflow 2”.

3. Now we add a new Task List for our workflow. Add a new “List Definition” project item named “Workflow 2 Tasks”.

image

Click “Add”.

In the next step select “Task” as base type for the List Defintion and check “Add a list instance”.

image

In the “Schema.xml” file of the create list definition edit the “List” tag at the beginning of the file: Add the attribute “Type” with Value “107”.

Modify the “ContentTypes” tag:

    <ContentTypes>
      <ContentTypeRef ID="0x010801">
      </ContentTypeRef>
    </ContentTypes>
This references to the “Workflow Task” content type.
Now edit the “Elements.xml” file of the List Definition (not the “Elements.xml” file of the List Instance!).
Change the “Type” attribute of the “ListTemplate” tag to “107”. You may change the “Description” attribute.
Now change the “Elements.xml” file of the List Instance.
Change the “Title” attribute of the “ListInstance” tag to “Workflow 2 Tasks”.
Furthermore change the “TemplateType” attribute to “107” and the “Url” attribute to “Lists/Workflow2Tasks”.

4. Now we ass a new “host list” for the workflow. Our workflow will be associated with this list. It’s a simple list of type “Custom List” with no column modifications.

Add  a new “List Definition” project item named “Workflow 2 Host List”.

image

Click “Add”.

Now choose “Custom List” and change the display name. Be sure to choose “Add list instance…”.

image

Click “Finish”.

You may change the “Elements.xml” of the List Instance. Modify the “Title” and “Url” attributes of the “ListInstance” tag.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ListInstance Title="Workflow 2 Host List"
                OnQuickLaunch="TRUE"
                TemplateType="10001"
                Url="Lists/Workflow2HostList"
                Description="My List Instance">
  </ListInstance>
</Elements>

No further changes have to be done for now.

5. Add a new module project item “Module” named “Workflow 2 Forms”. There we will store our Initiation and Association forms.

image

Click “Add”.

First remove the “Sample.txt” file from the module.

Than modify the “Elements.xml” file of the module. Add a “URL” attribute to the “Module” tag and set it’s value to “Workflow2Forms”. Add a “RootWebOnly” attribute to the “Module” tag and set the value to “False”.

6. Now add a new project item of type “Class” to the module. Name the class “Workflow2Data” and mark them as “public”. This class will contain the data the users have to enter on Association or Initiation form.

image

Modify the namespace of the new class file. It should be “ik.SharePoint2010.Workflow”.

Add the Attribute “[Serializable()]” to the class definition. (The entered association and initiation data will be serialized as XML in the SharePoint database.)

image

Add this “using” lines at top of the file:

using System.IO;
using System.Xml.Serialization;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now add four public string properties to this class. Name them “Data1” … “Data4”. Use this template for all of the properties:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

         private string _dataX = default(string);
 
         public string DataX
         {
             get
             {
                 return _dataX;
             }
             set
             {
                 _dataX = value;
             }
         }

All of the “DataX” properties will be used in association form, but only “Data3” and “Data4” will be editable in initiation form too!

Furthermore we need a static “DeserializeFormData” method:

         public static Workflow2Data DeserializeFormData(string xmlString)
         {
             using( MemoryStream stream =    new MemoryStream(Encoding.UTF8.GetBytes(xmlString)) )
             {
                 XmlSerializer serializer = new XmlSerializer(typeof(Workflow2Data));
                 Workflow2Data data = (Workflow2Data)serializer.Deserialize(stream);
                 return data;
             }
         } 

7. Now we add another “Class” project item to the module named “Workflow2DataPages”. This class have to be “public”. This will become the base class of the Association Form and Initiation Form ASPX’s code behind class.

image

Set the namespace of the class to “ik.SharePoint2010.Workflow”.

Set the base class to “LayoutsPageBase”.

image

Replace all the “usings” at the top of the file through this:

using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.WebControls;

using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.Xml.Serialization;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.Security;
using System.Security.Permissions;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Now add an enumeration to the class definition:

public enum FormType
{
    Association,
    Initiation
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Add the following field class members. You will need them on every association or instantiation form you may create.

protected SPList list;
protected SPContentType contentType;
protected string workflowName;
protected HyperLink hlReturn;
protected string requestQueryString;
protected bool useContentTypeTemplate = false;

Now add some special field class members that are belong to the current workflow context. Here we add four TextBoxes for the “Data” properties that we will enter in our association or instantiation forms:

protected TextBox textboxData1;
protected TextBox textboxData2;
protected TextBox textboxData3;
protected TextBox textboxData4;

8. In “Workflow2DataPages.cs” add the method “OnLoad”:

 protected override void OnLoad(EventArgs e)
 {
     base.OnLoad(e);
 
     EnsureRequestParamsParsed();
 
     SPBasePermissions perms = SPBasePermissions.Open | SPBasePermissions.ViewPages;
     if( useContentTypeTemplate )
         perms |= SPBasePermissions.AddAndCustomizePages;
     else
         perms |= SPBasePermissions.ManageLists;
 
     Web.CheckPermissions(perms);
 }
 

This code will parse the query string part of the URL and check the users permissons.

The following Method is called by “OnLoad” and parses the query string part of the URL:

 protected void EnsureRequestParamsParsed()
 {
     workflowName = Request.Params["WorkflowName"];
 
     string strListID = Request.QueryString["List"];
     string strCTID = Request.QueryString["ctype"];
 
     if( strListID != null )
         list = Web.Lists[new Guid(strListID)];
 
     if( strCTID != null )
     {
         requestQueryString = "ctype=" + strCTID;
 
         if( list != null )
         {
             requestQueryString += "&List=" + strListID;
             contentType = list.ContentTypes[new SPContentTypeId(strCTID)];
         }
         else
         {
             contentType = Web.ContentTypes[new SPContentTypeId(strCTID)];
             useContentTypeTemplate = true;
         }
     }
     else
         requestQueryString = "List=" + strListID;
 }
 

9. The next method we will add to “Workflow2DataPages” is used for deserializing the workflows initiation or association data from the SharePoint objects.

 internal void PopulatePageFromXml(string associationXml, FormType type)
 {
     Workflow2Data Workflow2Data = new Workflow.Workflow2Data();
     if( !string.IsNullOrEmpty(associationXml) )
     {
         XmlSerializer serializer = new XmlSerializer(typeof(Workflow2Data));
         XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(associationXml));
         Workflow2Data = (Workflow2Data)serializer.Deserialize(reader);
     } 
     /* ikarstein: Start your modifications here */
     if( type == FormType.Association )
     {
         textboxData1.Text = Workflow2Data.Data1;
         textboxData2.Text = Workflow2Data.Data2;
     }
 
     textboxData3.Text = Workflow2Data.Data3;
     textboxData4.Text = Workflow2Data.Data4;
/* ikarstein: End of Modifications */ }
  

As you can see the method will use a XmlSerializer object to deserialize the data stored in the parameter named “associationXml”. The method sets the values of the four TextBoxes that will be used to enter data in the browser.

10. Now we add a method for serializing the workflow data after the user has modified them in the browser.

 internal string SerializePageToXml(FormType type)
 {
     Workflow2Data data = new Workflow2Data();
     /* ikarstein: Start your modifications here */
     if( type == FormType.Association )
     {
         data.Data1 = textboxData1.Text;
         data.Data2 = textboxData2.Text;
     }
 
     data.Data3 = textboxData3.Text;
     data.Data4 = textboxData4.Text;
     /* ikarstein: End of Modifications */
 
     using( MemoryStream stream = new MemoryStream() )
     {
         XmlSerializer serializer = new XmlSerializer(typeof(Workflow2Data));
         serializer.Serialize(stream, data);
         stream.Position = 0;
         byte[] bytes = new byte[stream.Length];
         stream.Read(bytes, 0, bytes.Length);
         return Encoding.UTF8.GetString(bytes);
     }
 }
  

11. At least we add the following method to “Workflow2DataPages.cs”:

 [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
 protected override void OnPreInit(EventArgs e)
 {
     base.OnPreInit(e);
 
     string customMasterUrl = SPControl.GetContextWeb(this.Context).MasterUrl; //~masterurl/default.master
     this.MasterPageFile = customMasterUrl;
 }
  

12. Now we can start creating the Association Form ASPX page.

In the Visual Studio project add a new “Application Page” project item named “Workflow2AssociationForm.aspx”.

image

This item will be created inside the “Layoutsik.SharePoint2010.Workflow” folder. You have to move them from there into the module named “Workflow 2 Forms”. Do this by Drag’n’Drop in the Solution Explorer pane.

image

Now edit the ASPX page.

Modify the “Page” tag:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Workflow2AssociationForm.aspx.cs" 
Inherits="ik.SharePoint2010.Workflow.Workflow2AssociationForm"
MasterPageFile="~masterurl/default.master" %>

As you can see we need to replace the “DynamicMasterPageFile” attribute through “MasterPageFile” and change the “Inherits” attribute by modifying the classes full name. (Below we will change the namespace of the generated class file.)

Now we need to add some “Register” tags to register some SharePoint controls for using them in the site.

<%@ Register TagPrefix="wssuc" TagName="LinksTable" src="/_controltemplates/LinksTable.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="LinkSection" src="/_controltemplates/LinkSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ActionBar" src="/_controltemplates/ActionBar.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ToolBar" src="/_controltemplates/ToolBar.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" src="/_controltemplates/ToolBarButton.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="Welcome" src="/_controltemplates/Welcome.ascx" %>

You can remove the content placeholder named “PlaceHolderAdditionalPageHead”.

The following ASP.NET code we add to the content placeholder named “PlaceHolderPageTitle”:

    <asp:Literal ID="Literal1" runat="server" Text="Customize Workflow" />

The following ASP.NET code we add to the content placeholder named “PlaceHolderPageTitleInTitleArea”:

    <%
        string strPTS = "Customize " + workflowName;
        SPHttpUtility.HtmlEncode(strPTS, Response.Output);
    %>
    :<asp:HyperLink ID="hlReturn" runat="server" />

The following ASP.NET code we add to the content placeholder named “PlaceHolderPageImage”:

    <img src="/_layouts/images/blank.gif" width="1" height="1" alt="" />
The following ASP.NET code we add to the content placeholder named “PlaceHolderPageDescription”:
    <%
        string strPD = "Use this page to customize this instance of " + workflowName + ".";
        SPHttpUtility.HtmlEncode(strPD, Response.Output);
    %>

Now we add a ASP.NET table control and some more content to the content placeholder named “PlaceHolderMain”. Inside this snipped the TextBoxes are defined that the user will use later while editing the association data.

<asp:Table CellSpacing="0" CellPadding="0" BorderWidth="0" CssClass="ms-propertysheet">
    <wssuc:InputFormSection Title="Workflow Data Values" Description="Specify the default workflow data values." runat="server">
        <template_inputformcontrols>
            <wssuc:InputFormControl Runat="server" LabelText="Specify Form Data Values:">
                <Template_Control>
                    <table border="0" cellspacing="0" cellpadding="0">
                        <!-- ikarstein: Start your modifications here -->
                        <tr>
                            <td class="ms-authoringcontrols">
                                Data 1: <asp:TextBox id="textboxData1" runat="server"></asp:TextBox>
                            </td>
                        </tr>
                            <tr>
                            <td class="ms-authoringcontrols">
                                Data 2: <asp:TextBox id="textboxData2" runat="server"></asp:TextBox>
                            </td>
                            </tr>
                            <tr>
                            <td class="ms-authoringcontrols">
                                Data 3: <asp:TextBox id="textboxData3" runat="server"></asp:TextBox>
                            </td>
                            </tr>
                            <tr>
                            <td class="ms-authoringcontrols">
                                Data 4: <asp:TextBox id="textboxData4" runat="server"></asp:TextBox>
                            </td>
                            </tr>
                        <!-- ikarstein: End of modifications -->
                    </table>
                </Template_Control>
            </wssuc:InputFormControl>
        </template_inputformcontrols>
    </wssuc:InputFormSection>
        
    <input type="hidden" name="WorkflowDefinition" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["WorkflowDefinition"]),Response.Output); %>'/>
    <input type="hidden" name="WorkflowName" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["WorkflowName"]),Response.Output); %>'/>
    <input type="hidden" name="AddToStatusMenu" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AddToStatusMenu"]),Response.Output); %>'/>
    <input type="hidden" name="AllowManual" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AllowManual"]),Response.Output); %>'/>
    <input type="hidden" name="RoleSelect" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["RoleSelect"]),Response.Output); %>'/>
    <input type="hidden" name="GuidAssoc" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["GuidAssoc"]),Response.Output); %>'/>
    <input type="hidden" name="SetDefault" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["SetDefault"]),Response.Output); %>'/>
    <input type="hidden" name="HistoryList" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["HistoryList"]),Response.Output); %>'/>
    <input type="hidden" name="TaskList" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["TaskList"]),Response.Output); %>'/>
    <input type="hidden" name="UpdateLists" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["UpdateLists"]),Response.Output); %>'/>        
    <input type="hidden" name="AutoStartCreate" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AutoStartCreate"]),Response.Output); %>'/>
    <input type="hidden" name="AutoStartChange" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AutoStartChange"]),Response.Output); %>'/>
        
    <wssuc:ButtonSection runat="server">
        <template_buttons>
            <asp:PlaceHolder runat="server">
                <asp:Button runat="server" class="ms-ButtonHeightWidth" OnClick="BtnOK_Click" Text="OK" id="btnOK" />
            </asp:PlaceHolder>
        </template_buttons>
    </wssuc:ButtonSection>
</asp:Table>

(I’ve marked the section you should modify for your special needs with HTML comments…)

13. Now we modify the “code behind” for Workflow2AssociationForm.aspx. – Open “Workflow2AssociationForm.aspx.cs”. Replace the content with this code:

 using System;
 using Microsoft.SharePoint;
 using Microsoft.SharePoint.WebControls;
 using Microsoft.SharePoint.Utilities;
 using Microsoft.SharePoint.Workflow;
 using System.Web.UI.WebControls;
 using Microsoft.SharePoint.Security;
 using System.Security.Permissions;
 using System.Web;
 
 namespace ik.SharePoint2010.Workflow
 {
     public partial class Workflow2AssociationForm : Workflow2DataPages
     {
         protected struct WorkflowOptions
         {
             public string taskListName;
             public string historyListName;
             public Guid taskListId;
             public Guid historyListId;
 
             public bool allowManual;
             public bool autoStartCreate;
             public bool autoStartChange;
 
             public bool setDefault;
             public bool updateLists;
             public bool lockItem;
         }
 
         protected WorkflowOptions workflowOptions = new WorkflowOptions();
 
         protected SPWorkflowTemplate baseTemplate;
         protected SPWorkflowAssociation associationTemplate;
         protected HyperLink hlReturn;
 
         protected override void OnPreRender(EventArgs e)
         {
             base.OnPreRender(e);
             if( associationTemplate != null )
                 PopulatePageFromXml((string)associationTemplate.AssociationData, FormType.Association);
         }
 
         protected override void OnLoad(EventArgs ea)
         {
             base.OnLoad(ea);
 
             //Get the Workflow Name.
             FetchAssociationInfo();
             GetTaskAndHistoryList();
         }
 
         private void FetchAssociationInfo()
         {
             SPWorkflowAssociationCollection wfAccociationCollection;
             baseTemplate = Web.WorkflowTemplates[new Guid(Request.Params["WorkflowDefinition"])];
             associationTemplate = null;
 
             if( contentType != null )
             {
                 // Associating with a content type.
                 wfAccociationCollection = contentType.WorkflowAssociations;
                 hlReturn.Text = contentType.Name;
                 hlReturn.NavigateUrl = "ManageContentType.aspx" + requestQueryString;
             }
             else
             {
                 list.CheckPermissions(SPBasePermissions.ManageLists);
 
                 wfAccociationCollection = list.WorkflowAssociations;
                 hlReturn.Text = list.Title;
                 hlReturn.NavigateUrl = list.DefaultViewUrl;
             }
             if( wfAccociationCollection == null || wfAccociationCollection.Count < 0 )
             {
                 throw new SPException("No Associations Found");
             }
 
             workflowOptions.autoStartCreate = ( Request.Params["AutoStartCreate"] == "ON" );
             workflowOptions.autoStartChange = ( Request.Params["AutoStartChange"] == "ON" );
             workflowOptions.allowManual = ( Request.Params["AllowManual"] == "ON" );
             workflowOptions.lockItem = ( Request.Params["LockItem"] == "ON" );
             workflowOptions.setDefault = ( Request.Params["SetDefault"] == "ON" );
             workflowOptions.updateLists = ( Request.Params["UpdateLists"] == "TRUE" );
 
             string associationGuid = Request.Params["GuidAssoc"];
             if( associationGuid != string.Empty )
             {
                 associationTemplate = wfAccociationCollection[new Guid(associationGuid)];
             }
 
             SPWorkflowAssociation checkForDuplicateTemplate = wfAccociationCollection.GetAssociationByName(workflowName, Web.Locale);
 
             if( checkForDuplicateTemplate != null && ( associationTemplate == null || associationTemplate.Id != checkForDuplicateTemplate.Id ) )
             {
                 throw new SPException("Duplicate workflow name is detected.");
             }
         }
 
         private void GetTaskAndHistoryList()
         {
             if (useContentTypeTemplate)
             {
                 workflowOptions.taskListName = Request.Params["TaskList"];
                 workflowOptions.historyListName = Request.Params["HistoryList"];
             }
             else
             {
                 string taskListGuid = Request.Params["TaskList"];
                 if (taskListGuid[0] != ) // already existing list
                 {
                     workflowOptions.taskListId = new Guid(taskListGuid);
                 }
                 else  // new list
                 {
                     SPList list = null;
                     workflowOptions.taskListName = taskListGuid.Substring(1);
                     try
                     {
                         list = Web.Lists[workflowOptions.taskListName];
                     }
                     catch (ArgumentException)
                     {
                     }
 
                     if (list != null)
                         throw new SPException("A list already exists with the same name as that proposed for the new task list. "+
                                               "Use your's Back button and either change the name of the workflow or "+
                                               "select an existing task list.&lt;br&gt;");
                 }
 
                 // Do the same for the history list
                 string historyListGuid = Request.Params["HistoryList"];
                 if (historyListGuid[0] != ) // user selected already existing list
                 {
                     workflowOptions.historyListId = new Guid(historyListGuid);
                 }
                 else // User wanted a new list
                 {
                     SPList list = null;
 
                     workflowOptions.historyListName = historyListGuid.Substring(1);
 
                     try
                     {
                         list = Web.Lists[workflowOptions.historyListName];
                     }
                     catch (ArgumentException)
                     {
                     }
                     if (list != null)
                         throw new SPException("A list already exists with the same name as that proposed for the new history list. Use your's Back button and either change the name of the workflow or select an existing history list.&lt;br&gt;");
                 }
             }
         }
 
         public void BtnOK_Click(object sender, EventArgs e)
         {
             SPList taskList = null;
             SPList historyList = null;
             if (!IsValid)
                 return;
             if (!useContentTypeTemplate)
             {
                 // If the user requested a new task list, create it.
                 if( workflowOptions.taskListId == Guid.Empty )
                 {
                     string description = string.Format("Task list for the {0} workflow.", workflowName);
                     workflowOptions.taskListId = Web.Lists.Add(workflowOptions.taskListName, description, SPListTemplateType.Tasks);
                 }
 
                 // If the user requested a new history list, create it.
                 if( workflowOptions.historyListId == Guid.Empty )
                 {
                     string description = string.Format("History list for the {0} workflow.", workflowName);
                     workflowOptions.historyListId = Web.Lists.Add(workflowOptions.historyListName, description, SPListTemplateType.WorkflowHistory);
                 }
                 taskList = Web.Lists[workflowOptions.taskListId];
                 historyList = Web.Lists[workflowOptions.historyListId];
             }
 
             // Perform association (if it does not already exist).
             bool isNewAssociation;
             if (associationTemplate == null)
             {
                 isNewAssociation = true;
                 if (!useContentTypeTemplate)
                     associationTemplate = SPWorkflowAssociation.CreateListAssociation(baseTemplate,
                                         workflowName, taskList, historyList);
                 else
                 {
                     associationTemplate = SPWorkflowAssociation.CreateWebContentTypeAssociation(baseTemplate, workflowName, 
                                         workflowOptions.taskListName, workflowOptions.historyListName);
                 }
             }
             else // Modify existing template.
             {
                 isNewAssociation = false;
                 associationTemplate.Name = workflowName;
                 associationTemplate.SetTaskList(taskList);
                 associationTemplate.SetHistoryList(historyList);
             }
 
             // Set up startup parameters in the template.
             associationTemplate.Name = workflowName;
             associationTemplate.LockItem = workflowOptions.lockItem;
             associationTemplate.AutoStartCreate = workflowOptions.autoStartCreate;
             associationTemplate.AutoStartChange = workflowOptions.autoStartChange;
             associationTemplate.AllowManual = workflowOptions.allowManual;
 
             if (associationTemplate.AllowManual)
             {
                 SPBasePermissions newPerms = SPBasePermissions.EmptyMask;
 
                 if (Request.Params["ManualPermEditItemRequired"] == "ON")
                     newPerms |= SPBasePermissions.EditListItems;
                 if (Request.Params["ManualPermManageListRequired"] == "ON")
                     newPerms |= SPBasePermissions.ManageLists;
 
                 associationTemplate.PermissionsManual = newPerms;
             }
 
             // Place data from form into the association template.
             associationTemplate.AssociationData = SerializePageToXml(FormType.Association);
 
             // If this is a content type association, add the template to the content type.
             if (contentType != null)
             {
                 if( isNewAssociation )
                     contentType.WorkflowAssociations.Add(associationTemplate);
                 else
                     contentType.WorkflowAssociations.Update(associationTemplate);
 
                 if( workflowOptions.updateLists )
                     contentType.UpdateWorkflowAssociationsOnChildren(false);
             }
             else // Else, if this is a list association.
             {
                 if (isNewAssociation)
                     list.WorkflowAssociations.Add(associationTemplate);
                 else
                     list.WorkflowAssociations.Update(associationTemplate);
 
                 if (associationTemplate.AllowManual && list.EnableMinorVersions)
                 {
                     // If this WF was selected to be the content approval WF 
                     // (m_setDefault = true, see association page) then enable content
                     // Approval for the list.
                     if (list.DefaultContentApprovalWorkflowId != associationTemplate.Id && workflowOptions.setDefault)
                     {
                         if (!list.EnableModeration)
                             list.EnableModeration = true;
                         list.DefaultContentApprovalWorkflowId = associationTemplate.Id;
                         list.Update();
                     }
                     else if( list.DefaultContentApprovalWorkflowId == associationTemplate.Id && !workflowOptions.setDefault )
                     {
                         // Reset the DefaultContentApprovalWorkflowId
                         list.DefaultContentApprovalWorkflowId = Guid.Empty;
                         list.Update();
                     }
                 }
             }
 
             SPUtility.Redirect("WrkSetng.aspx", SPRedirectFlags.RelativeToLayoutsPage, HttpContext.Current, requestQueryString);
         }
     }
 }
 
 

In most cases you should not need to modify this code! Because the serialization and deserialization of the data is done in the “Workflow2DataPages” base class.

14. Now we create another “Application Page” project item named “Workflow2InitiationForm.aspx”. This will be created in folder “Layoutsik.SharePoint2010.Workflow”. Move them to the module project item named “Workflow 2 Forms”.

image

Now you can remove the “Layouts” folder in the project structure in the Solution Explorer pane. (This folder was created automatically by adding a “Application Page” project item.)

image

Now edit the ASPX page.

Modify the “Page” tag:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Workflow2InitiationForm.aspx.cs" 
Inherits="ik.SharePoint2010.Workflow.Workflow2InitiationForm"
MasterPageFile="~masterurl/default.master" %>

As you can see we need to replace the “DynamicMasterPageFile” attribute through “MasterPageFile” and change the “Inherits” attribute by modifying the classes full name. (Below we will change the namespace of the generated class file.)

Now we need to add some “Register” tags to register some SharePoint controls for using them in the site.

<%@ Register TagPrefix="wssuc" TagName="LinksTable" src="/_controltemplates/LinksTable.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="LinkSection" src="/_controltemplates/LinkSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ActionBar" src="/_controltemplates/ActionBar.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ToolBar" src="/_controltemplates/ToolBar.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" src="/_controltemplates/ToolBarButton.ascx" %> 
<%@ Register TagPrefix="wssuc" TagName="Welcome" src="/_controltemplates/Welcome.ascx" %>

You can remove the content placeholder named “PlaceHolderAdditionalPageHead”.

The following ASP.NET code we add to the content placeholder named “PlaceHolderPageTitle”:

    <asp:Literal ID="Literal1" runat="server" Text="Customize Workflow" />

The following ASP.NET code we add to the content placeholder named “PlaceHolderPageTitleInTitleArea”:

    <%
        string strPTS = "Customize " + workflowName;
        SPHttpUtility.HtmlEncode(strPTS, Response.Output);
    %>

The following ASP.NET code we add to the content placeholder named “PlaceHolderPageImage”:

    <img src="/_layouts/images/blank.gif" width="1" height="1" alt="" />
The following ASP.NET code we add to the content placeholder named “PlaceHolderPageDescription”:
    <%
        string strPD = "Use this page to customize this instance of " + workflowName + ".";
        SPHttpUtility.HtmlEncode(strPD, Response.Output);
    %>

Now we add a ASP.NET table control and some more content to the content placeholder named “PlaceHolderMain”. Inside this snipped the TextBoxes are defined that the user will use later while editing the association data.

<asp:Table CellSpacing="0" CellPadding="0" BorderWidth="0" CssClass="ms-propertysheet">
    <wssuc:InputFormSection Title="Workflow Data Values" Description="Specify the workflow data values." runat="server">
        <template_inputformcontrols>
            <wssuc:InputFormControl LabelText="Specify Form Data Values:" runat="server">
                <Template_Control>
                    <table border="0" cellspacing="0" cellpadding="0">
                        <!-- ikarstein: Start your modifications here -->
                        <tr>
                            <td class="ms-authoringcontrols">
                                Data 3: <asp:TextBox id="textboxData3" runat="server"></asp:TextBox>
                            </td>
                        </tr>
                        <tr>
                            <td class="ms-authoringcontrols">
                                Data 4: <asp:TextBox id="textboxData4" runat="server"></asp:TextBox>
                            </td>
                            </tr>
                        <!-- ikarstein: End of modifications -->
                    </table>
                </Template_Control>
            </wssuc:InputFormControl>
        </template_inputformcontrols>
    </wssuc:InputFormSection>
        
    <input type="hidden" name="WorkflowDefinition" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["WorkflowDefinition"]),Response.Output); %>'/>
    <input type="hidden" name="WorkflowName" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["WorkflowName"]),Response.Output); %>'/>
    <input type="hidden" name="AddToStatusMenu" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AddToStatusMenu"]),Response.Output); %>'/>
    <input type="hidden" name="AllowManual" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AllowManual"]),Response.Output); %>'/>
    <input type="hidden" name="RoleSelect" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["RoleSelect"]),Response.Output); %>'/>
    <input type="hidden" name="GuidAssoc" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["GuidAssoc"]),Response.Output); %>'/>
    <input type="hidden" name="SetDefault" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["SetDefault"]),Response.Output); %>'/>
    <input type="hidden" name="HistoryList" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["HistoryList"]),Response.Output); %>'/>
    <input type="hidden" name="TaskList" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["TaskList"]),Response.Output); %>'/>
    <input type="hidden" name="UpdateLists" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["UpdateLists"]),Response.Output); %>'/>        
    <input type="hidden" name="AutoStartCreate" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AutoStartCreate"]),Response.Output); %>'/>
    <input type="hidden" name="AutoStartChange" value='<% SPHttpUtility.NoEncode(SPHttpUtility.HtmlEncode(Request.Form["AutoStartChange"]),Response.Output); %>'/>

    <wssuc:ButtonSection runat="server">
        <template_buttons>
            <asp:PlaceHolder runat="server">
                <asp:Button runat="server" class="ms-ButtonHeightWidth" OnClick="BtnOK_Click" Text="OK" id="btnOK" />
            </asp:PlaceHolder>
        </template_buttons>
    </wssuc:ButtonSection>
</asp:Table>

(I’ve marked the section you should modify for your special needs with HTML comments…)

You see in the ASP.NET code: in this form only “Data3” and “Data4” will be modified. The propertied “Data1” and “Data2” will be modified only in the Association Form. (The “DataX” properties are defined in the “Workflow2Data” class.

15. Now we modify the “code behind” for Workflow2InitiationForm.aspx. – Open “Workflow2InitiationForm.aspx.cs”. Replace the content with this code:

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
using System.Web;
using Microsoft.SharePoint.Security;
using System.Security.Permissions;

namespace ik.SharePoint2010.Workflow
{
    public partial class Workflow2InitiationForm : Workflow2DataPages
    {
        protected SPListItem listItem;
        protected string listItemName;
        protected string listItemUrl;
        protected SPWorkflowAssociation associationTemplate;
        protected SPWorkflowTemplate baseTemplate;

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            GetListItemInfo();
            GetAssociationInfo();

            if( !IsPostBack )
            {
                PopulatePageFromXml((string)associationTemplate.AssociationData, FormType.Initiation);
            }
        }

        private void GetAssociationInfo()
        {
            Guid associationTemplateId = new Guid(Request.Params["TemplateID"]);

            associationTemplate = list.WorkflowAssociations[associationTemplateId];
            
            if( associationTemplate == null ) 
            {
                SPContentTypeId contentTypeId = (SPContentTypeId)listItem["ContentTypeId"];
                SPContentType contentType = list.ContentTypes[contentTypeId];
                associationTemplate = contentType.WorkflowAssociations[associationTemplateId];
            }

            if( associationTemplate == null )
                throw new SPException("The requested workflow could not be found.");

            baseTemplate = Web.WorkflowTemplates[associationTemplate.BaseId];
            
            workflowName = associationTemplate.Name;
            
            string m_formData = (string)associationTemplate.AssociationData;
        }

        private void GetListItemInfo()
        {
            listItem = list.GetItemById(Convert.ToInt32(Request.Params["ID"]));

            if( listItem.File == null )
                listItemUrl = Web.Url + listItem.ParentList.Forms[PAGETYPE.PAGE_DISPLAYFORM].ServerRelativeUrl + "?ID=" + listItem.ID.ToString();
            else
                listItemUrl = Web.Url + "/" + listItem.File.Url;

            if( list.BaseType == SPBaseType.DocumentLibrary )
            {
                listItemName = (string)listItem["Name"];

                int i = listItemName.LastIndexOf('.');
                
                if( i > 0 )
                    listItemName = listItemName.Substring(0, i);
            }
            else
                listItemName = (string)listItem["Title"];
        }

        public void BtnOK_Click(object sender, EventArgs e)
        {
            string InitData = SerializePageToXml(FormType.Initiation);
            InitiateWorkflow(InitData);
        }

        private void InitiateWorkflow(string InitData)
        {
            try
            {
                Web.Site.WorkflowManager.StartWorkflow(listItem, associationTemplate, InitData);
            }
            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 most cases you should not need to modify this code! Because the serialization and deserialization of the data is done in the “Workflow2DataPages” base class.

16. Now modify the “Elements.xml” file of the module “Workflow 2 Forms”.

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="Workflow 2 Forms" Url="Workflow2Forms" RootWebOnly="False">
    <File Path="Workflow 2 FormsWorkflow2AssociationForm.aspx" Url="Workflow2AssociationForm.aspx" />
    <File Path="Workflow 2 FormsWorkflow2InitiationForm.aspx" Url="Workflow2InitiationForm.aspx" />
</Module>
</Elements>

You have to add the two “File” tags (including their content) to the “Module” tag.

17. Now we are done with creating the Association and Initiation form! In the next post I will create a sequential workflow, connect them with both forms created in this walkthrough and test the project. Continue reading here: Part 2.

Walkthrough: Creating a simple Sequential Workflow with a custom Task Form in SharePoint 2010 using Visual Studio 2010 (Part 3 of (2+1))

 

This article belongs to these previous posts:

You should read this article if you have problems to deploy my sample project on Codeplex (http://spworkflowdemo.codeplex.com/) in Visual Studio 2010.

1. Download the code and copy them into your sources folder.

2. Create a site collection in SharePoint 2010, e.g. “http://sharepoint.local/sites/workflow”. Use “Blank Site” site template.

3. Open the solution file in Visual Studio 2010 (file with extension “.sln”).

4. In the Solution Explorer pane select the project node.

5. Set the “Site URL” property to your previously created SharePoint Site (see step 2 above).

image

6. In the Solution Explorer pane select the “Workflow 1” node in the project.

7. In the Properties pane modify the value of “Display Name”. Set it to “Workflow 1”.

8. Now click into the values edit box of “History List” (or “Target List” or “Task List”). This will open a wizard.

9. In the wizard specify the values of the Workflow Association. Choose the values shown in following screenshot:

Step “A”:

image

Click “Next”.

Step “B”:

image

Click “Next”.

Step “C”:

image

Click “Finish”.

10. Now you should be able to deploy the project.

Walkthrough: Deploying a InfoPath 2010 List Form in a Visual Studio 2010 solution package

This is not easy. It took me some days. – Now it works! Smile

Here I’ll show you how to create a List Form for a SharePoint List using InfoPath 2010. Therefore you need SharePoint 2010 Server with Enterprise CALs! – With InfoPath Forms Services it’s possible to render List Forms (“New”, “Edit” and “Display” forms) as browser enabled InfoPath forms. This is really, really cool!

I’ve published the project source code on Codeplex: http://spinfopathlistform.codeplex.com/

Lets start…

1. We create a SharePoint site collection named “http://sharepoint.local/sites/infopathlistform”.

image

2. Now we activate the site collection features:

  • SharePoint Server Enterprise Site Collection features
  • SharePoint Server Standard Site Collection features

image

The enterprise features need to be activated in order to get InfoPath Forms Services working.

3. Open Visual Studio 2010. Create an “Empty SharePoint Project” named “ik.SharePoint2010.InfoPathListFormDemo”.

image

Deploy them as “Farm Solution”.

image

4. Now we add a new project item of type “List Definition” named “Test List 1”.

image

We choose “Custom List” as base type and check “Add a list instance…”

image

5. We open the “Elements.xml” file of the List Instance.

image

We change the List Title and List URL.

image

6.  We edit the “schema.xml” file.

image

We create a list content type with 3 fields.

Here you see the complete content of the “Schema.xml” file.

 <?xml version="1.0" encoding="utf-8"?> 
 <List xmlns:ows="Microsoft SharePoint" Title="Test List 1" FolderCreation="FALSE"
       Direction="$Resources:Direction;"
       Url="Lists/ik.SharePoint2010.InfoPathListFormDemo-TestList1"
       Type="100"
       BaseType="0" xmlns="http://schemas.microsoft.com/sharepoint/"> 
   <MetaData> 
     <ContentTypes> 
       <ContentType ID="0x01009A15733A093E44C385EDB29F8E2E1B5A" Name="List Item" Inherits="false"> 
         <Folder TargetName="Item" /> 
         <FieldRefs> 
           <FieldRef ID="{10F8137D-B555-472C-8ACB-B64FF5BEAAF8}" Name="Field_1" /> 
           <FieldRef ID="{84E33CDB-8DDC-4F46-BBF5-845D4071ED41}" Name="Field_2" /> 
           <FieldRef ID="{52680EED-BE6F-478F-A6EC-730398CD626C}" Name="Field_3" /> 
         </FieldRefs> 
       </ContentType> 
       <ContentTypeRef ID="0x0120" /> 
     </ContentTypes> 
     <Fields> 
       <Field ID="{10F8137D-B555-472C-8ACB-B64FF5BEAAF8}" Name="Field_1" StaticName="Field_1"
              DisplayName="Text Field" Type="Text"/> 
       <Field ID="{84E33CDB-8DDC-4F46-BBF5-845D4071ED41}" Name="Field_2"  StaticName="Field_2"
              DisplayName="DateTime Field" Type="DateTime" Format="DateOnly" /> 
       <Field ID="{52680EED-BE6F-478F-A6EC-730398CD626C}" Name="Field_3"  StaticName="Field_3"
              DisplayName="Checkbox Field" Type="Boolean" /> 
     </Fields> 
     <Views> 
       <View BaseViewID="0" Type="HTML" MobileView="TRUE" TabularView="FALSE"> 
         <Toolbar Type="Standard" /> 
         <XslLink Default="TRUE">main.xsl</XslLink> 
         <RowLimit Paged="TRUE">30</RowLimit> 
         <ViewFields> 
           <FieldRef Name="LinkTitleNoMenu"></FieldRef> 
           <FieldRef Name="Field_1"></FieldRef> 
           <FieldRef Name="Field_2"></FieldRef> 
           <FieldRef Name="Field_3"></FieldRef> 
         </ViewFields> 
         <Query> 
           <OrderBy> 
             <FieldRef Name="Modified" Ascending="FALSE"></FieldRef> 
           </OrderBy> 
         </Query> 
         <ParameterBindings> 
           <ParameterBinding Name="AddNewAnnouncement" Location="Resource(wss,addnewitem)" /> 
           <ParameterBinding Name="NoAnnouncements" Location="Resource(wss,noXinviewofY_LIST)" /> 
           <ParameterBinding Name="NoAnnouncementsHowTo" Location="Resource(wss,noXinviewofY_ONET_HOME)" /> 
         </ParameterBindings> 
       </View> 
       <View BaseViewID="1" Type="HTML" WebPartZoneID="Main" DisplayName="$Resources:core,objectiv_schema_mwsidcamlidC24;" DefaultView="TRUE" MobileView="TRUE" MobileDefaultView="TRUE" SetupPath="pages\viewpage.aspx" ImageUrl="/_layouts/images/generic.png" Url="AllItems.aspx"> 
         <Toolbar Type="Standard" /> 
         <XslLink Default="TRUE">main.xsl</XslLink> 
         <RowLimit Paged="TRUE">30</RowLimit> 
         <ViewFields> 
           <FieldRef Name="Attachments"></FieldRef> 
           <FieldRef Name="LinkTitle"></FieldRef> 
           <FieldRef Name="Field_1"></FieldRef> 
           <FieldRef Name="Field_2"></FieldRef> 
           <FieldRef Name="Field_3"></FieldRef> 
         </ViewFields> 
         <Query> 
           <OrderBy> 
             <FieldRef Name="ID"></FieldRef> 
           </OrderBy> 
         </Query> 
         <ParameterBindings> 
           <ParameterBinding Name="NoAnnouncements" Location="Resource(wss,noXinviewofY_LIST)" /> 
           <ParameterBinding Name="NoAnnouncementsHowTo" Location="Resource(wss,noXinviewofY_DEFAULT)" /> 
         </ParameterBindings> 
       </View> 
     </Views> 
     <Forms> 
       <Form Type="DisplayForm" Url="DispForm.aspx" SetupPath="pagesform.aspx" WebPartZoneID="Main" /> 
       <Form Type="EditForm" Url="EditForm.aspx" SetupPath="pagesform.aspx" WebPartZoneID="Main" /> 
       <Form Type="NewForm" Url="NewForm.aspx" SetupPath="pagesform.aspx" WebPartZoneID="Main" /> 
     </Forms> 
   </MetaData> 
 </List>

 

7. The next step is to add an “Empty Element” project item named “Fields”. We add the following content:

 <?xml version="1.0" encoding="utf-8"?> 
 <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
   <Field ID="{10F8137D-B555-472C-8ACB-B64FF5BEAAF8}" Name="Field_1" StaticName="Field_1"
          DisplayName="Text Field" Type="Text"/> 
   <Field ID="{84E33CDB-8DDC-4F46-BBF5-845D4071ED41}" Name="Field_2"  StaticName="Field_2"
          DisplayName="DateTime Field" Type="DateTime" Format="DateOnly" /> 
   <Field ID="{52680EED-BE6F-478F-A6EC-730398CD626C}" Name="Field_3"  StaticName="Field_3"
          DisplayName="Checkbox Field" Type="Boolean" /> 
 </Elements> 


 

8. Now we deploy the project.

9. Open the site in the browser. Navigate to the “Test List 1”. Select “Edit in SharePoint Designer” from the “Site Actions” menu.

image

10. In SharePoint Designer 2010 open the “Lists and Libraries” from the “Navigation” pane and open the “Test List 1”.

image

In the Ribbon you’ll see the command “Design Forms in InfoPath”.

image

Click the command button and select “List Item” in the drop down menu.

Now InfoPath Designer 2010 opens…

11. In InfoPath Designer 2010 modify the form.

(I will not modify it.)

image

Press the “Publish” button on the Shortcut Menu:

image

12. In SharePoint Designer 2010 choose “All Files” from the “Navigation” pane. Click “Lists”.

image

Click “TestList1”.

image

Click “Item”.

image

Right-click “template.xsn”. Select “Properties…” from the context menu.

image

Select the “Location” from the properties dialog.

image

Copy the URL and open it in a new browser window. You’ll get a “File Download” dialog.

image

Save the file to a temporary location, e.g. to the Desktop.

13. Add a new “Module” project item and name it “Form”.

image

Open a Windows Explorer. Navigate to the Desktop. Drag the file “template.xsn” from the Desktop into the “Form” module inside the Visual Studio project.

Modify the “Elements.xml” file of the “Form” module.

image

14. Add a new “Module” project item and name it “FormPages”.

Into the “Elements.xml” file of this module enter the following code:

 

 <?xml version="1.0" encoding="utf-8"?> 
 <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
   <Module Name="TestList1Form" Url="Lists/TestList1/Item" SetupPath="pages" RootWebOnly="FALSE" xmlns="http://schemas.microsoft.com/sharepoint/"> 
     <File Url="displayifs.aspx" Type="Ghostable" Path="form.aspx"> 
       <BinarySerializedWebPart> 
         <GUIDMap> 
           <GUID Id="1b8faa0c_4e13_43e7_981f_57d525170d09" ListUrl="Lists/TestList1" /> 
         </GUIDMap> 
         <WebPart ID="{D95DEC48-6F9E-4961-BE47-380AD2870CCA}"
                  List="{$ListId:Lists/TestList1;}"
                  Type="4"
                  Url="Lists/TestList1/Item/displayifs.aspx"
                  WebPartOrder="0"
                  WebPartZoneID="Main"
                  IsIncluded="True"
                  FrameState="0"
                  WPTypeId="{b1dc92e2-8558-f555-ae81-35ed9ddf1644}"
                  Assembly="Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
                  Class="Microsoft.Office.InfoPath.Server.Controls.WebUI.BrowserFormWebPart"
                  AllUsers="B6Dt/iwAAAABAAAAAAAAAAEAAAB+bGlzdC9JdGVtL3RlbXBsYXRlLnhzbgD/ARQrABQCAgIDAgMBBAACAQIJAQEAAggFDEZvcm1Mb2NhdGlvbgUXfmxpc3QvSXRlbS90ZW1wbGF0ZS54c24FDUNvbnRlbnRUeXBlSWQFJjB4MDEwMDdEMTVFNzgxOUUwNDMzNEE4QkY5OTE3OEM1OUNDNjhDBQxMaXN0Rm9ybU1vZGULKaIBTWljcm9zb2Z0Lk9mZmljZS5JbmZvUGF0aC5TZXJ2ZXIuQ29udHJvbHMuV2ViVUkuTGlzdEZvcm1Nb2RlLCBNaWNyb3NvZnQuT2ZmaWNlLkluZm9QYXRoLlNlcnZlciwgVmVyc2lvbj0xNC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj03MWU5YmNlMTExZTk0MjljAQUOU3VibWl0QmVoYXZpb3ILKaQBTWljcm9zb2Z0Lk9mZmljZS5JbmZvUGF0aC5TZXJ2ZXIuQ29udHJvbHMuV2ViVUkuU3VibWl0QmVoYXZpb3IsIE1pY3Jvc29mdC5PZmZpY2UuSW5mb1BhdGguU2VydmVyLCBWZXJzaW9uPTE0LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTcxZTliY2UxMTFlOTQyOWMDAQAAAgICAgU+VXNlIHRoaXMgV2ViIFBhcnQgdG8gZGlzcGxheSBhbiBJbmZvUGF0aCBicm93c2VyLWVuYWJsZWQgZm9ybS4="
                  PerUser="/wEUKwAJAgICAwIBAQAAAgQChAELKjFTeXN0ZW0uV2ViLlVJLldlYkNvbnRyb2xzLldlYlBhcnRzLlBhcnRDaHJvbWVUeXBlAgIEBRZJbmZvUGF0aCBGb3JtIFdlYiBQYXJ0" /> 
       </BinarySerializedWebPart> 
     </File> 
     <File Url="editifs.aspx" Type="Ghostable" Path="form.aspx"> 
       <BinarySerializedWebPart> 
         <GUIDMap> 
           <GUID Id="1b8faa0c_4e13_43e7_981f_57d525170d09" ListUrl="Lists/TestList1" /> 
         </GUIDMap> 
         <WebPart ID="{f811e526-1ad2-4563-b51b-4233f2641a33}"
                  List="{$ListId:Lists/TestList1;}"
                  Type="6"
                  Flags="0"
                  Url="Lists/TestList1/Item/editifs.aspx"
                  WebPartOrder="0"
                  WebPartZoneID="Main"
                  IsIncluded="True"
                  FrameState="0"
                  WPTypeId="{b1dc92e2-8558-f555-ae81-35ed9ddf1644}"
                  Assembly="Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
                  Class="Microsoft.Office.InfoPath.Server.Controls.WebUI.BrowserFormWebPart"
                  AllUsers="B6Dt/iwAAAABAAAAAAAAAAEAAAB+bGlzdC9JdGVtL3RlbXBsYXRlLnhzbgD/ARQrABICAgIDAgMBBAACAQIJAQEAAgYFDEZvcm1Mb2NhdGlvbgUXfmxpc3QvSXRlbS90ZW1wbGF0ZS54c24FDUNvbnRlbnRUeXBlSWQFJjB4MDEwMDdEMTVFNzgxOUUwNDMzNEE4QkY5OTE3OEM1OUNDNjhDBQ5TdWJtaXRCZWhhdmlvcgsppAFNaWNyb3NvZnQuT2ZmaWNlLkluZm9QYXRoLlNlcnZlci5Db250cm9scy5XZWJVSS5TdWJtaXRCZWhhdmlvciwgTWljcm9zb2Z0Lk9mZmljZS5JbmZvUGF0aC5TZXJ2ZXIsIFZlcnNpb249MTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49NzFlOWJjZTExMWU5NDI5YwMBAAACAgICBT5Vc2UgdGhpcyBXZWIgUGFydCB0byBkaXNwbGF5IGFuIEluZm9QYXRoIGJyb3dzZXItZW5hYmxlZCBmb3JtLg=="
                  PerUser="/wEUKwAJAgICAwIBAQAAAgQChAELKjFTeXN0ZW0uV2ViLlVJLldlYkNvbnRyb2xzLldlYlBhcnRzLlBhcnRDaHJvbWVUeXBlAgIEBRZJbmZvUGF0aCBGb3JtIFdlYiBQYXJ0" /> 
       </BinarySerializedWebPart> 
     </File> 
     <File Url="newifs.aspx" Type="Ghostable" Path="form.aspx"> 
       <BinarySerializedWebPart> 
         <GUIDMap> 
           <GUID Id="1b8faa0c_4e13_43e7_981f_57d525170d09" ListUrl="Lists/TestList1" /> 
         </GUIDMap> 
         <WebPart ID="{7319002a-a547-4cdf-8acc-c5ba9cf8a6c9}"
                  List="{$ListId:Lists/TestList1;}"
                  Type="8"
                  Flags="0"
                  Url="Lists/TestList1/Item/newifs.aspx"
                  WebPartOrder="0"
                  WebPartZoneID="Main"
                  IsIncluded="True"
                  FrameState="0"
                  WPTypeId="{b1dc92e2-8558-f555-ae81-35ed9ddf1644}"
                  Assembly="Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
                  Class="Microsoft.Office.InfoPath.Server.Controls.WebUI.BrowserFormWebPart"
                  AllUsers="B6Dt/iwAAAABAAAAAAAAAAEAAAB+bGlzdC9JdGVtL3RlbXBsYXRlLnhzbgD/ARQrABICAgIDAgMBBAACAQIJAQEAAgYFDEZvcm1Mb2NhdGlvbgUXfmxpc3QvSXRlbS90ZW1wbGF0ZS54c24FDUNvbnRlbnRUeXBlSWQFJjB4MDEwMDdEMTVFNzgxOUUwNDMzNEE4QkY5OTE3OEM1OUNDNjhDBQ5TdWJtaXRCZWhhdmlvcgsppAFNaWNyb3NvZnQuT2ZmaWNlLkluZm9QYXRoLlNlcnZlci5Db250cm9scy5XZWJVSS5TdWJtaXRCZWhhdmlvciwgTWljcm9zb2Z0Lk9mZmljZS5JbmZvUGF0aC5TZXJ2ZXIsIFZlcnNpb249MTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49NzFlOWJjZTExMWU5NDI5YwMBAAACAgICBT5Vc2UgdGhpcyBXZWIgUGFydCB0byBkaXNwbGF5IGFuIEluZm9QYXRoIGJyb3dzZXItZW5hYmxlZCBmb3JtLg=="
                  PerUser="/wEUKwAJAgICAwIBAQAAAgQChAELKjFTeXN0ZW0uV2ViLlVJLldlYkNvbnRyb2xzLldlYlBhcnRzLlBhcnRDaHJvbWVUeXBlAgIEBRZJbmZvUGF0aCBGb3JtIFdlYiBQYXJ0" /> 
       </BinarySerializedWebPart> 
     </File> 
   </Module> 
 </Elements>

 

During deployment this creates the form aspx pages that will host the InfoPath Web Part that renders the form at runtime.

In the XML code there are several references to the destination list. All of them have to be set to “Lists/TestList1”. All the code needs to remain unmodified. Don’t change “WPTypeId” attributes! “{b1dc92e2-8558-f555-ae81-35ed9ddf1644}” is the identifier for InfoPath Render Web Part.

BUT change the “ID” attributes of the three “WebPart” tags! Use the “Create GUID” tool from the “Tools” menu of Visual Studio 2010.

15. Now open the “Schema.xml” file of the List Definition project item of “Test List 1”. Add the following code at the before the closing tag of our list content type.

         <XmlDocuments> 
           <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"> 
             <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"> 
               <Display> ListForm</Display> 
               <Edit> ListForm</Edit> 
               <New> ListForm</New> 
             </FormTemplates> 
           </XmlDocument> 
           <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url"> 
             <FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url"> 
               <Display> ~list/Item/displayifs.aspx</Display> 
               <Edit> ~list/Item/editifs.aspx</Edit> 
               <New> ~list/Item/newifs.aspx</New> 
             </FormUrls> 
           </XmlDocument> 
         </XmlDocuments>

 

16. Add another project item of type “Empty Element” named “PropertyBag”. Edit the “Elements.xml” file. Add this code:

 <?xml version="1.0" encoding="utf-8"?> 
 <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
   <PropertyBag Url="Lists/TestList1/Item/template.xsn"  
                ParentType="File" RootWebOnly="FALSE"  
                xmlns="http://schemas.microsoft.com/sharepoint/"> 
     <Property Name="ipfs_listform" Value="true" Type="string" /> 
     <Property Name="ipfs_streamhash" Value="" Type="string" /> 
   </PropertyBag> 
 </Elements> 

 

This will define the “template.xsn” to be a “InfoPath Forms Services” file (image).

17. Deploy the project.

18. Open the browser and navigate to the “Test List 1”.

Click “Add new item”…

19. HERE WE ARE!

image

But if you enter values and try to save you get this error:

image

This is caused by the missing “Title” field inside the form. This field is marked as mandatory so you need to define it or to remove it from the Content Type and modify the form.

We will do the last one now.

20. In Visual Studio modify the “schema.xml” file of the “Test List 1”. Insert the marked line of code inside the “FieldsRefs” tag of the content type with id starting “0x01…”.

image

Now search for “LinkTitleNoMenu” in the schema.xml file. Replace this “view field reference” with “Edit”.

Now search for “LinkTitle” and replace it with “Edit” too.

21. In Visual Studio look for “template.xsn” in “Form” path in the Solution Explorer. Right-Click the project item and select “Open Containing Folder”.

image

22. In Windows Explorer right-click the file “template.xsn” and click “Design”. The InfoPath 2010 Designer will open.

23. In InfoPath 2010 you’ll see that “Title” cannot be removed. To remove it you would need the edit the xsn file with Notepad: XSN is a CAB file. So you can rename it to cab, extract it’s content, modify the “manifest.xfs” file and compress the folder as CAB file named “template.xsn”… (If you need assistance on this please post as comment.)

In the “Fields” pane select “Title”.

image

(This is the “advanced view” !)

Click the arrow behind the element. Click “Properties” in the context menu.

Uncheck “Cannot be blank”. Click the “OK” button. Save the file. Don’t press “Publish” on the upcoming dialog. Just press “Save”!

Quit InfoPath.

Deploy the project.

24. In Internet Explorer refresh you site and add a new item to the “Test  List 1”.

You’ll see: the “Title” column is gone. Now we have a “Edit item” column on the list view.

Now add an item.

image

Save it.

25. HERE WE ARE AGAIN!

It works for me now!

image

Just for testing purpose select the list item and click “View Item” on the Ribbon.

You will see the list item in a read-only InfoPath browser form!! – If you click “Edit” on the Ribbon, you’ll be able to edit the item.

Walkthrough: Creating a simple Sequential Workflow with a custom Task Form in SharePoint 2010 using Visual Studio 2010 (Part 1 of 2)

In this walkthrough I want to show you how to create a Sequential Workflow with Visual Studio 2010 for use in SharePoint 2010. – I will show how to create a custom Task Form for interaction with users. The Task form will be a native SharePoint list form. No InfoPath. There are many InfoPath samples out there but they cannot be used on a SharePoint Foundation 2010 platform. But workflows can be used on SharePoint Foundation 2010 too!

To reproduce the following steps you need to create a SharePoint site. – In the walkthrough I’ll use a Site Collection located at “http://sharepoint.local/sites/workflow”.

This blog post is part 1 of 2. It describes the steps 1 to 15. Read part 2 here: https://blog.kenaro.com/2011/03/30/walkthrough-creating-a-simple-sequential-workflow-with-a-custom-task-form-in-sharepoint-2010-using-visual-studio-2010-part-2-of-2/

You can download – and help to develop – the whole demo project source code at Codeplex: http://spworkflowdemo.codeplex.com/

(If you need assistance for the deployment process because of deployment errors please see this third post of this series: https://blog.kenaro.com/2011/04/22/walkthrough-creating-a-simple-sequential-workflow-with-a-custom-task-form-in-sharepoint-2010-using-visual-studio-2010-part-3-of-21/)

Let’s start…

1. We create the Site Collection. Use the “Blank Site” site template.

image

2. In Visual Studio 2010 we create a Empty SharePoint Project. I named it “ik.SharePoint2010.Workflow”

image

We create it with “Deploy as farm solution” and specify the location “http://sharepoint.local/sites/workflow/”.

image

3. This is the project structure at start:

image

4. First we need to create an instance of the Workflow History list that is needed for the workflow.

Create a new “Empty Element” project item named “Workflow History”.

image

Open the created “element.xml” file and modify it as shown below.

1: <?xml version="1.0" encoding="utf-8"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3:  <ListInstance Title="Workflow History"
4:  OnQuickLaunch="TRUE"
5:  TemplateType="140"
6:  FeatureId="00BFEA71-4EA5-48D4-A4AD-305CF7030140"
7:  Url="Lists/WorkflowHistory"
8:  Description="">
9:  </ListInstance>
10: </Elements>

The “TemplateType” attribute represents the “Workflow History” list template. It resists on a SharePoint feature with ID “00BFEA71-4EA5-48D4-A4AD-305CF7030140”. It’s a native SharePoint feature.

You can add the attibute “Hidden” to the “ListInstance” tag and set it’s value to “TRUE” to hide the list as it’s done by SharePoint by default for this list. In this case you should also change “OnQuickLaunch” to “FALSE”. For my demo purpose I want to have “Workflow History” visible and on the Quick Launch bar.

5. Now we will create all tools we need for a “Workflow 1”. (May be I’ll create more workflow demos later. So it’s number 1.)

6. We create the SharePoint fields for “Workflow 1”. Therefore we create another “Empty Element” project item named “Workflow 1 Fields”.

image

I’ll create 3 fields for use in the Task Form we will create: Test1, Test2, Test3. They are all fields of type Text.

1: <?xml version="1.0" encoding="utf-8"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3:  <!-- Fields for Task 1 of Workflow 1-->
4:  <Field ID="{2FE15855-3CAB-44A6-AB29-1600204FCA20}" Name="Workflow1Task1_Test1"
5:  MaxLength="255" DisplayName="Test1" Description=""
6:  Direction="None" Type="Text" Overwrite="TRUE"
7:  xmlns="http://schemas.microsoft.com/sharepoint/" />
8:  <Field ID="{517B22A5-1B89-4C24-82BE-3D4FD99645BC}" Name="Workflow1Task1_Test2"
9:  MaxLength="255" DisplayName="Test2" Description=""
10:  Direction="None" Type="Text" Overwrite="TRUE"
11:  xmlns="http://schemas.microsoft.com/sharepoint/" />
12:  <Field ID="{3ECFF1FE-F56B-4556-8805-4570D9422FF4}" Name="Workflow1Task1_Test3"
13:  MaxLength="255" DisplayName="Test3" Description=""
14:  Direction="None" Type="Text" Overwrite="TRUE"
15:  xmlns="http://schemas.microsoft.com/sharepoint/" />
16: </Elements>

7. Now we create a new “Module” project item named “Workflow 1 Forms”. In this module we will store the Task Form.

image

Remove the “Sample.txt” file from the created module.

Create a new “Application Page” project item named “Task1Form.aspx”.

image

This project item will stored in the folder “Layoutsik.SharePoint2010.Workflow”.

image

Move the project item “Task1Form.aspx” using Drag & Drop into the module “Workflow 1 Forms”.

image

Remove the “Layouts” folder from the project. It should be empty.

8. We open “Task1Form.aspx”

First we need to edit the “Page” tag of the ASPX site.

1:  <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Task1Form.aspx.cs" Inherits="ik.SharePoint2010.Workflow.Task1Form" MasterPageFile="~masterurl/default.master"  %>

Now we insert the following code into the “PlaceHolderMain” content placeholder.

1:     <asp:ScriptManagerProxy runat="server" ID="ProxyScriptManager">
2:     </asp:ScriptManagerProxy>
3:     <table width="100%" border="0" cellpadding="0" cellspacing="0">
4:         <tr>
5:             <td valign="top">
6:                 <table cellspacing="0" cellpadding="4" border="0" width="100%">
7:                     <tr>
8:                         <td class="ms-vb">
9:                             &nbsp;
10:                         </td>
11:                     </tr>
12:                 </table>
13:                 <table border="0" width="100%">
14:                     <tr>
15:                         <td>
16:                             <table border="0" cellspacing="0" width="100%">
17:                                 <tr>
18:                                     <td class="ms-formlabel" valign="top" nowrap="true" width="25%">
19:                                         <b>Title:</b>
20:                                     </td>
21:                                     <td class="ms-formbody" valign="top" width="75%">
22:                                         <SharePoint:FormField runat="server" ID="ff4" ControlMode="Display" FieldName="Title" /><br />
23:                                     </td>
24:                                 </tr>
25:                                 <tr>
26:                                     <td width="25%" class="ms-formlabel">
27:                                         <b>Test1:</b>
28:                                     </td>
29:                                     <td width="75%" class="ms-formbody">
30:                                         <SharePoint:FormField runat="server" ID="ff1" ControlMode="Edit" FieldName="Workflow1Task1_Test1" />
31:                                         <SharePoint:FieldDescription runat="server" ID="ff1description" FieldName="Workflow1Task1_Test1"
32:                                             ControlMode="Edit" />
33:                                     </td>
34:                                 </tr>
35:                                 <tr>
36:                                     <td width="25%" class="ms-formlabel">
37:                                         <b>Test2:</b>
38:                                     </td>
39:                                     <td width="75%" class="ms-formbody">
40:                                         <SharePoint:FormField runat="server" ID="ff2" ControlMode="Edit" FieldName="Workflow1Task1_Test2" />
41:                                         <SharePoint:FieldDescription runat="server" ID="ff2description" FieldName="Workflow1Task1_Test2"
42:                                             ControlMode="Edit" />
43:                                     </td>
44:                                 </tr>
45:                                 <tr>
46:                                     <td width="25%" class="ms-formlabel">
47:                                         <b>Test3:</b>
48:                                     </td>
49:                                     <td width="75%" class="ms-formbody">
50:                                         <SharePoint:FormField runat="server" ID="ff3" ControlMode="Edit" FieldName="Workflow1Task1_Test3" />
51:                                         <SharePoint:FieldDescription runat="server" ID="ff3description" FieldName="Workflow1Task1_Test3"
52:                                             ControlMode="Edit" />
53:                                     </td>
54:                                 </tr>
55:                             </table>
56:                         </td>
57:                     </tr>
58:                 </table>
59:                 <table cellspacing="0" cellpadding="4" border="0" width="100%">
60:                     <tr>
61:                         <td nowrap="nowrap" class="ms-vb">
62:                             <asp:Button Text="Save As Draft" runat="server" ID="btnSaveAsDraft" />
63:                         </td>
64:                         <td>
65:                             <asp:Button Text="Complete Task" runat="server" ID="btnComplete" />
66:                         </td>
67:                         <td nowrap="nowrap" class="ms-vb" width="99%">
68:                             <asp:Button Text="Cancel" runat="server" ID="btnCancel" />
69:                         </td>
70:                     </tr>
71:                 </table>
72:             </td>
73:             <td width="1%" class="ms-vb" valign="top">&nbsp;</td>
74:         </tr>
75:     </table>
76:

Now we add some ASP.NET code into the “PlaceHolderPageTitle” content placeholder.

1:     <SharePoint:ListFormPageTitle runat="server" />

Furthermore we add this lines of code into the “PlaceHolderPageTitleInTitleArea” content placeholder.

1:     <span class="die">
2:         <SharePoint:ListProperty Property="LinkTitle" runat="server" ID="ID_LinkTitle" />
3:         : </span>
4:     <SharePoint:ListItemProperty ID="ID_ItemProperty" MaxLength="40" runat="server" />

At least we add the following code into the “PlaceHolderAdditionalPageHead” content placeholder.

1:     <SharePoint:UIVersionedContent UIVersion="4" runat="server">
2:         <contenttemplate>
3:             <SharePoint:CssRegistration Name="forms.css" runat="server"/>
4:         </contenttemplate>
5:     </SharePoint:UIVersionedContent>

You can see the input fields for the three fields of Task 1. Furthermore you see three buttons. For them we now create some “code behind”.

1: using System;
2: using Microsoft.SharePoint;
3: using Microsoft.SharePoint.WebControls;
4: using Microsoft.SharePoint.Utilities; 
5:
6: namespace ik.SharePoint2010.Workflow
7: {
8:     public partial class Task1Form : WebPartPage
9:     {
10:         protected void Page_Load(object sender, EventArgs e)
11:         {
12:             btnSaveAsDraft.Click += new EventHandler (btnSaveAsDraft_Click);
13:             btnComplete.Click += new EventHandler (btnComplete_Click);
14:             btnCancel.Click += new EventHandler (btnCancel_Click);
15:         }
16:
17:         void btnCancel_Click(object sender, EventArgs e)
18:         {
19:             CloseForm();
20:         }
21:
22:         private void CloseForm()
23:         {
24:             if ( ( SPContext.Current != null ) && SPContext.Current.IsPopUI )
25:             {
26:                 this.Context.Response.Write("<script>window.frameElement.commitPopup();</script>" );
27:                 this.Context.Response.Flush();
28:                 this.Context.Response.End();
29:             }
30:             else
31:             {
32:                 string str = this.Page.Request.QueryString["Source"];
33:                 if ( ( str != null ) && ( str.Length > 0 ) )
34:                 {
35:                     SPUtility.Redirect(string.Empty, SPRedirectFlags.UseSource, this.Context);
36:                 }
37:             }
38:         }
39:
40:         void btnComplete_Click(object sender, EventArgs e)
41:         {
42:             SPList l = SPContext.Current.List;
43:             SPListItem li = SPContext.Current.ListItem;
44:             li[SPBuiltInFieldId.TaskStatus] = "Tasks_Completed";
45:             li[SPBuiltInFieldId.PercentComplete] = 1;
46:
47:             SaveButton.SaveItem(SPContext.Current, false, "" );
48:
49:             CloseForm();
50:         }
51:
52:         void btnSaveAsDraft_Click(object sender, EventArgs e)
53:         {
54:             SaveButton.SaveItem(SPContext.Current, false, "" );
55:
56:             CloseForm();
57:         }
58:     }
59: }

We need to modify the “Elements.xml” file of the module named “Workflow 1 Forms”.

1: <?xml version="1.0" encoding="utf-8"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3:  <Module Name="Workflow 1 Forms" Url="Workflow1Forms" RootWebOnly="FALSE">
4:  <File Path="Workflow 1 FormsTask1Form.aspx" Url="Task1Form.aspx" />
5:  </Module>
6: </Elements> 

This specifies there to store the “Task1Form.aspx” file in the site structure.

9. In the next step we create the task list that will contain our workflow tasks.

First create a “List Definition” project item named “Workflow 1 Tasks”.

image

Use “Workflow 1 Tasks” as name of the list definition and “Tasks” as base type. Check “Add a list instance…”.

image

10. Now open “Elements.xml” of the new list definition project item.

image

We need to change the identifier of the list type we create! – It must be “107”. This is the list template ID for workflow tasks lists in SharePoint. The workflow designer will search for a list with this type inside the site where a new workflow will be created.

Here is the content of “Elements.xml” after our modification:

1: <?xml version="1.0" encoding="utf-8"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3:  <ListTemplate
4:  Name="Workflow 1 Tasks"
5:  Type="107"
6:  BaseType="0"
7:  OnQuickLaunch="TRUE"
8:  SecurityBits="11"
9:  Sequence="360"
10:  DisplayName="Workflow 1 Tasks"
11:  Description="Tasks of Workflow 1"
12:  Image="/_layouts/images/itgen.png"/>
13: </Elements>  

11. Now we modify the “Elements.xml” file of the list instance that will be created during deployment:

image

Here we also need to modify the type identifier to “107”. Furthermore we change the list url: “Lists/Workflow1Tasks”.

Here is the complete content of “Elements.xml”:

1: <?xml version="1.0" encoding="utf-8"?>
2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
3:  <ListInstance Title="Workflow 1 Tasks"
4:  OnQuickLaunch="TRUE"
5:  TemplateType="107"
6:  Url="Lists/Workflow1Tasks"
7:  Description="Tasks of Workflow 1">
8:  </ListInstance>
9: </Elements> 

In a “real world” scenario we would prevent the list from being listed on the Quick Launch bar. So the corresponding parameter must be set to “FALSE”.

12. Now we need to modify the “Schema.xml” file of the list definition.

image

First we set the list type to “107” and configure some other attributes:

1: <List xmlns:ows="Microsoft SharePoint" Title="Workflow 1 Tasks"
2:  FolderCreation="FALSE" Direction="$Resources:Direction;"
3:  EnableContentTypes="TRUE" VersioningEnabled="TRUE"
4:  Url="Lists/Workflow1Tasks"
5:  Type="107" BaseType="0"
6:  xmlns="http://schemas.microsoft.com/sharepoint/">
7: […]

Now remove the the content types defined in the “ContentTypes” tag in the “schema.xml” file.

image

Insert this content type definition into the “ContentTypes” tag:

1:  <ContentType ID="0x01080100FFbc98c2529347a5886b8d2576b954ef"
2:  Name="Workflow 1 Tasks 1"
3:  Group="Workflow 1 Tasks"
4:  Description="Content Type of Tasks 1 of Workflow 1">
5:  <FieldRefs>
6:  <FieldRef ID="{2FE15855-3CAB-44A6-AB29-1600204FCA20}" Name="Workflow1Task1_Test1" DisplayName="Test1" Required="FALSE" Hidden="FALSE" ReadOnly="FALSE" PITarget="" PrimaryPITarget="" PIAttribute="" PrimaryPIAttribute="" Aggregation="" Node="" />
7:  <FieldRef ID="{517B22A5-1B89-4C24-82BE-3D4FD99645BC}" Name="Workflow1Task1_Test2" DisplayName="Test2" Required="FALSE" Hidden="FALSE" ReadOnly="FALSE" PITarget="" PrimaryPITarget="" PIAttribute="" PrimaryPIAttribute="" Aggregation="" Node="" />
8:  <FieldRef ID="{3ECFF1FE-F56B-4556-8805-4570D9422FF4}" Name="Workflow1Task1_Test3" DisplayName="Test3" Required="FALSE" Hidden="FALSE" ReadOnly="FALSE" PITarget="" PrimaryPITarget="" PIAttribute="" PrimaryPIAttribute="" Aggregation="" Node="" />
9:
10:  <FieldRef ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" />
11:  <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" ShowInNewForm="TRUE" ShowInEditForm="TRUE" />
12:  <FieldRef ID="{c3a92d97-2b77-4a25-9698-3ab54874bc6f}" Name="Predecessors" />
13:  <FieldRef ID="{a8eb573e-9e11-481a-a8c9-1104a54b2fbd}" Name="Priority" />
14:  <FieldRef ID="{c15b34c3-ce7d-490a-b133-3f4de8801b76}" Name="Status" />
15:  <FieldRef ID="{d2311440-1ed6-46ea-b46d-daa643dc3886}" Name="PercentComplete" />
16:  <FieldRef ID="{53101f38-dd2e-458c-b245-0c236cc13d1a}" Name="AssignedTo" />
17:  <FieldRef ID="{7662cd2c-f069-4dba-9e35-082cf976e170}" Name="Body" />
18:  <FieldRef ID="{64cd368d-2f95-4bfc-a1f9-8d4324ecb007}" Name="StartDate" />
19:  <FieldRef ID="{cd21b4c2-6841-4f9e-a23a-738a65f99889}" Name="DueDate" />
20:  <FieldRef ID="{58ddda52-c2a3-4650-9178-3bbc1f6e36da}" Name="WorkflowLink" />
21:  <FieldRef ID="{16b6952f-3ce6-45e0-8f4e-42dac6e12441}" Name="OffsiteParticipant" />
22:  <FieldRef ID="{4a799ba5-f449-4796-b43e-aa5186c3c414}" Name="OffsiteParticipantReason" />
23:  <FieldRef ID="{18e1c6fa-ae37-4102-890a-cfb0974ef494}" Name="WorkflowOutcome" />
24:  <FieldRef ID="{e506d6ca-c2da-4164-b858-306f1c41c9ec}" Name="WorkflowName" />
25:  <FieldRef ID="{ae069f25-3ac2-4256-b9c3-15dbc15da0e0}" Name="GUID" />
26:  <FieldRef ID="{8d96aa48-9dff-46cf-8538-84c747ffa877}" Name="TaskType" />
27:  <FieldRef ID="{17ca3a22-fdfe-46eb-99b5-9646baed3f16}" Name="FormURN" />
28:  <FieldRef ID="{78eae64a-f5f2-49af-b416-3247b76f46a1}" Name="FormData" />
29:  <FieldRef ID="{8cbb9252-1035-4156-9c35-f54e9056c65a}" Name="EmailBody" />
30:  <FieldRef ID="{47f68c3b-8930-406f-bde2-4a8c669ee87c}" Name="HasCustomEmailBody" />
31:  <FieldRef ID="{cb2413f2-7de9-4afc-8587-1ca3f563f624}" Name="SendEmailNotification" />
32:  <FieldRef ID="{4d2444c2-0e97-476c-a2a3-e9e4a9c73009}" Name="PendingModTime" />
33:  <FieldRef ID="{35363960-d998-4aad-b7e8-058dfe2c669e}" Name="Completed" />
34:  <FieldRef ID="{1bfee788-69b7-4765-b109-d4d9c31d1ac1}" Name="WorkflowListId" />
35:  <FieldRef ID="{8e234c69-02b0-42d9-8046-d5f49bf0174f}" Name="WorkflowItemId" />
36:  <FieldRef ID="{1c5518e2-1e99-49fe-bfc6-1a8de3ba16e2}" Name="ExtendedProperties" />
37:  </FieldRefs>
38:  <XmlDocuments>
39:  <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
40:  <FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
41:  <Display>ListForm</Display>
42:  <Edit>ListForm</Edit>
43:  <New>ListForm</New>
44:  </FormTemplates>
45:  </XmlDocument>
46:  <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
47:  <FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
48:  <Edit>Workflow1Forms/Task1Form.aspx</Edit>
49:  </FormUrls>
50:  </XmlDocument>
51:  </XmlDocuments>
52:  </ContentType>
53:
54:

You see the “FormUrls” tag? Inside this tag we specify our custom form template we created before.

The new content type is derived from the “Workflow Task” content type 0x010801. – In the “FieldRefs” section we add our fields we need inside the workflow.

13. Now we need to add the field definitions of our custom fields to the “Schema.xml”. Copy them from the “Elements.xml”  file of project item “Workflow 1 Fields” into the “Fields” tag of “Schema.xml”:

image

Furthermore you need to add a field definition for the field “Completed”:

1:  <Field ID="{35363960-D998-4aad-B7E8-058DFE2C669E}" Name="Completed"
2:  SourceID="http://schemas.microsoft.com/sharepoint/v3"
3:  StaticName="Completed" Group="Base Columns" Type="Boolean"
4:  DisplayName="Completed" Hidden="TRUE" Sealed="TRUE"
5:  Overwrite="TRUE" xmlns="http://schemas.microsoft.com/sharepoint/">
6:  <Default>FALSE</Default>
7:  </Field> 

14. Now deploy the project.

While deployment you may get this dialog:

image

Check “Do not prompt…”. Press “Resolve Automatically”  – it’s your only option Smile.

15. Have a look into the site using the browser. – We will test our “Edit” form. Remember that we did not specify special “New” form or “Display” form. This you could do the same way as you created the “Edit” form.

We see our lists in the Quick Launch.

image

Open the “Workflow 1 Tasks” list. On the Ribbon open the “Items” tab. We see our Content Type in the New Item submenu:

image

Create an item of this type. You see a standard “New” form and on it you see our three “Test” fields:

image

Enter some data and press  “Save”.

image

Open the item  in “Edit” form. Now you should see our custom list form.

image

If you click “Save As Draft” your changes will be stored in the task item. If you click “Complete Task” two item fields will be changed in addition to the changes made in the form: It sets “Status” to “Tasks_Completed” and “% Complete” to “100”. You can see this in the Code Behind file of the list form.

Test all buttons.

After “Complete Task”:

image

You see: “% Complete” is set to “100 %”.

So far our projects works as expected.

See Part 2 for the next steps… There I will show you how to create a simple Sequential Workflow that uses our Task Form.

Here is part 2: https://blog.kenaro.com/2011/03/30/walkthrough-creating-a-simple-sequential-workflow-with-a-custom-task-form-in-sharepoint-2010-using-visual-studio-2010-part-2-of-2/