Share via


Hands-on Lab: Introduction to Windows Workflow Foundation in .NET Framework 4

 

Hands-On Lab

Introduction To Windows Workflow Introduction to Windows Workflow Foundation in .NET Framework 4

 

**
**

Contents

Table of Contents

Overview

Exercise 1: Hello Workflow

Task 1 – Creating a Simple Hello Workflow Application.

Exercise 1 Verification 

Exercise 2: Refactoring Workflow

Task 0 – Opening the Solution

Task 1 – Renaming Workflow1 to SayHello

Task 2 – Updating the Main Method to Use the New Name

Exercise 2: Verification 

Exercise 3: The CodeActivity

Task 0 – Opening the Solution

Task 1 – Creating the SayHelloInCode Activity

Task 2 – Updating Main to Invoke SayHelloInCode

Exercise 3: Verification

Exercise 4: Dynamic Workflows with XAML

Task 0 – Opening the Solution

Task 1 – Modifying SayHello.xaml Files Properties

Task 2 – Modifying Main() to Load the SayHello.xaml File

Exercise 4: Verification

Exercise 5:  Testing Workflows

Task 0 – Opening the Solution

Task 1 – Creating the Unit Test Project

Task 2 – Creating the Test

Task 3 – Getting the Application to Compile

Task 4 – Seeing the Test Fail

Task 5 – Making the Test Pass

Exercise 5: Verification

Exercise 6: WorkflowApplication

Task 0 – Opening the Solution

Task 1 – Writing the Test to Verify that the Workflow Thread ID is Returned as an Out Argument

Task 2 – Returning the WorkflowThread as an Argument

Task 3 – Modifying the Test to use WorkflowApplication

Exercise 6: Verification

Exercise 7: If/Else Logic

Task 0 – Opening the Solution

Task 1 – Writing the Test for a New Requirement

Task 2 – Implementing the New Requirement in the Workflow

Exercise 7: Verification

Exercise 8: Error Handling

Task 0 – Opening the Solution

Task 1 – Writing a Test to Observe Error Behavior

Task 2 – Adding the Try/Catch Activity to the Workflow

Exercise 8: Verification

Exercise 9: Custom Activities and Designers

Task 0 – Opening the Solution

Task 1 – Add Activity Designer Library

Task 2 – Create Composite Activity in XAML

Task 3 – Add Existing Activities

Task 4 – Observe Custom Activities without Designers

Task 6 – Explore Activity Designers

Task 7 – Linking the Activity Designers to the Activity

Exercise 9: Verification

Task 1 – Prepare the test projects

Task 2 – Create DiagnosticTrace Test

Task 3 – Add a DiagnosticTrace activity to the SayHello Workflow

Task 4 – Add the PrePostSequence Test

Exercise 10: Hosted Designer

Task 0 – Opening the Solution

Task 1 – Adding new WPF Application

Exercise 10: Verification

Summary

 

Overview

Welcome to Windows Workflow Foundation 4 (WF) Microsoft's programming model, engine and tools for quickly building workflow-enabled applications. This release of WF in .NET Framework 4 changes several development paradigms from previous versions. Workflows are now easier to create, execute, and maintain.

In this lab, you will get to know the basics of creating, hosting and running a workflow. This lab is also an introduction to the new workflow authoring constructs in the .NET Framework 4 and Visual Studio 2010, including the new Workflow Designer, expressions, variables and arguments. Additionally, you will explore the use of some basic built-in activities, create custom activities, activity designers and even re-host the workflow designer in a WPF application.

  Do I have to do all the exercises?

This lab is long.  To do all of it will probably take close to 2 hours.  This lab has been designed so that each exercise is independent of the others.  Just open the solution found under the Begin folder for the exercise.  For example, if you wanted to learn how to create a custom activity designer you would start with Exercise 9 and open the solution from the Source\Ex9-ActivityDesigner\Begin folder

 

Objectives

In this Hands-On Lab, you will learn:

·         How to create a sequential workflow using the designer and [[XAML]] or, alternatively, using plain C# or Visual Basic code

·         How to run and test the sequential workflow using the WorkflowApplication and WorkflowInvoker classes

·         How to pass InArguments and receive OutArguments from a workflow

·         How Expressions and Variables are used

·         How to use the WriteLine, If, TryCatch, Catch<T> and Throw activities

·         How to load and run an activity from a xaml file

·         How to create an activity designer

·         How to host the WorkflowDesigner in your own application

 

System Requirements

You must have the following items to complete this lab:

·         Microsoft Visual Studio 2010

·         Microsoft .NET Framework 4

 

Setup

All the requisites for this lab are verified using the Dependency Checker. To make sure that everything is correctly configured, follow these steps:

Note: To perform the setup steps you need to run the scripts in a command window with administrator privileges.

 

1.       Run the Dependency checker for the Training Kit if you haven't done it previously. To do this, run the CheckDependencies.cmd script located under the \Source\Setup folder. Install any pre-requisites that are missing (rescanning if necessary) and complete the wizard.

 

Note: For convenience, much of the code you will be managing along this hands-on lab is available as Visual Studio code snippets.

To use code snippets with XAML, right click on the xaml file, select Open With and choose XML Editor. Then you can insert XML snippets into the file.

 

Exercises

This Hands-On Lab comprises the following exercises:

Note: each exercise builds on the one before it but you can start at any point in the lab by opening the solution in the Begin folder for the exercise.

 

1.       Hello Workflow

2.       Refactoring Workflow

3.       The CodeActivity

4.       Dynamic Workflows with [[XAML]]

5.       Testing Workflows

6.       WorkflowApplication

7.       Adding If/Else Logic

8.       Error Handling

9.       Custom Activities and Designers

10.   Hosted Designer

 

Estimated time to complete this lab: [120] minutes.

Starting Materials

·         Visual Studio solutions. Depending on the exercise you will find Visual Studio solutions for C# and Visual Basic that you can use as starting point for the exercises.

  What if I get stuck?

The source code that accompanies this hands-on lab includes an end folder where you can find a Visual Studio solution with the code that you would obtain if you complete the steps in each exercise. You can use this solution as a guide if you need additional help working through the exercises.

Remember to build the solutions before opening any file using the workflow designer.

 

Next Step

Exercise 1: Hello Workflow

 

Exercise 1: Hello Workflow

Workflows execute a business process. Each step in that process is implemented by an Activity.

In this exercise, you will create and test a simple “Hello World” process using Windows Workflow Foundation 4.

Task 1 – Creating a Simple Hello Workflow Application

In this task you will create a very simple workflow that will be the equivalent to this code:

C#

private static void SayHello()

{

    Console.WriteLine("Hello Workflow 4");

}

Visual Basic

Private Shared Sub SayHello()

    Console.WriteLine("Hello Workflow 4")

End Sub

 

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Select File / New Project Ctrl+Shift+N and set the project options

a.       Installed Templates: select either Visual C# or Visual Basic then select Workflow  under the language

b.      Template: Workflow Console Application

c.       Name: HelloWorkflow.

d.      Location: %TrainingKitInstallFolder%\Labs\IntroToWF\Ex1-HelloWorkflow\Begin

e.      Solution: HelloWorkflow

Figure 1

Creating a new Workflow Console Application (C#)

 

Figure 2

Creating a new Workflow Console Application (Visual Basic)

 

3.       Since your business process is a 1-step process, you can simply add a WriteLine activity to implement the process. From the Toolbox, drag a WriteLine activity and drop it on the design surface.

Figure 3

Adding a WriteLine activity

 

Note: If the Toolbox window is not visible, select Toolbox from the View menu.

 

4.       Set the WriteLine Text property to "Hello Workflow 4".

Figure 4

Setting the Text property

 

  WriteLine Activity

The WriteLine activity is a simple activity that will display a message on the console. The text property is an Expression which could be the result of calling a function or evaluating the property of an object. In this case, the expression is a literal string so you must quote the string.

 

 

Next Step

Exercise 1 Verification

 

 

Exercise 1 Verification

In order to verify that you have correctly performed all steps of exercise 1, proceed as follows:

1.       Press CTRL+F5 to build and run the workflow without debugging. The application should run in a console window and print the message “Hello Workflow 4”.

Figure 5

The completed HelloWorkflow application

 

Next Step

Exercise 2: Refactoring Workflow

 

 

Exercise 2: Refactoring Workflow

Even though you have a working application, you might want to make a few improvements. In this exercise, as Workflow1 is not a very descriptive name, you will change it to SayHello and see how the changes to the workflow name affect the rest of the program.

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 1. Alternatively, you can follow the following steps to begin with Exercise 2.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 2 located under the Source\Ex2-Refactoring\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Renaming Workflow1 to SayHello

1.       To change the name of the workflow defined in Workflow1.xaml, Open it with the workflow designer and click on an empty area the designer surface. This will allow you to change the properties of the workflow.

Figure 6

Click on an empty area of the designer surface so you can set the name of the workflow

 

2.       In the Properties window, set the Name property of the Workflow to HelloWorkflow.SayHello (C#) or SayHello (VB)

Figure 7

Set the Name property of the workflow (C#)

 

Figure 8

Set the Name property of the workflow (VB)

 

3.       While it is not required, it is a good idea for the name of the .xaml file to match the name of the activity contained in it. In Solution Explorer, right-click Workflow1.xaml, select Rename and type SayHello.xaml.

  Watch Out

When you rename C# or Visual Basic files, Visual Studio asks you if you want refactoring to change the name of the class also. It does not do this when renaming XAML files, so if you want to change the name of the workflow also you need to do this manually.

 

4.       Press CTRL+SHIFT+B to build the solution. It will fail to compile because you changed the name of the workflow.

Figure 9

The workflow fails to compile (C#)

 

Figure 10

The workflow fails to compile (VB)

 

  Why does the application fail to compile when I change the workflow name?

Your workflow is actually a class declared in XAML that inherits from System.Activities.Activity. The name of the workflow is the name of the class.  When you set the Name property, you changed the name of the class.

 

Task 2 – Updating the Main Method to Use the New Name

WF4 runs a workflow with the Workflow Runtime. The easiest way to invoke the runtime is to use the WorkflowInvoker class which is what the Workflow Console Application template uses.

1.       To fix the build break, open Program.cs (C#) or Module1.vb (Visual Basic) and modify it to use the new SayHello class name. To do this, locate the WorkflowInvoker.Invoke method call, and modify it to use SayHello instead of Workflow1 as shown in the following code (shown in bold).

C#

static void Main(string[] args)

{

    WorkflowInvoker.Invoke(new SayHello());

}

 

Visual Basic

Shared Sub Main()

**    WorkflowInvoker.Invoke(New** SayHello())

End Sub

 

 Why does Visual Studio warn me that the type SayHello is not defined?

If you change Main() before compiling the workflow with the new SayHello name, you may notice a warning about the type SayHello not being defined. The reason for this is that Visual Studio is not aware of types declared in XAML until you build the solution.

 

 

Next Step

Exercise 2: Verification

 

Exercise 2: Verification

In order to verify that you have correctly performed all steps of exercise 2, proceed as follows:

1.       Press CTRL+SHIFT+B to build the solution. It should now build without errors.

2.       Press CTRL+F5 to run it. You should see the same message as before “Hello Workflow 4”

Figure 11

Hello Workflow 4 message

 

 

Next Step

Exercise 3: The CodeActivity

 

Exercise 3: The CodeActivity

As you have seen so far, WF4 consists of a designer that edits .xaml files and a runtime that **** invokes activities. When you author a workflow, you are creating a new kind of activity and because activities are just classes that inherit from System.Activities.Activity or one of its subclasses, you can declare workflows using C#, VB or XAML. In this exercise, you will implement your “greeting” business process by creating an activity in C# or VB.

As previously said, activities implement a business process. Some activities will implement the process by invoking other activities. For example, your SayHello activity did not actually write text to the console but instead used the WriteLine activity to do the work.  could have implemented the same SayHello activity in C# or VB by inheriting from System.Activities.Activity and creating an Implementation as you see in the following examples

C#

public sealed class SayHelloActivity : Activity

{

    WriteLine writeLine = new WriteLine() { Text = "Hello Workflow 4" };

 

    public SayHelloActivity()

    {

        Implementation = () => { return writeLine; };

    }

}

 

 

Visual Basic

Public NotInheritable Class SayHelloActivity

    Inherits Activity

 

    Private writeLine As New WriteLine With {.Text = "Hello Workflow 4"}

    Public Sub New()

        Implementation = Function()

                             Return writeLine

                         End Function

    End Sub

End Class

 

This approach is useful if you are creating a workflow out of other activities that do the work as did in exercise 1 with the WriteLine activity. However, there may be times when you want to create an activity that implements the business logic in itself or by calling another class that is not an activity to do the work. To do this, you will inherit from a different base class System.Activities.CodeActivity and override the Execute method.

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 2. Alternatively, you can follow the following steps to begin with Exercise 3.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 3 located under the Source\Ex3-CodeActivity\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Creating the SayHelloInCode Activity

In this task, you will create an activity in code and write the text to the console using Console.WriteLine

1.       Right-click the HelloWorkflow project, select Add / New Item.   From the Workflow templates select Code Activity and name the file SayHelloInCode.

2.       Delete the Text property from the template – you will not need it.

3.       CodeActivity is an abstract class. That means that when you inherit from it, you must override the Execute method. This is the place where you will do the work of your activity. Replace the default implementation of the class with the following code.

(Code Snippet - Introduction to WF4 Lab - SayHelloInCode Class CSharp)

C#

public sealed class SayHelloInCode : CodeActivity

{

** ** **  protected override void Execute(CodeActivityContext** context)

** ** **  {**

**   ** **    Console.WriteLine("Hello Workflow 4 in code");**

** ** **  }**

}

 

* (Code Snippet - Introduction to WF*4 Lab - SayHelloInCode Class VB)

Visual Basic

Public NotInheritable Class SayHelloInCode

** ** **  Inherits** CodeActivity

** ** **  **

**   ** Protected Overrides Sub Execute(ByVal context As CodeActivityContext)

**   ** **    Console.WriteLine("Hello Workflow 4 in code")**

** ** **  End** Sub

End Class

 

 

Task 2 – Updating Main to Invoke SayHelloInCode

1.       Change Program.cs (C#) or Module1.vb (Visual Basic) to use the new SayHelloInCode class. To do this, locate the WorkflowInvoker.Invoke method code, and replace it with the following code:

(Code Snippet - Introduction to WF4 Lab - InvokeSayHelloInCode Class CSharp)

C#

static void Main(string[] args)

{

    WorkflowInvoker.Invoke(new SayHelloInCode());

}

 

(Code Snippet - Introduction to WF4 Lab - InvokeSayHelloInCode Class VB)

Visual Basic

Shared Sub Main(ByVal args() As String)

**    WorkflowInvoker.Invoke(New SayHelloInCode())**

End Sub

 

 

Next Step

Exercise 3: Verification

 

Exercise 3: Verification

1.       Press CTRL+F5 to run the workflow without debugging. The application should run in a console window and print the message “Hello Workflow 4 in code”.

Figure 12

The completed HelloWorkflow application

 

 Why would I create a code activity?

Writing business logic in code is nothing new. Why would you go to the effort to write it in a special class that inherits from CodeActivity? The reason is that by doing so now your business logic can be composed into other larger business processes that use the workflow runtime. As you will see later in the lab it will also benefit from a model for threading and data management that provides for highly scalable and long running business applications.

 

 

Next Step

Exercise 4: Dynamic Workflows with XAML

 

Exercise 4: Dynamic Workflows with XAML

Up to this point, you have been authoring workflows in .xaml, .cs or .vb files. These files are compiled into types that are included in the assembly of the project and run by the workflow runtime.

While it might look like the format of the source files does not matter, .xaml files offer some distinct advantages over authoring workflows in C# or VB.

·         The Workflow Designer works with .xaml files only, workflows authored in C# or VB have no designer support.

·         XAML can be loaded and run dynamically without compiling it into an assembly

Dynamic workflows provide some interesting possibilities for programs that want to generate business logic or make a runtime decision on which business logic to load and run.

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 3. Alternatively, you can follow the following steps to begin with Exercise 4.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 4 located under the Source\Ex4-XAML\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Modifying SayHello.xaml Files Properties

 In this task you will modify the HellowWorkflow program to load and run the SayHello.xaml file. Then you will modify the text of SayHello.xaml and observe the change in the message the next time you run the application.

1.       You need to tell Visual Studio to treat SayHello.xaml as deployed content rather than code. To do this

a.       In Solution Explorer select SayHello.xaml

b.      Build Action: Content

c.       Copy To Output Directory: Copy Always

d.      Set the Custom Tool to blank

Figure 13

Change the properties of SayHello.xaml to treat it as content

 

Task 2 – Modifying Main() to Load the SayHello.xaml File

Previously your workflow was compiled into a type. To invoke a workflow from a .xaml file you will need to use ActivityXamlServices to load the .xaml file into memory and create an instance of an activity that WorkflowInvoker can invoke. Keep in mind that any assemblies that your .xaml file references must be available when the workflow is invoked.

1.       Add the following namespace directives to the program.cs or Module1.vb file.

C#

using System.Activities.XamlIntegration;

 

Visual Basic

Imports System.Activities.XamlIntegration

 

2.       Modify program.cs or Module1.vb to use ActivityXamlServices and also add a call to Console.ReadKey this will make it easier to see what is happening with your app when running it from Windows Explorer.

(Code Snippet - Introduction to WF4 Lab - ActivityXamlServices CSharp)

C#

static void Main(string[] args)

{

** ** **  WorkflowInvoker.Invoke(ActivityXamlServices.Load("SayHello.xaml"));**

**    Console.ReadKey(false);**

}

* *

(Code Snippet - Introduction to WF4 Lab - ActivityXamlServices VB)

Visual Basic

Sub Main()

**    WorkflowInvoker.Invoke(ActivityXamlServices.Load("SayHello.xaml"))**

**    Console.ReadKey(False)**

End Sub

 

 

Next Step

Exercise 4: Verification

 

Exercise 4: Verification

In this verification, you will run the HelloWorld application, then modify the deployed SayHello.xaml file from the bin\Debug folder and see if the new text appears in the console window.

1.       Press CTRL+F5 to run the workflow without debugging. The application should run in a console window and print the message “Hello Workflow 4”.

2.       Navigate to the Bin\Debug directory under your project folder and locate SayHello.xaml.

Figure 14

Locate the SayHello.xaml file in the project’s Bin\Debug directory

 

3.       Right click on SayHello.xaml and select Edit to open it in Notepad

Figure 15

Right click and select Edit

 

4.       In Notepad change the Text property of the WriteLine activity to “Hello Workflow 4 XAML” then save and close it.

Figure 16

Change the Text property in Notepad

 

5.       In Windows Explorer, run HelloWorkflow.exe and it should now say “Hello Workflow 4 from XAML”. Press any key to exit

Figure 17

HelloWorkflow.exe showing a new message from the .xaml file

 

6.       Go back to Visual Studio and restore SayHello.xaml to the default settings.  You will need this for the rest of the labs.

a.       Build Action: XamlAppDef

b.      Copy to Output Directory: Do not copy

c.       Custom Tool: MSBuild:Compile

Figure 18

Restore SayHello.xaml to the default properties for .xaml files

 

7.       In Solution Explorer, select SayHello.xaml and set the build action to XamlAppDef.  Once this is done you can delete Activity1.xaml from the project.

Next Step

Exercise 5:  Testing Workflows

 

Exercise 5: Testing Workflows

Up to this point, the application is not very interesting. It is only working in a console and does not receive any input arguments. Most applications that do meaningful work will have to deal with input and output arguments. Additionally, the application is not easily tested in its current form.

In this task, you will modify the SayHello activity to use arguments and prepare it to be useful in other non-console application environments by returning a greeting rather than writing directly to the Console with the WriteLine activity. You will be doing this using the "write the test first" approach. First, you will create a test that fails and then add the necessary code to make it pass.

The resulting application will be the equivalent of the following code.

C#

private static string SayHello(string name)

{

    return "Hello " + name + " from Workflow 4";

}

 

Visual Basic

Private Shared Function SayHello(ByVal name As String) As String

    Return "Hello " & name & " from Workflow 4"

End Function

 

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 4. Alternatively, you can follow the following steps to begin with Exercise 5.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 5 located under the Source\Ex5-Testing\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Creating the Unit Test Project

1.       To begin with, create a unit test for the workflow to verify the behavior. In Solution Explorer, right-click the HelloWorkflow solution and select Add / New Project and set the project options.

a.       Installed Templates: select either Visual C# or Visual Basic then select Test  under the language

b.      Template: Test Project

c.       Name: HelloWorkflow.Tests

Figure 19

Adding a new Test Project to the solution (C#)

 

Figure 20

Adding a new Test Project to the solution (Visual Basic)

 

2.       Right-click the HelloWorkflow.Tests project and click Add Reference. Using the Projects tab, add a project reference to the HelloWorkflow project. Repeat these steps, using the .NET tab instead to add a reference to the System.Activities library.

3.       Right-click UnitTest1.cs (C#) or UnitTest1.vb (Visual Basic), click Rename and change its name to SayHelloFixture.cs (C#) or SayHelloFixture.vb (Visual Basic). When prompted to rename the UnitTest1 class select Yes.

 

Task 2 – Creating the Test

In this task you will create the test before you actually implement the behavior in the activity.

1.       Add the following namespace directive to the SayHelloFixture.cs (C#) file or SayHelloFixture.vb (Visual Basic):

C#

using System.Activities;

using HelloWorkflow;

 

Visual Basic

Imports System.Activities

Imports HelloWorkflow

 

2.       Create a test to make sure that the workflow properly greets you. To do this, open SayHelloFixture.cs (C#) or SayHelloFixture.vb (Visual Basic), locate the TestMethod1 method and rename it to ShouldReturnGreetingWithName.

3.       The SayHello activity does not accept any arguments yet, but you will write the code to invoke it as though it does. This will allow you to think about what the interface to your activity feels like when consuming it.  Do not worry if Visual Studio warns you about the UserName property not being defined yet. Replace the implementation as shown

(Code Snippet - Introduction to WF4 Lab - ShouldReturnGreetingImpl CSharp)

C#

[TestMethod]

public void ShouldReturnGreetingWithName()

{

**    IDictionary<string, object> output;**

** **

**  ** ** output = WorkflowInvoker.Invoke(**

**   ** **    new** SayHello()

**   ** **    {**

**     ** **      UserName = "Test"**

**   ** **    });**

** ** **  Assert.AreEqual("Hello Test from Workflow 4", output["Greeting"]);**

}

 

(Code Snippet - Introduction to WF4 Lab - ShouldReturnGreetingImpl VB)

Visual Basic

<TestMethod()> Public Sub ShouldReturnGreetingWithName()

** ** **  Dim output = WorkflowInvoker.Invoke(**

**   ** **    New SayHello() With {.UserName = "Test"})**

** ** **  Assert.AreEqual("Hello Test from Workflow 4", output("Greeting"))**

End Sub

 

  How do I pass arguments to an activity?

You can create the activity and initialize the arguments (which are public properties) using object initialization or you can pass a Dictionary<string, object> (C#) or Dictionary(Of String, Object) (Visual Basic) of input parameters that map to the names of the input arguments of the activity.

   How do I get data from output?

The output variable is an IDictionary<string, object> (C#) or IDictionary(Of String, Object) (Visual Basic) that contains a map of output variables using the name of the variable as the key.

 

 

Task 3 – Getting the Application to Compile

The interface to your Activity (in terms of input and output arguments) looks good but your application doesn’t compile. Your first goal is to get the app into a state where it will compile.

1.       Press CTRL+SHIFT+B to build the application it should fail with a compile error

Figure 21

UserName has not been defined yet (C#)

 

Figure 22

UserName has not been defined yet (VB)

 

2.       Open SayHello.xaml in the designer

3.       Open the arguments pane by clicking on Arguments

Figure 23

Click on Arguments to open the arguments pane

 

4.       Add the UserName and Greeting arguments as shown

Figure 24

Declare the arguments to the activity

 

  Arguments

In Windows Workflow Foundation (WF), arguments represent the flow of data into and out of an activity**.** An activity has a set of arguments and they make up the signature of the activity. Each argument has a specified direction: input, output, or input/output.

 

5.       Press CTRL+SHIFT+B to build the solution, it should now build without errors.

 

Task 4 – Seeing the Test Fail

It is important to see your test fail because it is possible that you have a bug in your test that would cause it to always succeed. In this task, you will insure that your test does indeed fail.

1.       Press CTRL+R,T to run the unit tests in the current context. The test will run but it will fail, because the activity is not returning anything in the Greeting out argument.

Figure 25

ShouldReturnGreetingwithName test

 

Task 5 – Making the Test Pass

Now that you know your test is working, you need to do the simplest possible thing you can to make the test pass.

1.       Writing text to the console with WriteLine isn’t going to make the test pass so delete the WriteLine activity by right-clicking on it and choosing Delete ().

2.       You need to set the value of the Greeting out argument. To do this, drag an Assign activity from the Primitives group in the Toolbox and drop it onto the designer surface.

Figure 26

Drag an Assign activity to the design surface

 

3.       Set the To property to Greeting

Figure 27

Type Greeting in the left box

 

4.       You could type the greeting expression into the design surface but since the expression is longer, use the properties window.  Click the button to the right of the Value property to open the expression editor

Figure 28

Click the button to the right of the Value property

 

5.       In the expression editor, you can enter longer expressions, including expressions that use the VB line continuation character “_”.  Set the expression to "Hello " & UserName  _
& " from Workflow 4"

Figure 29

The Assign activity that sets the Greeting argument

 

If you are a C# developer you may not be familiar with the line oriented nature of Visual Basic syntax.  If you want an expression to break across lines in Visual Basic you must use an underscore to continue the expression on the next line.

 

6.       Press CTRL+SHIFT+B to build the solution, it should compile without errors.

 

Next Step

Exercise 5: Verification

 

Exercise 5: Verification

Now you are ready to run the test and see if your activity passes the test

1.       Open the Test View window. To do this, on the Test menu point to Windows and click Test View ().

2.       In the Test View, select the ShouldReturnGreetingWithName test and click the Run Selection button ().

Figure 30

Selecting and running the ShouldReturnGreetingWithName test

 

3.       Verify that the test run passes.

Figure 31

The test passed

 

 

Next Step

Exercise 6: WorkflowApplication

 

Exercise 6: WorkflowApplication

To this point in the labs you have focused on creating an activity and invoking it in the simplest way possible with the WorkflowInvoker class. The WorkflowInvoker.Invoke method is simple because it is synchronous and invokes the workflow on the same thread as the caller.

Another way to invoke a workflow is with the WorkflowApplication class. This class allows you to run a workflow on a separate thread and to supply delegates to be invoked when the workflow completes, goes idle, terminates or has an unhandled exception. This allows you to create multi-threaded server or client programs more easily than you can without using WF4.

In this exercise, you will modify the host application to run your SayHello activity with WorkflowApplication and observe the threading behavior. You now have two requirements for your workflow.

1.       Return a personalized greeting

2.       Return a non-zero Int32 value containing the managed thread ID that the workflow was invoked on

Using the “write the test first” approach, you will begin by writing a test that verifies your new requirement for the workflow thread ID.

 

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 5. Alternatively, you can follow the following steps to begin with Exercise 6.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 6 located under the Source\Ex6-WorkflowApplication\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Writing the Test to Verify that the Workflow Thread ID is Returned as an Out Argument

In this task, you will create a test that verifies that the workflow returns a non-zero integer in the output arguments named WorkflowThread.

1.       Open SayHelloFixture.cs and add the following namespace directives

C#

using System.Threading;

using System.Diagnostics;

 

Visual Basic

Imports System.Threading

Imports System.Diagnostics

 

2.       Add the ShouldReturnWorkflowThread test as shown

(Code Snippet - Introduction to WF4 Lab - ShouldReturnWorkflowThread Test CSharp)

C#

/// <summary>

/// Verifies that the workflow returns an Out Argument

/// Name: WorkflowThread

/// Type: Int32

/// Value: Non-Zero

/// </summary>

[TestMethod]

public void ShouldReturnWorkflowThread()

{

**    var output = WorkflowInvoker.Invoke(**

**     new SayHello()**

**     {**

**         UserName = "Test"**

**     });**

** **

**    Assert.IsTrue(output.ContainsKey("WorkflowThread"),**

**        "SayHello must contain an OutArgument named WorkflowThread");**

** **

**    // Don't know for sure what it is yet**

**    var outarg = output["WorkflowThread"];**

** **

**    Assert.IsInstanceOfType(outarg, typeof(Int32),**

**        "WorkflowThread must be of type Int32");**

** **

**    Assert.AreNotEqual(0, outarg,**

**        "WorkflowThread must not be zero");**

** **

**    Debug.WriteLine("Test thread is " +**

**                    Thread.CurrentThread.ManagedThreadId);**

**    Debug.WriteLine("Workflow thread is " + outarg.ToString());**

}

 

(Code Snippet - Introduction to WF4 Lab - ShouldReturnWorkflowThread Test VB)

Visual Basic

''' <summary>

''' Verifies that the SayHello workflow contains an Out Argument

''' Name: WorkflowThread

''' Type: Int32

''' Value: Non-Zero

''' </summary>

''' <remarks></remarks>

<TestMethod()> Public Sub ShouldReturnWorkflowThread()

**    Dim output = WorkflowInvoker.Invoke( _**

**    New SayHello() With {.UserName = "Test"})**

** **

**    Assert.IsTrue(output.ContainsKey("WorkflowThread"),**

**  ** **   "SayHello must contain an OutArgument named WorkflowThread")**

** **

** ** **  ' Don't know for sure what it is yet**

** ** **  Dim** outarg = output("WorkflowThread")

** **

** ** **  Assert.IsInstanceOfType(outarg, GetType(Int32),**

**   ** **    "WorkflowThread must be of type Int32")**

** **

** ** **  Assert.AreNotEqual(0, output("WorkflowThread"),**

**       "WorkflowThread must not be zero")**

** **

** ** **  Debug.WriteLine("Test thread is " & _**

**   ** **    Thread.CurrentThread.ManagedThreadId)**

** ** **  Debug.WriteLine("Workflow thread is " & outarg.ToString())**

End Sub

 

3.       Press CTRL+R, A to run all tests. You should see the new test fail because you have not added the WorkflowThread out argument yet.

Figure 32

One test passes, one test fails because you have no WorkflowThread argument

 

 

Task 2 – Returning the WorkflowThread as an Argument

Now that you have a test to verify the behavior, you need modify your workflow to make the test pass.

1.       Open SayHello.xaml and add an Out argument named WorkflowThread of type Int32

Figure 33

Add the WorkflowThread Out argument

 

Until now, your workflow has been just one activity. Now you need two activities, one to assign the greeting and another to assign the workflow thread. You need to modify the workflow to use an activity that will contain the two assign activities and there are several activities you could choose from to do this, but let’s begin with the simplest one, the Sequence activity.

2.       You cannot drop a sequence on the designer until you remove the Assign activity that is already there.  You will use this assign activity inside the sequence so you need to cut it, drop the sequence and then and paste it back. Right click on the Assign activity and select Cut.

Figure 34

Cutting the Assign an Activity

 

3.       Drag a Sequence and drop it on the design surface

Figure 35

Drop a Sequence on the designer

 

4.       Right click inside the sequence and select Paste to put the Assign activity inside the sequence.

Figure 36

Paste the assign activity inside the sequence

 

5.       Import the System.Threading namespace into your workflow. Click on Imports and add System.Threading. To do this type the first few letters of the namespace and when the correct namespace is highlighted press Enter

Figure 37

Click on Imports and add System.Threading

 

  Do I have to add System.Threading to Imports?

No, this is optional just as it would be in any C# or VB project. If you don’t import the namespace you will have to fully qualify classes from the name space as in System.Threading.Thread

 

6.       Now you need to assign the current managed thread ID to the WorkflowThread out argument. Drop an Assign activity on the Sequence below the first Assign activity and set the properties as shown

Figure 38

Set the properties of the second Assign activity as shown

 

7.       Press CTRL+SHIFT+B to build the solution

8.       Press CTRL+R,A to run all tests. The tests will now pass. If you want to see the thread returned by the workflow, double click on the ShouldReturnWorkflowThread test. The Debug output will appear in the test results. The actual thread ID will vary on your computer but you will notice that the thread ID for the test is the same as the thread ID for the Workflow because WorkflowInvoker invokes the workflow synchronously on the calling thread.

Figure 39

Debug trace output appears in test results

 

Task 3 – Modifying the Test to use WorkflowApplication

Your test is good but it has one weakness. It verifies that the WorkflowThread returned is non-zero but it does not verify that it returns the actual managed thread ID that the workflow ran on.  For example, your test would pass if the workflow always returned 1 in the WorkflowThread out argument.

If you want to verify the actual thread ID you will need to use WorkflowApplication to run the workflow.  In this task you will modify the test to capture the thread of the workflow by obtaining it during a call to the WorkflowApplication.Completed action and then comparing that value to the value returned from the workflow.

1.       Open SayHelloFixture.cs (C#) or SayHelloFixture.vb (VB) and locate the ShouldReturnWorkflowThread test.  Modify it as shown to use the WorkflowApplication class to run the workflow. This code will use the WorkflowApplication.Completed action to capture the output arguments and thread ID.

(Code Snippet - Introduction to WF4 Lab - ShouldReturnWorkflowThread WFApp Test CSharp)

C#

/// <summary>

/// Verifies that the workflow returns an Out Argument

/// Name: WorkflowThread

/// Type: Int32

/// Value: Non-Zero, matches thread used for Completed action

/// </summary>

[TestMethod]

public void ShouldReturnWorkflowThread()

{

** ** **  AutoResetEvent sync = new AutoResetEvent(false);**

** ** **  Int32 actionThreadID = 0;**

** ** **  IDictionary<string, object> output = null;**

** **

** ** **  WorkflowApplication** workflowApp =

**   ** **    new** WorkflowApplication(

**                 new** SayHello()

**                 {**

**         ** **           UserName = "Test"**

**                 });**

** **

** ** **  // Create an Action<T> using a lambda expression**

** ** **  // To be invoked when the workflow completes**

** ** **  workflowApp.Completed = (e) =>**

**   ** **    {**

**     ** **      output = e.Outputs;**

**     ** **      actionThreadID = Thread.CurrentThread.ManagedThreadId;**

** **

**     ** **      // Signal the test thread the workflow is done**

**     ** **      sync.Set();**

**   ** **    };**

** **

** ** **  workflowApp.Run();**

** **

** ** **  // Wait for the sync event for 1 second**

** ** **  sync.WaitOne(TimeSpan.FromSeconds(1));**

** **

** ** **  Assert.IsNotNull(output,**

**   ** **    "output not set, workflow may have timed out");**

** **

** ** **  Assert.IsTrue(output.ContainsKey("WorkflowThread"),**

**   ** **    "SayHello must contain an OutArgument named WorkflowThread");**

** **

** ** **  // Don't know for sure what it is yet**

** ** **  var** outarg = output["WorkflowThread"];

** **

** ** **  Assert.IsInstanceOfType(outarg, typeof(Int32),**

**   ** **    "WorkflowThread must be of type Int32");**

** **

** ** **  Assert.AreEqual(actionThreadID, (int)outarg,**

**   ** **    "WorkflowThread should equal actionThreadID");**

** **

** ** **  Debug.WriteLine("Test thread is " +**

**         ** **          Thread.CurrentThread.ManagedThreadId);**

** ** **  Debug.WriteLine("Workflow thread is " + outarg.ToString());**

}

 

(Code Snippet - Introduction to WF4 LabShouldReturnWorkflowThread WFApp Test VB)

Visual Basic

''' <summary>

''' Verifies that the SayHello workflow contains an Out Argument

''' Name: WorkflowThread

''' Type: Int32

''' Value: Non-Zero, matches thread used for Completed action

''' </summary>

''' <remarks></remarks>

<TestMethod()> Public Sub ShouldReturnWorkflowThread()

** ** **  Dim sync As New AutoResetEvent(False)**

** ** **  Dim actionThreadID As Int32 = 0**

** ** **  Dim** output As IDictionary(Of String, Object) = Nothing

** ** **  Dim** workflowApp As New WorkflowApplication( _

**   ** **    New** SayHello() With {.UserName = "Test"})

** **

** ** **  workflowApp.Completed = _**

**   ** **    Function(e)**

**     ** **      output = e.Outputs**

**     ** **      actionThreadID = Thread.CurrentThread.ManagedThreadId**

** **

**     ** **      ' Signal the test thread the workflow is done**

**     ** **      sync.Set()**

** **

**     ** **      ' VB requires a lambda expression to return a value**

**     ** **      ' It is not used with Action(Of T)**

**     ** **      Return** Nothing

**   ** **    End** Function

** **

** ** **  workflowApp.Run()**

** **

** ** **  ' Wait for the sync event for 1 second**

** ** **  sync.WaitOne(TimeSpan.FromSeconds(1))**

** **

** ** **  Assert.IsNotNull(output, "output not set, workflow may have timed out")**

** **

** ** **  Assert.IsTrue(output.ContainsKey("WorkflowThread"),**

**        ** **         "SayHello must contain an OutArgument named WorkflowThread")**

** **

** ** **  ' Don't know for sure what it is yet**

** ** **  Dim** outarg = output("WorkflowThread")

** **

** ** **  Assert.IsInstanceOfType(outarg, GetType(Int32),**

**           ** **            "WorkflowThread must be of type Int32")**

** **

**    Assert.AreEqual(actionThreadID,**

**                DirectCast(output("WorkflowThread"),** Integer),

**   ** **            "WorkflowThread should equal actionThreadID")**

** **

** **

** ** **  Debug.WriteLine("Test thread is " & _**

**       ** **        Thread.CurrentThread.ManagedThreadId)**

** ** **  Debug.WriteLine("Workflow thread is " & outarg.ToString())**

End Sub

 

  Delegates, not events

WorkflowApplication.Completed and other properties of WorkflowApplication are not events but delegates. Handling them requires you to provide a method, anonymous method or lambda expression.

 

 

Next Step

Exercise 6: Verification

 

Exercise 6: Verification

Now you are ready to run the test and see if your activity passes the test with the new requirements

1.       Press CTRL+SHIFT+B to build the solution

2.       Press CTRL+R, A to run all tests

3.       Verify that all tests pass

Figure 40

All tests pass

 

4.       Double click on the ShouldReturnWorkflowThread test result to view the debug messages about the threads

Figure 41

The test results show the workflow ran on a different thread

 

 

Next Step

Exercise 7: If/Else Logic

 

Exercise 7: If/Else Logic

In the previous exercise, you created an enhanced Hello Workflow application with a custom hello message. In this exercise, you will add If/Else logic to the workflow to display a different Hello message depending on a custom condition.

This exercise will use the "write the test first" approach, that is, first writing a test for the new requirement and then implementing the necessary code to make it pass.

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 6. Alternatively, you can follow the following steps to begin with Exercise 7.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 7 located under the \Source\Ex7-IfElse\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Writing the Test for a New Requirement

You have a new requirement for your application. If the name has an odd number of letters then you want the first word of the greeting to be "Greetings", otherwise the first word should be "Hello". As an example, now your workflow will be the equivalent of this code:

C#

private static string SayHello(string userName)

{

    string FirstWord = null;

 

    if (userName.Length % 2 == 0)

        FirstWord = "Hello";

    else

        FirstWord = "Greetings";

 

    return FirstWord + ", " + userName +" from Workflow 4";

}

 

Visual Basic

Private Shared Function SayHello(ByVal userName As String) As String

    Dim FirstWord As String = Nothing

 

    If userName.Length Mod 2 = 0 Then

        FirstWord = "Hello"

    Else

        FirstWord = "Greetings"

    End If

 

    Return FirstWord & ", " & userName & " from Workflow 4"

End Function

 

1.       Create a test to verify the new requirement. To do this, open the SayHelloFixture.cs file (C#) or SayHelloFixture.vb (Visual Basic), located under the HelloWorkflow.Test project, and add the following test.

(Code Snippet - Introduction to WF4 Lab - ShouldReturnGreetingWithOddLengthName Test CSharp)

C#

[TestMethod]

public void ShouldReturnGreetingWithOddLengthName()

{

** ** **  var output = WorkflowInvoker.Invoke(**

**   ** **    new SayHello() { UserName = "Odd" });**

** **

** ** **  string** greeting = output["Greeting"].ToString();

** **

** ** **  Assert.AreEqual("Greetings Odd from Workflow 4", greeting);**

}

 

(Code Snippet - Introduction to WF4 Lab - ShouldReturnGreetingWithOddLengthName Test VB)

Visual Basic

<TestMethod()>

Public Sub ShouldReturnGreetingWithOddLengthName()

** ** **  Dim output = WorkflowInvoker.Invoke(**

**   ** **    New SayHello() With {.UserName = "Odd"})**

** ** **  Assert.AreEqual("Greetings Odd from Workflow 4",**

**         ** **          output("Greeting").ToString())**

End Sub

 

2.       Right-click over test method and select Run Tests (). The test will fail because you have not modified the workflow yet to return a different hello message in each case.

Figure 42

ShouldReturnGreetingWithOddLengthName test failing

 

Task 2 – Implementing the New Requirement in the Workflow

1.       Open SayHello.xaml in the workflow designer. To do this, double-click the file in Solution Explorer.

2.       Add a FirstWord variable to store the first word of the hello message, whether it is "Hello" or "Greetings". To do this, follow these steps:

a.       Select the Sequence by clicking over the shape surface.

b.      Click the Variables button. A panel that displays the available variables for the Sequence activity appears.

c.       Click Create Variable.

d.      Type FirstWord in the Name box.

Figure 43

Adding the FirstWord variable of type String to Sequence

 

  Variables

In Windows Workflow Foundation (WF), variables represent the storage of data. Arguments, on the other side, represent the flow of data into and out of an activity. Variables have a scope just as they do in C# or Visual Basic. If you open the variables pane without selecting an activity you will not be able to add a variable. The activity selected in the designer will provide the scope for the variable. In this case, FirstWord belongs to the Sequence scope.

 

3.       Now you need to test the UserName argument to see if it has an even or odd number of characters. To do this, drag an If activity from the Control Flow group in the toolbox and drop it into the Sequence activity above the Assign activity.

4.       Set the If activity DisplayName to If UserName is Even by selecting the text on the shape and editing it inline or by setting it in the property grid (select the activity and press F4 to display it).

Note: The Workflow designer allows you to give the shape a more readable name by setting its DisplayName property.

 

Figure 44

The If activity with the description set

 

  Watch Out

The red icons () are warning you that this workflow is not valid currently. The If activity still requires that you supply an expression for the Condition argument.

 

5.       Supply an expression for the If condition. To do this, double-click the If activity to open it and type the following expression in the Condition box. This will check if the name length is odd or even.

Visual Basic

UserName.Length Mod 2 = 0

 

   Expressions

Expressions are program statements that can be a literal string, a conditional statement, or an expression that concatenates several strings or calls a method or invokes another activity. Expressions are written using Visual Basic syntax even if the application is in C#. This means capitalization does not matter, comparison is performed using a single equals sign instead of “==”, and the Boolean operators are the words "And" and "Or" instead of the symbols "&&" and "||".

 

6.       Update the FirstWord variable value for even length names, which should be greeted with a "Hello" message. Drag an Assign activity from Primitives group in the toolbox and drop it into the Then area. After that, type FirstWord in the To box (left side) and "Hello " (including the space at the end) in the Value box (right side).

7.       Now update the FirstWord variable value for odd length names, which should be greeted with a "Greetings" message. Drag and Assign activity from Primitives group in the toolbox and drop it into the Else area. Then type FirstWord in the To box (left side) and "Greetings " (including the space at the end) in the Value box (right side).

Figure 45

The completed If activity

 

8.       Modify the final Assign activity to customize the hello message displayed based on the FirstWord value. To do this, in the Assign activity located below the If activity replace the content of the Value box (right side) with the following expression:

Visual Basic

FirstWord & UserName _

& " from Workflow 4"

 

Figure 46

The Completed Workflow

 

9.       Press CTRL+SHIFT+B to build the solution.

Next Step

Exercise 7: Verification

 

Exercise 7: Verification

In order to verify that you have correctly performed all the steps of the exercise you will run the all the tests of the solution and ensure they pass.

1.       From the menu select Test / Run / All Tests In Solution or press CTRL+R,A

2.       Verify that all the tests pass.

Figure 47

Tests pass

 

 

Next Step

Exercise 8: Error Handling

 

Exercise 8: Error Handling

You may have noticed a potential bug in your simple application. What happens if you do not pass a UserName to the workflow? In this exercise, you will add some error handling capabilities to your workflow by using the Try/Catch, Catch<T> and Throw built-in activities.

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 7. Alternatively, you can follow the following steps to begin with Exercise 8.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 8 located under the Source\Ex8-ErrorHandling\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Writing a Test to Observe Error Behavior

Notice that if you pass an empty string ("") the application should function correctly. The only way to get a null name passed to the workflow is to create it without supplying the in argument for UserName.

1.       Create a test to observe what happens when you do not pass the name argument to the workflow. To do this, using Solution Explorer, open SayHelloFixture.cs (C#) or SayHelloFixture.vb (Visual Basic) located under the HelloWorkflow.Tests project and add the following test.

(Code Snippet - Introduction to WF4 Lab - ShouldHandleNullUserName Test CSharp)

C#

[TestMethod]

public void ShouldHandleNullUserName()

{

**    // Invoking with no arguments**

**    WorkflowInvoker.Invoke(new SayHello());**

}

 

(Code Snippet - Introduction to WF4 Lab - ShouldHandleNullUserName Test VB)

Visual Basic

<TestMethod()>

Public Sub ShouldHandleNullUserName()

** **

**    ' Invoking with no arguments**

**    WorkflowInvoker.Invoke(New SayHello())**

** **

End Sub

 

2.       Now run the test. Press Ctrl+R, T or Right-click over test method name and select Run Tests ().

Figure 48

Run the test

 

3.       The result is that the test fails with a NullReferenceException because you have an expression that uses UserName.Length in the If activity and UserName was null.

Figure 49

ShouldHandleNullUserName test failing

 

Test Result

HelloWorkflow.Tests.SayHelloFixture.ShouldHandleNullUserName threw exception: System.NullReferenceException: Object reference not set to an instance of an object.

 

 

Task 2 – Adding the Try/Catch Activity to the Workflow

To handle the error, you could validate the name argument prior to using it or you could simply catch the exception and deal with it. In this task, you will be catching the exception.

1.       Open SayHello.xaml in the designer and drag a TryCatch activity from Toolbox and drop it at the top of the sequence.

Figure 50

The TryCatch activity in the Toolbox

 

Figure 51

Adding a TryCatch activity

 

  Try/Catch Activity

Workflows can use the TryCatch activity to handle exceptions that are raised during the execution of a workflow. These exceptions can be handled or they can be re-thrown using the Throw activity. Activities in the Finally section are executed when either the Try section or the Catches section completes.

 

2.       Now you need to move the If activity inside the Try block.

a.       Collapse the If activity () to make the diagram smaller

b.      Drag-and-drop the if activity into the Try block.

Figure 52

Moving the If activity

 

3.       Now you need to catch a NullReferenceException. To do this, follow these steps:

a.       Click on Add new catch.

b.      Select System.NullReferenceExeption from the combo box and press Enter

Figure 53

Selecting NullReferenceExeption

 

4.       In the Catch section of the activity you need to decide how to handle the error. In this case, you are going to replace the exception with an ArgumentNullException and throw it. This lets the caller know that the exception was their fault for not supplying the UserName argument. Drag a Throw activity from the Toolbox and drop it into the Catches area.

5.       Set the Throw activity expression. To do this, select the Throw activity and in the Properties windows type the following expression in the Exception box.

Visual Basic

New ArgumentNullException("UserName")

 

 Figure 54

Adding a Throw activity

 

6.       Now you need to fix the ShouldHandleNullUserName test so that it expects this exception. To do this, open SayHelloFixture.cs and add an ExpectedException annotation to the test method as shown in the following code.

C#

[TestMethod]

[ExpectedException(typeof(ArgumentNullException))]

public void ShouldHandleNullUserName()

{

  // Invoking with no arguments

  WorkflowInvoker.Invoke(new SayHello());

}

 

Visual Basic

<TestMethod(), ExpectedException(GetType(ArgumentNullException))>

Public Sub ShouldHandleNullUserName()

 

        ' Invoking with no arguments

        WorkflowInvoker.Invoke(New SayHello())

 

End Sub

 

Next Step

Exercise 8: Verification

 

Exercise 8: Verification

1.       Press CTRL+SHIFT+B to build the solution.

2.       From the menu select Test / Run / All Tests In Solution or press CTRL+R,A

3.       Verify that all the tests pass.

Figure 55

Tests pass

 

Next Step

Exercise 9: Activity Designers

 

Exercise 9: Custom Activities and Designers

As you have already seen, Windows Workflow allows you to build custom activities in code.  Depending on your need, there are several base classes you can use.

Base Class

Used For

Activity

Activities that are composed of other activities

CodeActivity

Activities that want to control execution

AsyncCodeActivity

Activities that want to do async work during execution

NativeActivity

Activities that contain other activities or need advanced services from the workflow runtime

 

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 8. Alternatively, you can follow the following steps to begin with Exercise 9.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 9 located under \Source\Ex9-ActivityDesigner\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Add Activity Designer Library

1.       Right click on the HelloWorkflow solution file and select Add / New Project…

2.       Select the Workflow templates and choose Activity Designer Library. Name the project HelloWorkflow.Activities.

Figure 56

Add a new Activity Designer Library project named HelloWorkflow.Activities (C#)

 

Figure 57

Add a new Activity Designer Library project named HelloWorkflow.Activities (VB)

  Why add an Activity Designer Library instead of an Activity Library?

If you intend to create designers using the Activity Designer Library project template is a better choice because it includes the necessary references to the WPF libraries in addition to the System.Activities and related libraries.

 

3.       Delete ActivityDesigner1.xaml, you will not need it for this lab.

 

Task 2 – Create Composite Activity in XAML

Activities can be created by composing other activities.  In this task, you will create a XAML activity that consists of other activities.

1.       Right click on the HelloWorkflow.Activities project and select Add/New Item… and add a new activity using the following values

Item

Value

Installed Templates

Workflow

Template

Activity

Name

WriteTwoLines.xaml

Figure 58

Add a the WriteTwoLines activity (C#)

Figure 59

Add a the WriteTwoLines activity (VB)

2.       WriteTwoLines.xaml will be opened in the designer.  To complete it do the following

a.       From the Control Flow toolbox category drop a Sequence activity

b.      Add two In arguments of type string named Line1 and Line2

c.       From the primitives group, drop two WriteLine activities

d.      Set the first activity Text to Line1 and the second to Line2

Figure 60

The completed WriteTwoLines activity

 

Task 3 – Add Existing Activities

In this task you will add some pre-built code activities

1.       Add the existing PrePostSequence and DiagnosticTrace files. To do this, in Solution Explorer right-click the HelloWorkflow.Activities project, and select Add / Existing Item... browse to Source\Assets folder of this lab (choosing the folder that matches the language of your preference) and select both the PrePostSequence and DiagnosticTrace files.

  What do these activities do?

The PrePostSequence activity is like a Sequence activity but also includes a Pre activity that will execute before the body and a Post activity that will execute after the body.

The DiagnosticTrace activity outputs a message to System.Diagnostics.Trace as well as a CustomTracking record

2.       Press CTRL+SHIFT+B to build the solution

3.       The HelloWorkflow project will need a reference to the HelloWorkflow.Activities project.  To add the reference, right click on the HelloWorkflow project and select Add Reference… in the projects tab select HelloWorkflow.Activities and click OK to add the reference. Then repeat and add another reference to the WPF library PresentationFramework

4.       Open HelloWorkflow / SayHello.xaml in the designer and notice that the toolbox now contains the new activities.

 

Task 4 – Observe Custom Activities without Designers

In this task you will see how custom activities behave without a designer.  Some can be used without a designer while others may benefit from one or may require one for intended functionality.

Note: You won't be saving SayHello.xaml in this task – we will close it without saving at the end.  The point is just to observe the behavior.

1.       Drop the WriteTwoLines activity on the design surface after the last activity.  Note that you can initialize the arguments for Line1 and Line2 using the property grid.

Figure 61

Initializing the arguments for WriteTwoLines using the property grid.

 

  Do I have to create an activity designer?

No, you don’t.  For many activities, the property grid will be sufficient allowing developers to initialize arguments to your activity. However, adding a designer can improve the experience of using your custom activity making it easier to understand and use.

 

2.       Drop the DiagnosticTrace activity on to the design surface after the last Assign activity.  This activity is a little more complex with an enumerated value but you can still set the properties using the property grid.   You will notice that this activity also provides some validation of the required argument "Text". This type of activity could benefit from a designer.

Figure 62

The property grid for the DiagnosticTrace activity

3.       Drop the PrePostSequence activity on to the surface after the last Assign activity. The activity will work but there is no way to set the body Activities it will execute. You need to create a custom designer to make this activity fully functional.

 

Figure 63

The PrePostSequence activity without a custom designer

 

4.       Close SayHello.xaml without saving it. 

 

Task 5 – Explore Activity Designers

In this task, you will add existing custom activity designers for the DiagnosticTrace and PrePostSequence activity to your project and explore how they work.

1.       Add the existing DiagnosticTraceDesigner.xaml and PrePostSequenceDesigner.xaml files. To do this,

a.       Right click on the HelloWorkflow.Activities project and select Add/New Folder

b.      Name the folder Designers

c.       Right-click the Designers folder, and select Add / Existing Item... browse to Source\Assets folder of this lab (choosing the folder that matches the language of your preference) and select both XAML files.  Be sure to select the file type XAML Files in the dialog as shown.

Figure 64

Add the existing XAML Designer files

2.       The HelloWorld.Activities project Designers folder should have the two designers added.

Figure 65

The HelloWorkflow.Activities folder with two activity designers

**3.      ** Next let’s take a tour of these designers and learn about how they work.  Open DiagnosticTraceDesigner.xaml

4.       Notice the DataTemplates – there is one for an Expanded view and one for a Collapsed view.  The templates contain the body of your controls

Figure 66

The DataTemplates contain the body of the controls for the designer

5.       There is a lot to see in this designer but let’s focus on two things.  The first is the ExpressionTextBox.  This control allows the user to edit an InArgument<T> value with design time support for Intellisense (when used within Visual Studio) and compile checking of the expression.  The DiagnosticTrace activity uses an InArgument<string> type for the Text property.  Below you can see the Collapsed data template using the ExpressionTextBox

XAML

<DataTemplate x:Key="Collapsed">

    <StackPanel Orientation="Horizontal">

        <!--Show only the text when collapsed-->

        <TextBlock

            VerticalAlignment="Center"

            Margin="5"

            >

            Text

        </TextBlock>

**        <sapv:ExpressionTextBox**

**            HintText="Text"**

**            Expression="{Binding Path=ModelItem.Text, Mode=TwoWay,**

**                Converter={StaticResource ArgumentToExpressionConverter},**

**                ConverterParameter=In }"**

**            ExpressionType="s:String"**

**            OwnerActivity="{Binding Path=ModelItem}"**

**            Width="300"**

**            Margin="0,5"**

**            MaxLines="1"**

**        />**

    </StackPanel>

</DataTemplate>

6.       The next thing to note is found in the Expanded data template.  It contains a ComboBox that will display the list of trace values.  The DiagnosticTrace activity uses a CLR property named “Level” for this trace value.  To bind to a CLR property you need to use ModelItem.(property name) and the ModelToObjectValueConverter as you see below.

XAML

<ComboBox

    ItemsSource="{Binding Source={StaticResource TraceLevelValues}}"

    SelectedValue="{Binding Path=ModelItem.Level, Mode=TwoWay, Converter={StaticResource ModelToObjectValueConverter}}"

    VerticalAlignment="Center"

    Grid.Row="2"

    Grid.Column="1"

    Margin="0,5">

</ComboBox>

 

7.       The PrePostSequence designer is more complex.  Because it is a composite activity, it supports dropping other activities on the design surface.  Open PrePostDesigner.XAML

8.       This activity designer uses data binding to bind to the properties of the PrePostSequence class.  The Pre and Post properties are a single activity so the designer uses WorkflowItemPresenter to enable a design surface for them which allows the user to drop a single activity.

XAML

<sap:WorkflowItemPresenter

    Margin="3"

    Item="{Binding Path=ModelItem.Pre, Mode=TwoWay}"

    HintText="Drop activity here"/>

 

**9.      ** The Activities collection uses the WorkflowItemsPresenter to create a design surface that can hold a collection of activities. In the XAML below you see how you can create a design surface that looks similar to the Sequence.

XAML

<sap:WorkflowItemsPresenter

    Margin="3"

    Items="{Binding Path=ModelItem.Activities}"

    HintText="Drop activities here">

    <sap:WorkflowItemsPresenter.SpacerTemplate>

        <DataTemplate>

            <Grid>

                <Path

                    Fill="Red"

                    Width="20"

                    Height="30"

                    HorizontalAlignment="Center"

                    Data="M 0,10 l 20,0 l -10,10 Z" />

            </Grid>

        </DataTemplate>

    </sap:WorkflowItemsPresenter.SpacerTemplate>

    <sap:WorkflowItemsPresenter.ItemsPanel>

        <ItemsPanelTemplate>

            <StackPanel Orientation="Vertical"/>

        </ItemsPanelTemplate>

    </sap:WorkflowItemsPresenter.ItemsPanel>

</sap:WorkflowItemsPresenter>

 

 

10.   Press CTRL+SHIFT+B to build the solution

 

Task 6 – Linking the Activity Designers to the Activity

Now you have designers for your activities but they will not be used until you link the designers to your custom activities.  There are two ways to do this, a declarative way and an imperative way.  We will use the declarative approach.

1.       Open DiagnosticTrace.cs (C#) and add the following namespace directives

C#

using HelloWorkflow.Activities.Designers;

2.       Add the Designer attribute to the class as shown

C#

[Designer(typeof(DiagnosticTraceDesigner))]

public sealed class DiagnosticTrace : CodeActivity

{

 

Visual Basic

<Designer(GetType(DiagnosticTraceDesigner))>

Public NotInheritable Class DiagnosticTrace

 

 

3.       Open PrePostSequence.cs (C#) and add the following namespace directives

C#

using HelloWorkflow.Activities.Designers;

 

4.       Add the Designer attribute to the PrePostSequence class.

C#

[ContentProperty("Activities")]

[Designer(typeof(PrePostSequenceDesigner))]

public sealed class PrePostSequence : NativeActivity

 

 

Visual Basic

<ContentProperty("Activities")>

<Designer(GetType(PrePostSequenceDesigner))>

Public NotInheritable Class PrePostSequence

Next Step

Exercise 9: Verification

 

Exercise 9: Verification

Task 1 – Prepare the test projects

1.       Press CTRL+SHIFT+B to build the solution.

2.       Add the existing HellowWorkflow.Activities.Tests project to your solution.  To do this, right click on the Solution and select Add/Existing Project… browse to Source\Assets folder of this lab (choosing the folder that matches the language of your preference) and select the HellowWorkflow.Activities.Tests project

3.       Right click on the HellowWorkflow.Activities.Tests project and select Add Reference… and add a project reference to HelloWorkflow.Activities

4.       The HelloWorkflow.Tests project will also need a reference to the custom activity project.  Right click on HelloWorkflow.Tests and select Add Reference. From the project tab select HelloWorkflow.Activities.

5.       Press Ctrl+R, A to run all tests. All the tests should pass.

  How do these Activity tests work?

Take a moment to explore the unit tests from HelloWorkflow.Activities.Tests.  These tests create workflows using code instead of XAML.  This is another option for testing workflows if you prefer to use it.

 

 

Task 2 – Create DiagnosticTrace Test

You need to create a test that verifies the runtime behavior of the DiagnosticTrace activity.  There are two elements of the behavior you want to test for.  The activity should emit a trace via System.Diagnostics.Trace and also a tracking record via the workflow tracking.  You will use the classes provided in HelloWorkflow.Activities.Tests to provide a means for testing this behavior.

1.       Right click on HelloWorkflow.Tests and select Add Reference… and add a project reference to HelloWorkflow.Activities.Tests

2.       We will first write the test and then update the workflow. Add the following namespace directives to  SayHelloFixture.cs (C#) or SayHelloFixture.vb (VB) file as shown.

C#

using System.Activities.Tracking;

using HelloWorkflow.Activities.Tests;

 

Visual Basic

Imports System.Activities.Tracking

Imports HelloWorkflow.Activities.Tests

 

3.       Add a new test to the SayHelloFixture.cs (C#) or SayHelloFixture.vb (VB) file as shown.

(Code Snippet - Introduction to WF4 LabShouldOutputVerboseTrace Test CSharp)

C#

[TestMethod()]

public void ShouldOutputVerboseTrace()

{

**    // Arrange**

**    string testCategory = "Test";**

**    string expectedData = "From SayHello";**

**    string expectedTrace = string.Format("{0}: {1}", testCategory, expectedData);**

**    var listener = new TestTraceListener();**

**    var tracker = new TestTrackingParticipant();**

**    var profile = new TrackingProfile()**

**    {**

**        Name = "TestTrackingProfile",**

**        Queries =**

**        {**

**           new CustomTrackingQuery()**

**           {**

**              Name="Test",**

**              ActivityName = "*"**

**           }**

**        }**

**    };**

** **

**    tracker.TrackingProfile = profile;**

** **

**    Trace.Listeners.Add(listener);**

** **

**    var target = new SayHello()**

**        {**

**            UserName = "Test"**

**        };**

** **

**    var workflow = new WorkflowInvoker(target);**

**    workflow.Extensions.Add(tracker);**

** **

**    // Act**

**    workflow.Invoke();**

** **

**    // Assert System.Diagnostics.Trace**

**    Assert.AreEqual(1, listener.Traces.Count);**

**    Assert.AreEqual(expectedTrace, listener.Traces[0]);**

** **

**    // Assert Tracking Records**

**    Assert.AreEqual(1, tracker.Records.Count);**

**    var customRecord = tracker.Records[0] as CustomTrackingRecord;**

**    Assert.AreEqual(expectedData, customRecord.Data["Text"]);**

**    Assert.AreEqual(testCategory, customRecord.Data["Category"]);**

}

 

 

(Code Snippet - Introduction to WF4 LabShouldOutputVerboseTrace Test VB)

Visual Basic

<TestMethod()>

Public Sub ShouldOutputVerboseTrace()

**    ' Arrange**

**    Dim testCategory As String = "Test"**

**    Dim expectedData As String = "From SayHello"**

**    Dim expectedTrace As String = String.Format("{0}: {1}", testCategory, expectedData)**

**    Dim listener = New TestTraceListener()**

**    Dim tracker = New TestTrackingParticipant()**

**    Dim profile = New TrackingProfile() With {.Name = "TestTrackingProfile"}**

** **

**    profile.Queries.Add(New CustomTrackingQuery() With {.Name = testCategory, .ActivityName = "*"})**

**    tracker.TrackingProfile = profile**

**    Trace.Listeners.Add(listener)**

**    Dim target = New SayHello() With {.UserName = "Test"}**

**    Dim workflow = New WorkflowInvoker(target)**

**    workflow.Extensions.Add(tracker)**

** **

**    ' Act**

**    workflow.Invoke()**

** **

**    ' Assert System.Diagnostics.Trace**

**    Assert.AreEqual(1, listener.Traces.Count)**

**    Assert.AreEqual(expectedTrace, listener.Traces(0))**

** **

**    ' Assert Tracking Records**

**    Assert.AreEqual(1, tracker.Records.Count)**

**    Dim customRecord = TryCast(tracker.Records(0), CustomTrackingRecord)**

**    Assert.AreEqual(expectedData, customRecord.Data("Text"))**

**    Assert.AreEqual(testCategory, customRecord.Data("Category"))**

End Sub

 

4.       Run the new test by right clicking on the test name ShouldOutputVerboseTrace and clicking on Run Tests.  The test will fail because you have not added the DiagnosticTrace activity to the SayHello workflow yet.

 

Task 3 – Add a DiagnosticTrace activity to the SayHello Workflow

1.       Open HelloWorkflow / SayHello.xaml in the workflow designer

2.       Drop a DiagnosticTrace activity at the bottom of the sequence and set the properties

a.       Text: "From SayHello"

b.      Category: Test

c.       Trace Level: Verbose

Figure 67

Add the DiagnosticTrace activity

3.       Press Ctrl+R, A to run all tests. All the tests should now pass.  If the debugger breaks on exceptions during the test this is expected – press F5 to continue.

 

Task 4 – Add the PrePostSequence Test

1.       Next you will add a test to verify your PrePostSequence behavior. Once again you will write the test first and then update the workflow. Add a new test to the SayHelloFixture.cs (C#) or SayHelloFixture.vb (VB) file as shown.

(Code Snippet - Introduction to WF4 LabShouldreturnPrePostMessages Test CSharp)

C#

[TestMethod]

public void ShouldReturnPrePostMessages()

{

**    IDictionary<string, object> output;**

** **

**    output = WorkflowInvoker.Invoke(new SayHello()**

**    {**

**        UserName = "Test"**

**    });**

** **

**    Assert.AreEqual("This is Pre-Sequence Ordinal:1", output["PreMessage"]);**

**    Assert.AreEqual("PrePost is Cool Ordinal:2", output["PrePostBody"]);**

**    Assert.AreEqual("This is Post-Sequence Ordinal:3", output["PostMessage"]);**

}

 

(Code Snippet - Introduction to WF4 LabShouldreturnPrePostMessages Test VB)

Visual Basic

<TestMethod()>

Public Sub ShouldReturnPrePostMessages()

**    Dim output As IDictionary(Of String, Object)**

** **

**    output = WorkflowInvoker.Invoke(New SayHello() With {.UserName = "Test"})**

** **

**    Assert.AreEqual("This is Pre-Sequence Ordinal:1", output("PreMessage"))**

**    Assert.AreEqual("PrePost is Cool Ordinal:2", output("PrePostBody"))**

**    Assert.AreEqual("This is Post-Sequence Ordinal:3", output("PostMessage"))**

End Sub

 

2.       Run the new test by right clicking on the test name ShouldReturnPrePostMessages and clicking on Run Tests.  The test will fail because you have not added the PrePostSequence to SayHello yet.

3.       Open HelloWorkflow  / SayHello.xaml

4.       Drop a PrePostSequence after the last activity, you should see the new PrePostSequence Activity Designer

5.       To test that your activity is working you will add 3 new out arguments to hold a message from the Pre and Post areas of the PrePostSequence.  Add PreMessage, PrePostBody and PostMessage as shown.

Figure 68

Add the PreMessage, PrePostBody and PostMessage out arguments

 

6.       To test that the activities are invoked in the correct order you will need a variable to store the ordinal.  Add the Ordinal variable with a default value of 1 to the PrePostSequence scope as shown.

Figure 69

Add the Ordinal variable

7.       Now you will store some values in the arguments you created that can be evaluated by the unit tests.  Drop an Assign activity from the Primitives group  on the Pre-Activities area and set the properties

a.       DisplayName: Set the PreMessage

b.      To: PreMessage

c.       Value: "This is Pre-Sequence Ordinal:" & Ordinal

8.       Increment the Ordinal variable by dropping an Assign activity in the Activities area of the PrePostSequence.

a.       DisplayName: Increment the Ordinal

b.      To: Ordinal

c.       Value: Ordinal + 1

9.       Drop another Assign activity on the Activities area and set the properties

a.       DisplayName: Set the PrePostBody

b.      To: PrePostBody

c.       Value: "PrePost is Cool Ordinal:" & Ordinal

10.   Copy the Increment the Ordinal activity by right clicking on it and select copy

11.   Paste the activity below the Set the PrePostBody activity.  You must right click on the Red arrow to paste the activity.

Figure 70

Copy then Paste the Increment Ordinal activity

12.   Drop another Assign activity on the Post-Activities area and set the properties

a.       DisplayName: Set the PostMessage

b.      To: PostMessage

c.       Value: "This is Post-Sequence Ordinal:" & Ordinal

Figure 71

The Completed PrePostSequence.

 

13.   Press CTRL+SHIFT+B to build the solution

14.   Press Ctrl+R, A to run all tests. All the tests should now pass.

Next Step

Exercise 10: Hosted Designer

 

Exercise 10: Hosted Designer

What if you wanted to create a custom activity like the PrePostSequence and have someone else who does not have Visual Studio use it? Many products allow end users to customize workflows. Windows Workflow Foundation 4 allows you to host the designer in an application quite easily. In this exercise, you will host the designer and use your custom activities.

Task 0 – Opening the Solution

To begin this exercise you can use the solution you finished from Exercise 9. Alternatively, you can follow the following steps to begin with Exercise 10.

1.       Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010.

2.       Open the starting solution for Exercise 10 located under the Source\Ex10-HostedDesigner\Begin folder and use it as the starting point for this exercise.

3.       Press CTRL+SHIFT+B to build the solution.

 

Task 1 – Adding new WPF Application

1.       Right click on the HelloWorkflow solution and select Add / New Project…

2.       From the Windows templates Add new WPF Application named HelloDesigner

3.       Set the HelloDesigner project as the Startup project

4.       Add references to the following assemblies

◦       System.Activities.Presentation

◦       System.Activities.Core.Presentation

◦       System.Activities

5.       From the Projects tab, add the following assemblies:

◦       HelloWorkflow.Activities

◦       HelloWorkflow.Activities.Designers

6.       Open MainWindow.xaml and modify it as shown

(Code Snippet - Introduction to WF4 LabMainWindow XAML CSharp)

XAML (C#)

<Window x:Class="HelloDesigner.MainWindow"

**        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"**

**        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"**

**        Title="MainWindow" Height="600" Width="1000">**

**    <Grid>**

**        <Grid.RowDefinitions>**

**            <RowDefinition Height="4*" />**

**            <RowDefinition Height="*" />**

**        </Grid.RowDefinitions>**

**        <Grid Name="grid1">**

**            <Grid.ColumnDefinitions>**

**                <ColumnDefinition Width="Auto" />**

**                <ColumnDefinition Width="4*" />**

**                <ColumnDefinition Width="Auto" />**

**            </Grid.ColumnDefinitions>**

**        </Grid>**

**        <TextBox Grid.Row="1" Name="textXAML"   
                 VerticalScrollBarVisibility="Visible" />**

**    </Grid>**

</Window>

 

(Code Snippet - Introduction to WF4 LabMainWindow XAML VB)

XAML (VB)

<Window x:Class="MainWindow"

**        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"**

**        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"**

**        Title="MainWindow" Height="600" Width="1000">**

**    <Grid>**

**        <Grid.RowDefinitions>**

**            <RowDefinition Height="4*" />**

**            <RowDefinition Height="*" />**

**        </Grid.RowDefinitions>**

**        <Grid Name="grid1">**

**            <Grid.ColumnDefinitions>**

**                <ColumnDefinition Width="Auto" />**

**                <ColumnDefinition Width="4*" />**

**                <ColumnDefinition Width="Auto" />**

**            </Grid.ColumnDefinitions>**

**        </Grid>**

**        <TextBox Grid.Row="1" Name="textXAML"**

**                 VerticalScrollBarVisibility="Visible" />**

**    </Grid>**

</Window>

 

7.       Open MainWindow.xaml.cs (C#) or MainWindow.xaml.vb

8.       Add the following namespace directives

(Code Snippet - Introduction to WF4 LabMainWindow Namespaces CSharp)

C#

using System.Activities.Presentation;

using System.Activities.Statements;

using System.Activities.Presentation.Toolbox;

using System.Activities.Core.Presentation;

using System.Activities.Presentation.Metadata;

using System.ComponentModel;

using HelloWorkflow.Activities;

using HelloWorkflow.Activities.Designers;

 

(Code Snippet - Introduction to WF4 LabMainWindow Namespaces VB)

Visual Basic

Imports System.Activities.Presentation

Imports System.Activities.Statements

Imports System.Activities.Presentation.Toolbox

Imports System.Activities.Core.Presentation

Imports System.Activities.Presentation.Metadata

Imports System.ComponentModel

Imports HelloWorkflow.Activities

Imports HelloWorkflow.Activities.Designers

 

9.       Add a field of type WorkflowDesigner

C#

public partial class MainWindow : Window

{

**     WorkflowDesigner** workflowDesigner = new WorkflowDesigner();

 

Visual Basic

Class MainWindow

**    Private workflowDesigner As WorkflowDesigner = New WorkflowDesigner()**

 

10.   Create a new function RegisterMetadata as shown.  This function enables the designer metadata store.

(Code Snippet - Introduction to WF4 LabRegisterMetadata method CSharp)

C#

private void RegisterMetadata()

{

**    DesignerMetadata metaData = new DesignerMetadata();**

**    metaData.Register();**

**    AttributeTableBuilder builder = new AttributeTableBuilder();**

**    MetadataStore.AddAttributeTable(builder.CreateTable());**

}

 

(Code Snippet - Introduction to WF4 LabRegisterMetadata method VB)

Visual Basic

Public Sub RegisterMetadata()

**    Dim metaData As DesignerMetadata = New DesignerMetadata()**

**    metaData.Register()**

**    Dim builder As AttributeTableBuilder = New AttributeTableBuilder()**

**    MetadataStore.AddAttributeTable(builder.CreateTable())**

End Sub

 

Note: When you host the designer you can control the toolbox. You choose what controls will appear, the categories they appear in and even the names of the controls.  Add the CreateToolboxControl function as shown.

 

(Code Snippet - Introduction to WF4 LabCreateToolboxControl method CSharp)

C#

private ToolboxControl CreateToolboxControl()

{

**    return new ToolboxControl()**

**    {**

**        Categories =**

**        {**

**            new ToolboxCategory("Hello Workflow")**

**            {**

**                Tools =**

**                {**

**                    new ToolboxItemWrapper(**

**                    "System.Activities.Statements.Assign",**

**                    "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",**

**                    null,**

**                    "Assign"),**

** **

**                    new ToolboxItemWrapper(**

**                    "System.Activities.Statements.Sequence",**

**                    "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",**

**                    null,**

**                    "Sequence"),**

** **

**                    new ToolboxItemWrapper(**

**                    "System.Activities.Statements.TryCatch",**

**                    "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",**

**                    null,**

**                    "Try It"), // Can use a different name**

** **

**                    new ToolboxItemWrapper(**

**                    "HelloWorkflow.Activities.PrePostSequence",**

**                    "HelloWorkflow.Activities",**

**                    null,**

**                    "PrePostSequence"),**

** **

**                    new ToolboxItemWrapper(**

**                    "HelloWorkflow.Activities.DiagnosticTrace",**

**                    "HelloWorkflow.Activities",**

**                    null,**

**                    "Diagnostic Trace")**

**                }**

**            }**

**        }**

**    };**

}

 

(Code Snippet - Introduction to WF4 LabCreateToolboxControl method VB)

Visual Basic

Private Function CreateToolboxControl() As ToolboxControl

**    'Create the ToolBoxControl**

**    Dim ctrl As ToolboxControl = New ToolboxControl()**

** **

**    'Create a collection of category items**

**    Dim category As ToolboxCategory = New ToolboxCategory("Hello Workflow")**

** **

**    'Creating toolboxItems**

**    category.Add(New ToolboxItemWrapper(**

**    "System.Activities.Statements.Assign",**

**    "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",**

**    Nothing,**

**    "Assign"))**

** **

**    category.Add(New ToolboxItemWrapper(**

**    "System.Activities.Statements.Sequence",**

**    "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",**

**    Nothing,**

**    "Sequence"))**

** **

**    category.Add(New ToolboxItemWrapper(**

**    "System.Activities.Statements.TryCatch",**

**    "System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",**

**    Nothing,**

**    "Try It")) ' Can use a different name**

** **

**    category.Add(New ToolboxItemWrapper(**

**    "HelloWorkflow.Activities.PrePostSequence",**

**    "HelloWorkflow.Activities",**

**    Nothing,**

**    "PrePostSequence"))**

** **

**    category.Add(New ToolboxItemWrapper(**

**    "HelloWorkflow.Activities.DiagnosticTrace",**

**    "HelloWorkflow.Activities",**

**    Nothing,**

**    "Diagnostic Trace"))**

** **

**    'Adding the category to the ToolBox control.**

**    ctrl.Categories.Add(category)**

**    Return ctrl**

End Function

** **

 

11.   Your Designer window will display the XAML of the workflow as it built.  To do this you will need a function to handle the ModelChanged event.  Add the ShowWorkflowXAML function as shown.

(Code Snippet - Introduction to WF4 LabShowWorkflowXAML method CSharp)

C#

private void ShowWorkflowXAML(object sender, EventArgs e)

{

**    workflowDesigner.Flush();**

**    textXAML.Text = workflowDesigner.Text;**

}

 

Visual Basic

Private Sub ShowWorkflowXAML(ByVal sender As Object, ByVal e As EventArgs)

**    workflowDesigner.Flush()**

**    textXAML.Text = workflowDesigner.Text**

End Sub

 

12.   Create the AddDesigner function as shown.  This function will add the designer to your window

(Code Snippet - Introduction to WF4 LabAddDesigner method CSharp)

C#

private void AddDesigner()

{

**    //Create an instance of WorkflowDesigner class**

**    this.workflowDesigner = new WorkflowDesigner();**

** **

**    //Place the WorkflowDesigner in the middle column of the grid**

**    Grid.SetColumn(this.workflowDesigner.View, 1);**

 

**    // Show the XAML when the model changes**

**    workflowDesigner.ModelChanged += ShowWorkflowXAML;**

** **

** **

**    //Load a new Sequence as default.**

**    this.workflowDesigner.Load(new Sequence());**

** **

**    //Add the WorkflowDesigner to the grid**

**    grid1.Children.Add(this.workflowDesigner.View);**

** **

**    // Add the Property Inspector**

**    Grid.SetColumn(workflowDesigner.PropertyInspectorView, 2);**

**    grid1.Children.Add(workflowDesigner.PropertyInspectorView);**

** **

**    // Add the toolbox**

**    ToolboxControl tc = CreateToolboxControl();**

**    Grid.SetColumn(tc, 0);**

**    grid1.Children.Add(tc);**

** **

**    // Show the initial XAML**

**    ShowWorkflowXAML(null, null);**

}

 

(Code Snippet - Introduction to WF4 LabAddDesigner method VB)

Visual Basic

Private Sub AddDesigner()

** **

**    'Create an instance of WorkflowDesigner class**

**    workflowDesigner = New WorkflowDesigner()**

** **

**    'Place the WorkflowDesigner in the middle column of the grid**

**    Grid.SetColumn(workflowDesigner.View, 1)**

** **

**    ' Setup the Model Changed event handler**

**    AddHandler workflowDesigner.ModelChanged, AddressOf ShowWorkflowXAML**

** **

**    'Load a new Sequence as default.**

**    workflowDesigner.Load(New Sequence())**

** **

**    'Add the WorkflowDesigner to the grid**

**    grid1.Children.Add(workflowDesigner.View)**

** **

**    ' Add the Property Inspector**

**    Grid.SetColumn(workflowDesigner.PropertyInspectorView, 2)**

**    grid1.Children.Add(workflowDesigner.PropertyInspectorView)**

** **

**    ' Add the toolbox**

**    Dim tc As ToolboxControl = CreateToolboxControl()**

**    Grid.SetColumn(tc, 0)**

**    grid1.Children.Add(tc)**

** **

**    ' Show the initial XAML**

**    ShowWorkflowXAML(Nothing, Nothing)**

End Sub

** **

 

13.   Modify the constructor to call the functions you added

(Code Snippet - Introduction to WF4 LabMainWindow method CSharp)

C#

public MainWindow()

{

**    InitializeComponent();**

**    RegisterMetadata();**

**    AddDesigner();**

}

 

(Code Snippet - Introduction to WF4 LabMainWindow method VB)

Visual Basic

Public Sub New()

**    InitializeComponent()**

**    RegisterMetadata()**

**    AddDesigner()**

End Sub

 

Next Step

Exercise 10: Verification

 

Exercise 10: Verification

In this verification you will run the HelloDesigner Application and modify  workflow

1.       Right click on the HelloDesigner project and select Set as StartUp Project

2.       Press F5 to launch the application in the debugger

3.       When the design window appears drop a DiagnosticTrace and PrePostSequence on the designer surface

4.       You should see the following

Figure 73

The HelloDesigner application hosting the WorkflowDesigner

 

Next Step

Summary

 

Summary

In this Hands-On Lab you have learned the basics of workflow authoring in Windows Workflow Foundation 4 by creating a simple Windows Workflow application. During the lab you learned how to create a workflow using the new Visual Studio 2010 workflow designer, and as an alternative, you also learned how create it using C# or VB code. You have explored the use of input and output arguments, seen how to add if/else logic to your workflow with the If activity and, finally, you have learned how to handle error conditions in your workflows by using exception-handling specific activities like the Try/Catch, Catch<T> and Throw activities. Of course, there is much more to Windows Workflow 4. You recommend you look at the Introduction to Workflow Services lab for more information about building Workflow enabled applications.

  Feedback

What do you think of this lab? What do you think of the new Windows Workflow 4? Your feedback is important; it will help us build the best product for you. Please take a moment to provide it. Send your comments to wfwcfhol@microsoft.com