Activity Binding in Windows Workflow Foundation

Windows Workflow Foundation provides a visual interface for creating and hosting workflows that integrate seamlessly into line-of-business applications built on the .NET 3.0 Framework. Activity binding, one feature of Windows Workflow Foundation, allows an activity author to expose the properties of his or her activity to other activities at a workflow level. Once the properties are available at the workflow level, they can interact with the components that serve as the solution's user interface. This allows developers to set activity properties dynamically in a way that they are familiar with in a Visual Studio environment. This article walks through an example solution that demonstrates activity binding.

Why Use Activity Binding?

Workflows provide a means to perform functions behind the scenes. Take, for example, a solution that interacts with a user through an interface and asks for a social security number. The workflow component of this solution has two activities: One retrieves contact information for the employee, and the other retrieves the employee's pay information. The question, then, is how does the solution propagate the social security number entered by the user to the activities that require the information to perform their functions? The answer is activity binding. (The code example in the following sections will show how to accomplish this.)

Activity binding also allows the solution to relay the social security number entered by the user to both activities while requiring the user to enter the number only once. This kind of reuse is a core concept in efficient, user-, and developer-friendly applications. The solution also could use activity binding in an activity-to-activity context, allowing one activity to synchronize a value with one supplied by another activity. Activity binding is, therefore, the glue between components in Windows Workflow.

Creating a Custom Activity

The first step in creating the example solution is to create a simple custom activity. In Visual Studio 2005, start a new solution with a Workflow Activity Library project. Visual Studio will provide a base activity (Activity1). Rename the activity to something suitable. The example in this article uses "GreetingActivity." The GreetingActivity will be responsible for greeting a user with his or her first and last name; therefore, the activity needs two dependency properties: FirstName and LastName. Dependency properties are properties that are exposed to the properties window in Visual Studio, allowing easy, visual configuration. (Dependency properties fall outside the scope of this article. For more information, please see MSDN.)

To configure FirstName and LastName, open the code-behind for the custom activity. Visual Studio again comes to the rescue. To create a dependency property, insert a Workflow, Dependency Property code snippet. Name it, set its type, provide a description that will be presented in the property window, and place it in a category. For this example, the two dependency properties are configured as follows:

namespace MyActivityLibrary
{
   public partial class GreetingActivity: SequenceActivity
   {
      public static DependencyProperty FirstNameProperty =
         System.Workflow.ComponentModel.DependencyProperty.
         Register("FirstName", typeof(string), 
         typeof(GreetingActivity));

      [Description("The First Name of the User")]
      [Category("Dependency Properties")]
      [Browsable(true)]
      [DesignerSerializationVisibility
         (DesignerSerializationVisibility.Visible)]
      public string FirstName
      {
         get
         {
            return ((string)(base.GetValue
               (GreetingActivity.FirstNameProperty)));
         }
         set
         {
            base.SetValue(GreetingActivity.FirstNameProperty, value);
         }
      }

      public static DependencyProperty LastNameProperty =
         System.Workflow.ComponentModel.DependencyProperty.
         Register("LastName", typeof(string), 
         typeof(GreetingActivity));

      [Description("The Last Name of the User")]
      [Category("Dependency Properties")]
      [Browsable(true)]
      [DesignerSerializationVisibility
         (DesignerSerializationVisibility.Visible)]
      public string LastName
      {
         get
         {
            return ((string)(base.GetValue
               (GreetingActivity.LastNameProperty)));
         }
         set
         {
            base.SetValue(GreetingActivity.LastNameProperty, value);
         }
      }

Now that the dependency properties have been defined, it is time to define the activity's function. This is done by overriding the Execute method of the activity. Simply add the following code:

protected override ActivityExecutionStatus Execute
   (ActivityExecutionContext executionContext)
{
   Console.WriteLine("Hello, " + FirstName + " " + LastName);
   return base.Execute(executionContext);
}

Now, the activity is ready to be consumed by a workflow.

Creating a Workflow to Consume a Custom Activity

Next, you add a new Sequential Workflow Console Application project to the solution. The GreetingActivity will be available in the Toolbox, and you now can drag it to the new workflow. The dependency properties you defined are displayed in the properties window for the activity in the workflow (see Figure 1).

Figure 1: Dependency Properties Displayed In Properties Window

This is very useful for workflow designers who wish to configure the properties of a custom activity in the visual designer. This can make your custom workflow appear professional and integrate flawlessly with the built-in activities of Windows Workflow Foundation.

However, you can expose these properties at the workflow level, offering additional flexibility and functionality to your applications. You can accomplish this in two ways: a code-driven method for the codeslingers and a visual method for those who prefer configuration in Visual Studio. The first is adding the properties manually to the workflow code-behind and then binding them to the activity properties with the designer. To do this, add the following properties (named WorkflowFirstName and WorkflowLastName to distinguish them from the properties defined at the activity level) to the code-behind:

private string workflowFirstName;

public string WorkflowFirstName
{
   get { return workflowFirstName; }
   set { workflowFirstName = value; }
}

private string workflowFirstName;

public string WorkflowLastName
{
   get { return WorkflowLastName; }
   set { WorkflowLastName = value; }
}

Now, return to the designer. In the properties window for the activity, click the ellipsis in the text box for the FirstName property. Choose the "Bind to an existing member" tab of the dialog and select the WorkflowFirstName property you just defined (see Figure 2).

Figure 2: WorkflowFirstName Property in "Bind to an existing member" Tab

This associates the value of the WorkflowFirstName property you can access from within the console application with the value of the activity's property. Do the same thing with the LastName property, this time associating it with WorkflowLastName.

For those who find manually typing out property code too strenuous, you can let the designer do all of the work. Instead of switching to the code-behind, stay in the designer and click the FirstName ellipsis in the activity's property window. This time, choose the "Bind to a new member" tab. Type in "WorkflowFirstName" as the name and choose Create Property (see Figure 3). Do the same for LastName, binding it to a new property named "WorkflowLastName".

Figure 3: Type in "WorkflowFirstName" and Choose Create Property

The designer adds two dependency properties to the code-behind for you. They will accomplish the same thing as properties added manually, and it binds them to the activity properties.

Activity Binding in Windows Workflow Foundation

Putting It All Together

Now, it is time to see all your hard work in action. You can modify the Main method in Program.cs in your console application to prompt the user for a first and last name, like so:

Console.Write("First Name:  ");
string firstName = Console.ReadLine();

Console.Write("Last Name:  ");
string lastName = Console.ReadLine();

Now, you must add the user input to a dictionary object that you will pass to the CreateWorkflow method. (For more information regarding dictionary objects, please see MSDN.) The CreateWorkflow method is overloaded to include a signature that allows the passing of a dictionary object as a parameter to be made available to the workflow. The names of the dictionary entries correspond to the names of the workflow's properties.

static void Main(string[] args)
{
   Console.Write("First Name:  ");
   string firstName = Console.ReadLine();

   Console.Write("Last Name:  ");
   string lastName = Console.ReadLine();

   Dictionary<String, Object> parameters =
      new Dictionary<string, object>();
   parameters.Add("WorkflowFirstName", firstName);
   parameters.Add("WorkflowLastName", lastName);

   using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
   {
      AutoResetEvent waitHandle = new AutoResetEvent(false);
      workflowRuntime.WorkflowCompleted += delegate(object sender,
         WorkflowCompletedEventArgs e) {waitHandle.Set();};
      workflowRuntime.WorkflowTerminated += delegate(object sender,
         WorkflowTerminatedEventArgs e)
      {
         Console.WriteLine(e.Exception.Message);
         waitHandle.Set();
      };

      WorkflowInstance instance = 
         workflowRuntime.CreateWorkflow(typeof
           (MyWorkflowConsoleApplication.MyWorkflow), parameters);
      instance.Start();

      waitHandle.WaitOne();
   }

   Console.ReadLine();
}

All that you have left to do now is run the application (see Figure 4).

[Activity10.jpg]

Figure 4: Run GreetingActivity Application

Binding Activity Properties to Other Activity Properties

Activity binding is not limited to activity-property-to-workflow-property binding. You also can bind the dependency properties of an activity to the properties of another activity. As an example, I created another activity called LogActivity in the MyActivityLibrary project and added an instance of the activity to the workflow. This activity has two dependency properties, LogFirstName and LogLastName, which I bound to the GreetingActivity's FirstName and LastName properties in the workflow designer (see Figure 5).

[Activity11.jpg]

Figure 5: Binding LogFirstName and LogLastName to the GreetingActivity's FirstName and LastName

I then overrode the LogActivity's Execute method, like so:

protected override ActivityExecutionStatus Execute
   (ActivityExecutionContext executionContext)
{
   Console.WriteLine(LogFirstName + " " + LogLastName +
                     " logged in at " + DateTime.Now.ToString());
   return base.Execute(executionContext);
}

Those are the only changes I needed to bind the new activity's properties to the GreetingActivity's properties, which, in turn, are those retrieved by the console application. Figure 6 shows the result.

[Activity13.jpg]

Figure 6: Result of LogActivity

Note: You can achieve the same result in Figure 6 by binding the LogActivity's properties directly to the properties exposed in the workflow, but this example demonstrates only how you can bind an activity to another activity.

Create Custom Activities

Dependency properties and activity binding allow a developer to create custom activities. These activities offer such seamless reusability and ease of implementation that they become indistinguishable from the out-of-the-box activities provided by Windows Workflow Foundation. In addition to binding an activity's dependency properties to workflow properties, the dependency properties also can be bound to other activities' properties. Activity binding, therefore, permits behind-the-scenes propagation of values, allowing activities and workflows to share information.

Activity binding obviously can provide value beyond the simple example presented in this article. It can add functionality to your applications by connecting the components of your workflow and by allowing top-level application code to communicate down to the activity level of a workflow.

About the Author

Rachel Wireman (MCTS) is a developer specializing in Windows-based solutions at Crowe Chizek in Oak Brook, Illinois. Reach her at rwireman@crowechizek.com.



Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Hundreds of millions of users have adopted public cloud storage solutions to satisfy their Private Online File Sharing and Collaboration (OFS) needs. With new headlines on cloud privacy issues appearing almost daily, the need to explore private alternatives has never been stronger. Join ESG Senior Analyst Terri McClure and Connected Data in this on-demand webinar to take a look at the business drivers behind OFS adoption, how organizations can benefit from on-premise deployments, and emerging private OFS …

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

Most Popular Programming Stories

More for Developers

RSS Feeds