
Our First Workflow
Maybe you've had one of those product managers who is always at your desk, asking "are you done, yet?" In this section, we will replace the annoying project manager with a trivial Windows Workflow program. The sample isn't meant to demonstrate all the capabilities of the platform, but give a general feel for creating and running a workflow with WF.
Before we can begin, we'll need to download and install the .NET 3.0 framework. The installation program is available from http://netfx3.com. Supported development tools for the .NET 3.0 framework include all editions of Visual Studio 2005. We'll also need to download and install Visual Studio 2005 Extensions for Windows Workflow Foundation. The extensions are also available from http://netfx3.com. The extensions are not compatible with the Express editions of Visual Studio 2005.
First, we'll use Visual Studio to create a new Workflow project (File | New Project). We'll choose C# as our language and select the Sequential Workflow Console Application template (see the screenshot on the next page). The template gives us a project with references to all the correct WF assemblies, an empty workflow, and a Program.cs
file to drive the workflow. Right-click the workflow and select Delete so we can start a workflow from scratch.

We can now right-click the project file in the Solution Explorer window and select Add New Item. From the list of items we'll choose Sequential Workflow (with code separation) and give the item the name of workflow1.xoml (see screenshot below). This XOML file will contain the XAML definition of our workflow.

If we click to expand the node containing Workflow1.xoml, we will find a C# code-beside file (Workflow1.xoml.cs
) containing a partial class. As we mentioned earlier, the partial class will combine with the class generated from the XAML to produce a single type. Let's modify the class in Workflow1.xoml.cs
by adding an IsFixed
property with a backing field, as shown below:
public partial class Workflow1 : SequentialWorkflowActivity { private bool _isFixed; public bool IsFixed { get { return _isFixed; } set { _isFixed = value; } } }
If we double-click the .xoml
file, the designer will appear. At this point we would want to open the Toolbox window if is not open (Ctrl+Alt+X). We can drag a While activity from the Toolbox and drop the activity between the start and end point of our workflow. The While Activity executes a child task until some condition is met. Our next step is to drag a Code activity from the Toolbox into the center of the While activity. At this point, our designer should resemble the following screenshot:

Notice both activities display a red exclamation point. The activities are failing their validation checks. We can hover the mouse cursor over the exclamation points and open a smart tag to view the validation error. If we tried to compile the program we'd see these same validation errors as compilation errors. We'll fix these errors now.
The Code activity requires us to assign an event handler for the ExecuteCode
event. We can set the event by opening the Properties window (F4) and clicking the Code activity to set focus. Double-clicking in the empty space beside the ExecuteCode property will send us into the code-beside file and generate a new event handler. We can place the following code into the event handler. This code will ask the user if a bug is fixed, and then read a key press. If the user presses the 'y' key, the code will set the _isFixed
field to true
.
private void codeActivity1_ExecuteCode(object sender, EventArgs e) { Console.WriteLine("Is the bug fixed?"); Char answer = Console.ReadKey().KeyChar; answer = Char.ToLower(answer); if (answer == 'y') { _isFixed = true; } else { Console.WriteLine(); Console.WriteLine("Get back to work!"); Console.WriteLine(); } }
The Code activity should now pass validation, so we can turn our attention to the While activity. A While activity requires a valid Condition
property. Several activities in the base activity library work with conditions, including the IfElse, ConditionedActivityGroup
, and Replicator
activities. Chapter 9 will cover conditions and rules in more detail.
We can set the Condition property of our activity by opening the drop‑down list beside the Condition property in the Properties window. We have the choice of selecting a CodeCondition or a RuleConditionReference. These choices represent the two techniques available to express a condition, the first being with code (a method that returns a Boolean value), the second being with a rule. Let's select the RuleConditionReference. A rule condition is a named expression that evaluates to true or false, and can live in an external .rules
file for easy maintenance. A plus sign appears beside the Condition property, and we can click the sign to expand the property editor.
When the Condition property expands, the Property window gives us the ability to set a ConditionName and an Expression. Clicking on the ellipsis (…) button in the Condition name will launch a Select Condition dialog box.

Clicking the New Condition... button will launch the Rule Condition Editor.

We want the While activity to loop until the bug is fixed. Our rule is !this.IsFixed
. Once we've entered the condition (notice the editor provides IntelliSense), we can click OK. When we return to the Select Condition dialog box, we can see the editor has given our condition the name of Condition1. We should select Condition1 and press OK. The While activity should now have a ConditionName and Expression set, and pass validation.
Now we need to open the Program.cs
file, which contains the method Main—the
entry point for our Console application. We need to host the WF runtime and ask the runtime to execute our workflow. The item template for a workflow project provides all the boilerplate code we need. Let's review the code:
class Program { static void Main(string[] args) { WorkflowRuntime workflowRuntime = new WorkflowRuntime(); workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs> (workflowRuntime_WorkflowCompleted); workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs> (workflowRuntime_WorkflowTerminated); WorkflowInstance instance; instance = workflowRuntime.CreateWorkflow(typeof(Workflow1)); instance.Start(); waitHandle.WaitOne(); } static void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e) { Console.WriteLine(e.Exception.Message); waitHandle.Set(); } static void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); } static AutoResetEvent waitHandle = new AutoResetEvent(false); }
The first step is to instantiate a WorkflowRuntime
instance. The code wires up event handlers to the runtime so we know if a workflow terminates (because of an exception), or completes successfully. The code instantiates our bug-fixing workflow using the CreateWorkflow
method, passing the type of our workflow. Since the workflow engine executes our workflow asynchronously, we need to block our thread on an AutoResetEvent
object and wait for the workflow to complete (otherwise, the console mode program would exit before the workflow gets an opportunity to run). An AutoResetEvent
object will block a thread until the object is in a signaled state, which we do with the Set
event in the event handlers.
We can now build our workflow solution and run the executable from the command line.
