Walkthrough: Creating a simple Sequential Workflow with a custom Task Form in SharePoint 2010 using Visual Studio 2010 (Part 2 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 2 of 2. It describes the steps 16 to 27. Read part 1 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-1-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 (again)…

16. Now we need to add a list that we use to connect a workflow with. We create a “List Definition” project item named “Workflow 1 Host List”.

image

image

We change the “Elements.xml” file of the List Instance project item.

image

We change the Title, URL and Description.

image

17. Deploy the project.

18. Now we start creating a Sequential Workflow.

Create a “Sequential Workflow” project item named “Workflow 1”.

image

Create the workflow as “List Workflow”.

image

You will see all the lists we created in the previous steps. Remember: You have to (successfully) deploy the project before you can use the lists that will be created during deployment. – Without previous deployment the wizard may fail before you see the following dialog page.

In the first dropdown list choose “Workflow 1 Host List”. All other lists should be selected automatically because there is only one usable list of every needed type.

image

Select all “Workflow start” options: “Manually”, “Item Created”, “Item Updated”.

image

You get:

image

19. We will add a “Create Task With Content Type” workflow activity that will use our “Workflow 1 Task 1” we created in the steps 1 to 15 (blog post “part 1”).

Open the “Toolbox” pane and drag “CreateTaskWithContentType” into the Workflow Designer.

image

After you did this you get:

image

Now we need to configure the workflow activity in the “Properties” pane.

Set “correlationToken” to “task1” and the sub element of “correlationToken” named “OwnerActivityName” to “Workflow_1”.

image

Now we need to create code behind properties for some activity properties. As example I’ll show how to create a code behind property for “ContentTypeId”.

Click on the Button “…” at the activity property edit box:

image

In the dialog select the tab “Bind to a new member”. Enter the name “New member name” and select the “Create Property” radio button. Press “OK”.

image

Here are the property bindings you need to create:

  • “ContentTypeId” = “task1ContentTypeId”
  • “TaskId” = “task1Guid”
  • “TaskProperties” = “task1Properties”

In “MethodInvoking” type “createTask1Invoke” and press ENTER. – You’ll be directed to the code editor.

(The activity properties look like this after you finished: image)

In the method we initialize the task1-Properties we created before. The “ContentTypeId” is taken from the “Schema.xml” file of our “Workflow 1 Tasks” list where we created the Content Type “Workflow 1 Task 1”.

After that your method looks like this:

image

20. Back in the workflow designer. We drag a “While” activity from the Toolbox pane into the workflow behind the “Create Task” activity.

image

Into the “While” activity we drag a “Sequence” property from the Toolbox pane. We get:

image

Into the “Sequence” activity we drag a “OnTaskChanged” activity from the Toolbox pane. We get:

image

Now we edit the properties of the created “OnTaskChanged” activity. We have to set the “CorrelationToken” as described above. Additionally we specify new property bindings:

  • “AfterProperties” = “task1Changed1_AfterProperties”
  • “BeforeProperties” = “task1Changed1_BeforeProperties”
  • “TaskId” => Bind to the existing member “task1Guid”!!!
    image

In the “Invoked” property add the method name “task1Changed1Invoke”. – The code editor will be opened.

(The activity properties look like this after you finished: image)

21. In the code we add a property at class level:

image

This property we set inside the “OnWorkflowItemChanged” activity. It will contain the information whether the Workflow Task Item we created before was “Completed” by the assigned user.

In the “task1Changed1Invoke” method we set the “isFinished” property.

image

22. Now we need to specify the condition for the “While” activity. In the workflow designer select this activity. In the “Condition” property select “Code Condition” and expand the property. Enter “while1Invoke” in the subproperty with (the same) name “Condition”.

image

In the code editor enter the code for “while1Invoke” like this:

image

The “e.Result” property has to be “TRUE” as long as the while loop should run. It should not run anymore (“e.Result = false”) if “isFinished” is TRUE.

23. In the workflow designer add a “CompleteTask” activity behind the “While” activity. Drag the “CompleteTask” activity from the Toolbar pane into the workflow designer. We get:

image

Select the “completeTask1” activity and edit it’s properties in the Property pane.

  • Bind “TaskId” to the existing member “task1Guid”
  • Create a new member namend “task1Outcome” for binding to “TaskOutcome”
  • Set the “CorrelationToken” to “task1”

24. Whew! Smile

25. Deploy the project. – It deployed successfully ?! – Congratulations!

26. Open the browser and navigate to “http://sharepoint.local/sites/workflow”. Open the “Workflow 1 Host List”. Add a new item… – The “Workflow 1” should start automatically.

image

image

(After F5 / page refresh:)

image

Click “In Process”. We see the Workflow Status page:

image

In the “Tasks” section open the context menu of the element “(no title)”. – We did not set a title for the created “Task 1”. This could be done in the method “createTask1Invoke”.

image

Click “Edit Item”.

There it is!!! :

image

Just edit a property and click “Save As Draft”.

On the Workflow Status page you see that the workflow is still running but was modified (“Last run”).

image

Then edit the task again. Click “Complete Task”.

After that the workflow is “Completed”:

image

27. We have done it! – Now we could add a “Task 2” for this “Workflow 1” where some other data is requested from users.

With that method you will be able to add custom forms for Workflow Tasks. You could also use forms you create with Silverlight !!!

Thank you for your kind attention!

Sample Visual Studio 2010 project for creating a custom SharePoint 2010 Ribbon tab on runtime (!)

The related articles on my blog are:


I wanted to create a sample project to show you who you can create a custom SharePoint 2010 Ribbon tab at runtime! – Especially: How to active the custom ribbon tab on “page loaded” (no need to click it!) So this article is an addition to my explanations in my previous article “How To Activate SharePoint Ribbon Tab by JavaScript Code”.

You can download the project from Codeplex:

http://spcustomribbondemo.codeplex.com

You are invited to download / update / improve the project! – I’d like to insert more samples in the project about how to deploy Ribbon customizations. In the sample I created a ribbon in a custom list view web page. (You’ll see in the code).

To use the project you need a SharePoint web application named “http://sharepoint.local” and a site collection named “ribbon”.

Deploy the project.

Open the SharePoint site and it’s list in your browser.

image

You’ll see:

image

This tab is actived automatically on page load!!!

The source code of my project is inspired by some blog post of this people:

These are the steps of development I’ve done:

1. Create an empty SharePoint 2010 project in Visual Studio 2010

2. Create an “List Definition” project item.

3. Add a “Application Page” project item.

4. Move the new application page to the “List Definition” project item.

5. Change the base class to “WebPartPage”

6. Modify the “Page” tag of the aspx page: replace “DynamicalMasterPageFile” with “MasterPageFile”

7. Replace the content of the aspx file with this code:

 

1: <asp:Content  ContentPlaceHolderID="PlaceHolderAdditionalPageHead"  runat="server"> 

2: </asp:Content> 

3:

4: <asp:Content  ContentPlaceHolderID="PlaceHolderMain"  runat="server"> 

5:     <WebPartPages:WebPartZone  runat="server"  FrameType="None"  ID="Main2"  Title="loc:Main"> 

6:         <ZoneTemplate> 

7:         </ZoneTemplate> 

8:     </WebPartPages:WebPartZone> 

9: </asp:Content> 

10: <asp:Content  ContentPlaceHolderID="PlaceHolderPageTitle"  runat="server"> 

11: </asp:Content> 

12: <asp:Content  ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea"  runat="server"> 

13: </asp:Content>

14:

8. Insert the following code into the CS file of the ASPX file. This code will create the custom Ribbon tab. (The Ribbon tab XML definition code could be created dynamically!!!)

 

1: using  System;

2: using  System.Security.Permissions;

3: using  Microsoft.SharePoint.Security;

4: using  Microsoft.SharePoint.WebPartPages;

5: using  Microsoft.SharePoint;

6: using  Microsoft.SharePoint.WebControls;

7: using  System.Web.UI.WebControls.WebParts;

8: using  System.Diagnostics;

9: using  System.Xml;

10: using  System.Collections.Generic;

11: using  System.Reflection;

12: using  System.Text;

13:

14: namespace  ik.SharePoint2010.SPCustomTabRibbonDemo.Layouts.SPCustomTabRibbonDemo

15: {

16:     public  partial  class  AllItems  : WebPartPage 

17:     {

18:         protected  void  Page_Load(object  sender, EventArgs  e)

19:         {

20:         }

21:

22:         private  string  tab = @" 

23:           <Tab Id=""ik.SharePoint2010.Ribbon.CustomTab"" Title=""ik-ribbon"" Description=""Ingo's Command Group"" Sequence=""501""> 

24:             <Scaling Id=""ik.SharePoint2010.Ribbon.CustomTab.Scaling""> 

25:               <MaxSize Id=""ik.SharePoint2010.Ribbon.CustomTab.Group1.MaxSize"" 

26:                        GroupId=""ik.SharePoint2010.Ribbon.CustomTab.Group1"" 

27:                        Size=""OneLarge""/> 

28:               <Scale Id=""ik.SharePoint2010.Ribbon.CustomTab.Group1.Scaling.CustomTabScaling"" 

29:                      GroupId=""ik.SharePoint2010.Ribbon.CustomTab.Group1"" 

30:                      Size=""OneLarge""/> 

31:               <MaxSize Id=""ik.SharePoint2010.Ribbon.CustomTab.Group2.MaxSize"" 

32:                        GroupId=""ik.SharePoint2010.Ribbon.CustomTab.Group2"" 

33:                        Size=""OneLarge""/> 

34:               <Scale Id=""ik.SharePoint2010.Ribbon.CustomTab.Group2.Scaling.CustomTabScaling"" 

35:                      GroupId=""ik.SharePoint2010.Ribbon.CustomTab.Group2"" 

36:                      Size=""OneLarge""/> 

37:               <MaxSize Id=""ik.SharePoint2010.Ribbon.CustomTab.Group3.MaxSize"" 

38:                        GroupId=""ik.SharePoint2010.Ribbon.CustomTab.Group3"" 

39:                        Size=""OneLarge""/> 

40:               <Scale Id=""ik.SharePoint2010.Ribbon.CustomTab.Group3.Scaling.CustomTabScaling"" 

41:                      GroupId=""ik.SharePoint2010.Ribbon.CustomTab.Group3"" 

42:                      Size=""OneLarge""/> 

43:             </Scaling> 

44:             <Groups Id=""ik.SharePoint2010.Ribbon.CustomTab.Groups""> 

45:               <Group Id=""ik.SharePoint2010.Ribbon.CustomTab.Group1"" 

46:                      Description=""Ingo's Custom Ribbon Demo 1"" 

47:                      Title=""Ingo's Custom Ribbon Demo 1"" 

48:                      Sequence=""10"" 

49:                      Template=""ik.SharePoint2010.Ribbon.Templates.OneLarge""> 

50:                 <Controls Id=""ik.SharePoint2010.Ribbon.CustomTab.Group1.Controls""> 

51:                   <Button Id=""ik.SharePoint2010.Ribbon.CustomTab.Group1.Button1"" 

52:                           Command=""ik.SharePoint2010.Command.Button1"" 

53:                           Description=""Do something"" 

54:                           Sequence=""10"" 

55:                           LabelText=""Do something"" 

56:                           Image16by16=""/_layouts/images/DOC16.GIF"" 

57:                           Image32by32=""/_layouts/images/DOC32.GIF"" 

58:                           TemplateAlias=""cust1""/> 

59:                 </Controls> 

60:               </Group> 

61:               <Group Id=""ik.SharePoint2010.Ribbon.CustomTab.Group3"" 

62:                      Description=""Ingo's Custom Ribbon Demo 3"" 

63:                      Title=""Ingo's Custom Ribbon Demo 3"" 

64:                      Sequence=""30"" 

65:                      Template=""ik.SharePoint2010.Ribbon.Templates.OneLarge""> 

66:                 <Controls Id=""ik.SharePoint2010.Ribbon.CustomTab.Group3.Controls""> 

67:                   <Button Id=""ik.SharePoint2010.Ribbon.CustomTab.Group3.Button3"" 

68:                           Command=""ik.SharePoint2010.Command.Button3"" 

69:                           Description=""Do more"" 

70:                           Sequence=""10"" 

71:                           LabelText=""Do more"" 

72:                           Image32by32=""/_layouts/images/PPEOPLE.GIF"" 

73:                           TemplateAlias=""cust1""/> 

74:                 </Controls> 

75:               </Group> 

76:               <Group Id=""ik.SharePoint2010.Ribbon.CustomTab.Group2"" 

77:                      Description=""Ingo's Custom Ribbon Demo 2"" 

78:                      Title=""Ingo's Custom Ribbon Demo 2"" 

79:                      Sequence=""20"" 

80:                      Template=""ik.SharePoint2010.Ribbon.Templates.OneLarge""> 

81:                 <Controls Id=""ik.SharePoint2010.Ribbon.CustomTab.Group2.Controls""> 

82:                   <Button Id=""ik.SharePoint2010.Ribbon.CustomTab.Group2.Button2"" 

83:                           Command=""ik.SharePoint2010.Command.Button2"" 

84:                           Description=""Do nothing"" 

85:                           Sequence=""10"" 

86:                           LabelText=""Do nothing"" 

87:                           Image32by32=""/_layouts/images/PPEOPLE.GIF"" 

88:                           TemplateAlias=""cust1""/> 

89:                 </Controls> 

90:               </Group> 

91:             </Groups> 

92:           </Tab> 

93:

94: " ;

95:         private  string  tabTempl = @" 

96:           <GroupTemplate Id=""ik.SharePoint2010.Ribbon.Templates.OneLarge""> 

97:             <Layout Title=""OneLarge"" LayoutTitle=""OneLarge""> 

98:               <Section Alignment=""Top"" Type=""OneRow""> 

99:                 <Row> 

100:                   <ControlRef DisplayMode=""Large"" TemplateAlias=""cust1"" /> 

101:                 </Row> 

102:               </Section> 

103:             </Layout> 

104:           </GroupTemplate> 

105: " ;

106:

107:         protected  override  void  OnPreRender(EventArgs  e)

108:         {

109:             //Debugger.Break(); 

110:             SPRibbon  r = Microsoft.SharePoint.WebControls.SPRibbon .GetCurrent(this .Page);

111:             XmlDocument  rx = new  XmlDocument ();

112:             rx.LoadXml(tab);

113:             r.RegisterDataExtension(rx.FirstChild, "Ribbon.Tabs._children" );

114:             rx.LoadXml(tabTempl);

115:             r.RegisterDataExtension(rx.FirstChild, "Ribbon.Templates._children" );

116:

117:             List <IRibbonCommand > commands = new  List <IRibbonCommand >();

118:             commands.Add(new  SPRibbonCommand ("ik.SharePoint2010.Command.Button1" , "ikSharePoint2010CommandButton1Action()" , "true" ));

119:             commands.Add(new  SPRibbonCommand ("ik.SharePoint2010.Command.Button2" , "ikSharePoint2010CommandButton2Action()" , "ikSharePoint2010CommandButton2Enable()" ));

120:             commands.Add(new  SPRibbonCommand ("ik.SharePoint2010.Command.Button3" , "ikSharePoint2010CommandButton3Action()" , "ikSharePoint2010CommandButton3Enable()" ));

121:

122:             SPRibbonScriptManager  rsm = new  SPRibbonScriptManager ();

123:

124:             ScriptLink .RegisterScriptAfterUI(this .Page, "SP.Runtime.js" , false , true );

125:             ScriptLink .RegisterScriptAfterUI(this .Page, "SP.js" , false , true );

126:             ScriptLink .RegisterScriptAfterUI(this .Page, "CUI.js" , false , true );

127:             ScriptLink .RegisterScriptAfterUI(this .Page, "SP.Ribbon.js" , false , true );

128:             ScriptLink .RegisterScriptAfterUI(this .Page, "ik.SharePoint2010.SPCustomTabRibbonDemo/ikactions.js" , false , true );

129:             ScriptLink .RegisterScriptAfterUI(this .Page, "ik.SharePoint2010.SPCustomTabRibbonDemo/ikribbon.UI.js" , false , true );

130:

131:             rsm.RegisterGetCommandsFunction(this .Page, "getGlobalCommands" , commands);

132:             rsm.RegisterCommandEnabledFunction(this .Page, "commandEnabled" , commands);

133:             rsm.RegisterHandleCommandFunction(this .Page, "handleCommand" , commands);

134:

135:             string  script = @" 

136:                 <script language=""javascript"" defer=""true""> 

137:                 //<![CDATA[ 

138:                    function ikribbonInit1() {  

139:                      ikribbonInit(); 

140:                    } 

141:                    ExecuteOrDelayUntilScriptLoaded(ikribbonInit1,'); 

142:                 //]]> 

143:                 </script>" ;

144:             ClientScript.RegisterClientScriptBlock(this .GetType(), "InitPageComponent" , script);

145:

146:             r.MakeTabAvailable("ik.SharePoint2010.Ribbon.CustomTab" );

147:             r.SetInitialTabId("ik.SharePoint2010.Ribbon.CustomTab" , "" );

148:             base .OnPreRender(e);

149:         }

150:

151:         protected  override  void  OnInitComplete(EventArgs  e)

152:         {

153:             base .OnInitComplete(e);

154:         }

155:     }

156: }

157:

158:

9. You need two JavaScript files in the project’s sub folder in the SharePoint’s hive folder “Layouts”.

This file contains code for the Ribbon:

 

1: function  ULS_SP() {

2:     if  (ULS_SP.caller) {

3:         ULS_SP.caller.ULSTeamName = "Windows SharePoint Services 4" ;

4:         ULS_SP.caller.ULSFileName = "ikribbon.UI.js" ;

5:     }

6: }

7:

8: Type.registerNamespace(< span> );

9:

10: //  Page Component 

11: ikribbon.UI.PageComponent = function  () {

12:     ULS_SP();

13:     ikribbon.UI.PageComponent.initializeBase(this );

14: }

15: ikribbon.UI.PageComponent.initialize = function  () {

16:     ULS_SP();

17:     ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null , ikribbon.UI.PageComponent.initializePageComponent), < span> );

18: }

19: ikribbon.UI.PageComponent.initializePageComponent = function  () {

20:     ULS_SP();

21:     var  ribbonPageManager = SP.Ribbon.PageManager.get_instance();

22:     if  (null  !== ribbonPageManager) {

23:         ribbonPageManager.addPageComponent(ikribbon.UI.PageComponent.instance);

24:         ribbonPageManager.get_focusManager().requestFocusForComponent(ikribbon.UI.PageComponent.instance);

25:     }

26: }

27: ikribbon.UI.PageComponent.refreshRibbonStatus = function  () {

28:     SP.Ribbon.PageManager.get_instance().get_commandDispatcher().executeCommand(Commands.CommandIds.ApplicationStateChanged, null );

29: }

30: ikribbon.UI.PageComponent.prototype = {

31:     getFocusedCommands: function  () {

32:         ULS_SP();

33:         return  [];

34:     },

35:     getGlobalCommands: function  () {

36:         ULS_SP();

37:         return  getGlobalCommands();

38:     },

39:     isFocusable: function  () {

40:         ULS_SP();

41:         return  true ;

42:     },

43:     receiveFocus: function  () {

44:         ULS_SP();

45:         return  true ;

46:     },

47:     yieldFocus: function  () {

48:         ULS_SP();

49:         return  true ;

50:     },

51:     canHandleCommand: function  (commandId) {

52:         ULS_SP();

53:         return  commandEnabled(commandId);

54:     },

55:     handleCommand: function  (commandId, properties, sequence) {

56:         ULS_SP();

57:         return  handleCommand(commandId, properties, sequence);

58:     }

59: }

60:

61: var  ikribbonActiveInterval = null ;

62:

63: function  ikribbonActiveRibbonTab() {

64:     try  {

65:         window.clearInterval(ikribbonActiveInterval);

66:

67:         if  (typeof  (_ribbonStartInit) == "function" ) {

68:             _ribbonStartInit(< span> , false , null );

69:         }

70:

71:         if  (true  && typeof  (_ribbonReadyForInit) == < span>  && !_ribbonReadyForInit()) {

72:             ikribbonActiveInterval = window.setInterval("ikribbonActiveRibbonTab()" , 100);

73:         }

74:     } catch  (e2) {

75:     };

76: };

77:

78: function  ikribbonInit() {

79:     alert(< span> );

80:     ikribbon.UI.PageComponent.registerClass(< span> , CUI.Page.PageComponent);

81:     ikribbon.UI.PageComponent.instance = new  ikribbon.UI.PageComponent();

82:     ikribbon.UI.PageComponent.initialize();

83:

84:     ExecuteOrDelayUntilScriptLoaded(ikribbonActiveRibbonTab, < span> );

85: }

86:

87: NotifyScriptLoadedAndExecuteWaitingJobs("ikribbon.UI.js" );

88:

89:

90:

91:

This file contains code for the actions executed on clicking the custom Ribbon buttons:

 

1: function  ikSharePoint2010CommandButton1Action() {

2:     alert("Button 1 pressed" );

3: }

4:

5: function  ikSharePoint2010CommandButton1Enable() {

6:     return  true ;

7: }

8:

9: function  ikSharePoint2010CommandButton2Action() {

10:     alert("Button 2 pressed" );

11: }

12:

13: function  ikSharePoint2010CommandButton2Enable() {

14:     return  true ;

15: }

16:

17: function  ikSharePoint2010CommandButton3Action() {

18:     alert("Button 3 pressed" );

19: }

20:

21: function  ikSharePoint2010CommandButton3Enable() {

22:     return  true ;

23: }

24:

25:

10. Modify the list definitions “schema.xml” file: Edit the following line:

<View BaseViewID=”1″ Type=”HTML” WebPartZoneID=”Main2″ DisplayName=”$Resources:core,objectiv_schema_mwsidcamlidC24;”

DefaultView=”TRUE” MobileView=”TRUE” MobileDefaultView=”TRUE” ImageUrl=”/_layouts/images/generic.png” Url=”AllItems.aspx”>

Remove the SetupPath attribute!

11. Set the view pages “DeploymentType” property to “NoDeployment”:

image

12. That’s all Hot smile

Create tool for Visual Studio 2010 to extract the full qualified public assemby name with PowerShell

I created a simple tool extension for Visual Studio for extracting the full qualified name of an .NET assembly.

Like this: http://msdn.microsoft.com/en-us/library/ee539398.aspx but without a compiled application.

I used PowerShell for this.

HERE ARE THE STEPS:

    1. Got to Visual Studio 2010
    2. Open Menu “Tools”
    3. Open Dialog “External Tools”
    4. Click “Add” to add a new tool entry
    5. Specify “Full Assembly Name” as “Title”
    6. Enter “c:windowssystem32WindowsPowerShellv1.0powershell.exe” as “Command”
    7. Enter
      -command "&{[System.Reflection.AssemblyName]::GetAssemblyName('$(TargetPath)').FullName}"

      as “Arguments”

    8. Check “Use output window”
    9. Click “OK”
    10. You can use this tool if your output binary can be compiled(!)

Screenshots:

image

image