RioterDeckers' HeadQuarter

Our opinions about Microsoft technologies and the way we are working with.
Welcome to RioterDeckers' HeadQuarter Sign in | Join | Help
in Search

Nezdeboeuf

[System.Workflow.ComponentModel.ActivityCondition] At your service.


Introduction

The march showers keep one's promises, articles about Windows Workflow Foundation are pouring:

March has even its own CTP announced by Paul Andrew on his blog : Windows Workflow Foundation Beta 2.2 Released Today
This march CTPis a thunder clap in the WinFx CTP sky, indeed its characteristics seem to be NOT compatible :

  • "The Beta 2.2 version of Windows Workflow Foundation is NOT compatible with Office "12" ..."
  • "The Beta 2.2 version of Windows Workflow Foundation is NOT compatible with the Microsoft Pre-Release Software WinFX Runtime Components ..."

Based on this and the fact that the WinFX January CTP main point of interest was that it came out pretty much at the same time as the galette des rois, we all agree that the February CTP deserves a better fate.

All the code implemented in this post is based on this WWF build. 

This post deals once more with the wonderful System.Workflow.ComponentModel.ActivityCondition(previously named System.Workflow.ComponentModel.Condition) class but with various options offered to define its own activities (validator, designer...) as well.

My previous post about the condition management in WWF invited Microsoft to support from the designer the configuration of the Condition property of the IfElseBranchActivity class by a custom condition.

We benefitted from the migration of the code of my previous post to offer this support. As we proposed it on this post in the wwf msdn forum, we have added a System.Drawing.Design.UITypeEditor to offer developpers a "Browse and Select a .NET type" dialog. Before listing in details the steps allowing the creation of an advanced activity, I would again promote the advantages to code these ActivityCondition classes by inheriting from the abstract class : ActivityCondition.

System.Workflow.ComponentModel.ActivityCondition

This base class declares only one method : public abstract bool Evaluate(Activity activity, IServiceProvider provider). Only one but a great one, indeed, this method allows gathering any information on the workflow whether its own properties (thanks to the parameter activity and maybe recursive calls to the Parent property of the Activity class.) or its service (thanks to the parameter provider).


You can define business interfaces to facilitate retrieving specific business properties from the workflow. And, there is no constraints enforced on the service definition, even  System.Workflow.Activities.ExternalDataExchangeAttribute (previously named System.Workflow.Activities.DataExchangeServiceAttribute) is not required.
Thus, you can define specific business conditions and package it in specific assemblies. This mechanism replaces advantageously the use of the System.Workflow.Activities.CodeCondition  class. Moreover, the ActivityCondition class inherits from the System.Workflow.ComponentModel.DependencyObject class, your conditions can profit from it by publishing their properties through the use of dependency properties.
Unfortunately, as I mentioned previously, these custom conditions are not supported by the designer. But, this post will sweep this weakness.

Define your own custom Activity

It is not possible to act on the System.Workflow.Activities.IfElseActivity and System.Workflow.Activities.IfElseBranchActivity activities offered by WWF in order to support custom ActivityCondition classes. So, we're going to define our own activities.
I recommend to you not to use the Workflow Activity Library project template provided by WWF to develop "system" activities. Indeed, this project template validates the design of your activity. It is annoying when you define a validator for your activity and that your activity doesn't respect it natively as we can see for the IfElseActivity.
Thus I reserve the Workflow Activity Library project template to define business activity (meaning High level activity).

Redevelop the IfElseActivity

As all activities, the IfElseActivity inherits from the System.Workflow.ComponentModel.Activity class through the System.Workflow.ComponentModel.CompositeActivity class. The logic of your activity must be declared in the Execute and Cancel methods of System.Workflow.ComponentModel.Activity. The CompositeActivity provides default mechanisms to support child activities and the IfElseActivity is composed by at least one IfElseBranchActivity.
So, when you want to develop a new activity, in first you must decide from what class will inherit your activity : Activity (a simple activity without children), CompositeActivity (an activity with children) or System.Workflow.Activities.SequenceActivity (The execution of its children is sequential, one at a time.)

We have no reason to change the logic implemented by the IfElseActivity :

  • The inheritance from the CompositeActivity.
  • The Execute method of the IfElseActivity parses its collection of IfElseBranchActivity activities. For each child activity, the Execute method calls the Evaluate method of the Condition property of the IfElseBranchActivity, if the call returns true, the Execute method calls the ExecuteActivity method of the current execution context on the IfElseBranchActivity.
  • The Cancel method of the IfElseActivity parses also its collection of IfElseBranchActivity activities, and calls for each child activity, the CancelActivity method of the current execution context on the IfElseBranchActivity.

Once, the logic of your activity implemented, you can customize its design :



Implement a validation logic for you activity

The IfElseActivity is used in conjunction with the IfElseBranchActivity. So, you can define a validator to constrains user input. To define a validator, you only need to inherit from the System.Workflow.ComponentModel.Compiler.ActivityValidator class. If your activity is based on the CompositeActivity, you can use, of course, the System.Workflow.ComponentModel.Compiler.CompositeActivityValidator rather than directly the ActivityValidator but don't forget also to call  the base methods in all your overridden methods.

The implementation of a custom validator can be reduced to the implementation of the Validate method. This method must return the errors that occured when the validation process is launched (by the designer for example). The parameter obj is the instance of your custom activity. The definition of an error is guided by the System.Workflow.ComponentModel.Compiler.ValidationError class. The parameter manager allows accessing the validation context and to know if child activities have been validated...
The ValidationError class proposes a specific static method named GetNotSetValidationError to create an error when a required property has not been set.

Guide the design of your activity

A validator offers a canvas that constrains the user input to comply with some pre-defined rules but it doesn't guide the user during the design of the activity. This responsibility is left to the designer as usual in the .NET framework.

WWF provides some ready-to-use designers such as the  System.Workflow.ComponentModel.Design.SequenceDesigner class and the System.Workflow.ComponentModel.Design.ParallelActivityDesigner class. Their use is recommended. These designers reduce in a considerable way the creation of designers. WWF provides specialized designers for each activity type (sequence, parallel...). The designer for the IfElseActivity is based on the ParallelActivityDesigner.
This designer provides mechanism to declare if you can insert (CanInsertActivities), move(CanMoveActivities) and remove(CanRemoveActivities) a child activity. You can even act on the menu item named "Add Branch" of the context menu associated to your activity by overriding the OnCreateNewBranch method .

So, our designer will inherit from the ParallelActivityDesigner and the OnCreateNewBranch method will return an instance of our custom IfElseBranchActivity, we allow to insert an IfElseBranchActivity, it will not be possible to remove the last IfElseBranchActivity.

Publish your activity to the toolbox window.

You can define :

  • A custom bitmap for the representation of your activity in the toolbox window. To do this, you add the ToolboxBitmap attribute to your activity class.
  • And actions that will be executed when the user drags and drops the toolbox item on the designer. To do this, you add the ToolboxItem attribute to your activity class and reference with this attribute the class that contains the actions to execute.

Similarly to the designer, WWF provides a base class specializing the implementation of a ToolboxItem. So, Let's take advantage of it.
When you drag and drop the IfElse toolbox item, the designer proposes to you by default two sub IfElseBranchActivity (for if and else). To do this, you only need to override the CreateComponentsCore method and instantiate our activity by adding two custom IfElseBranch activities.

In the case of IfElseBranchActivity, you don't need to publish it in the toolbox window. You use a specific constructor of the ToolboxItem attribute by passing a boolean parameter set to false.

Redevelop the IfElseBranchActivity

We must act simply on the design customization of the Condition property. As told at the top of this post, we're going to offer a "Browse and Select a .NET type" dialog to choose what  condition is associated to the IfElseBranchActivity.
WWF is definitely a great library, because this dialog is already offered with TypeBrowserEditor. However, we cannot simply reference it by using Editor attribute, indeed the ActivityCondition class defines its own type converter.
Moreover, it is nice to filter the types browsed by the TypeBrowserEditor. I prefer to invite you to read this post from Daniel Cazzulino to have more details on this functionnality.
So, the last two steps are : the implementation of a type converter and a type filter.

Implement a type converter

If you want to have more details about type converter implementation, you can read this MSDN page.
The only specificity of our type converter is that the GetProperties method must return the properties of the selected ActivityCondition.These properties can be specific, and the PropertyGrid must be updated accordingly. The internal WWF ConditionTypeConverter solves this problem by using the TypeDescriptor.GetConverter method on the selected ActivityCondition type. Based on the type converter returned, you only need to call its GetProperties implementation.

Implement a type filter

To filter the types browsed by the TypeBrowserEditor, you must implement the ITypeFilterProvider. This interface offers a method CanFilterType and a property FilterDescription. The property is used to name simply the TypeBrowserEditor. And the method as the name indicates it, returns if a type must be taken into account.
Daniel Cazzulino mentions it, but I prefer to draw your attention on it: it's necessary that your filer type provider implements a specific constructor : public xxxTypeFilterProvider(IServiceProvider serviceProvider){}if you want it to be supported by the TypeBrowserEditor.

Conclusion

To conclude, you can download below the source code of a sample application and a video. The sample application is based on a workflow which is composed of two custom IfElseActivity (RioterDeckerIfElseActivity).

This sample application is divided into three projects :

  • A sequential workflow console application project that contains the workflow,
  • A class library project which contains a custom activity condition referenced by the workflow,
  • A class library that contains our custom IfElseActivity and IfElseBranchActiviy.
The video shows how to add a custom condition to the sample application.
Have fun!


Download the code here.
Download the video here.

Thanks to
Thor's help, our collaboration assures more posts around WWF and WCF integration so stay tuned!
Published Monday, June 05, 2006 4:44 PM by raskal

Comments

No Comments
Anonymous comments are disabled

This Blog

Post Calendar

<June 2006>
SuMoTuWeThFrSa
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678

Syndication

Powered by Community Server, by Telligent Systems