Exploring Workflow Foundation Intermezzo 1: Apply what we learned so far – Early post of the Image Processing Activity Library

Just a quick posting of an application of Windows Workflow Foundation in the area of image processing.

When I started the Exploring Workflow Foundation series of posts this is the reason I started them: I wanted to make a library in the area of image processing based on workflow activities. Well, to be honest, it’s more the other way around: I wanted to learn Workflow Foundation and ended up with image processing as a candidate subject. Anyway, this library is not complete, let alone production ready code. It’s what I have so far but it still needs a lof of tweaking. For example, I did not make use of any validation code when compiling th eworkflow, which is something I will definitely add in the future. There is also no real exception handling code. In other words: this version is just a quick post of what I have so far but still needs a lot of work.

So keep watching for updates.

Just go to the ImageProcessing WF page for a download of the available code.

Advertisements

Exploring Workflow Foundation Part 7: Custom Designers – The FreeformActivityDesigner class

Let’s start with a disclaimer here:
First, this is the result of experimenting and watching the Microsoft dll’s with Redgate’s Reflector. This means the code is for demonstartion purposes only and is not at all production ready.
Second, the FreeFoemActivityDesigner is a huge subject. This post describes some scenario’s of what you can do with it but is by no means complete. As I have an idea for using this designer, the subjects handled are choosen with that idea in mind. There are a lot of other possibilities which I will not cover and to whome I don’t know the answer.
Third, because of point two, if you have any questions: I probably don’t know the answer!! But you can ask them anyway.

So, let’s get started…

Adding, Removing and Editing contained Activities

The FreeFormActivityDesigner has in its inheritance tree the CompositeActivityDesigner. This means it is meant to be used for activities deriving from CompositeActivity and thus you can

add other activities to it which will result in addng other ActivityDesigners to your FreeFormActivityDesigner derived class.

The most important methods for managing the contained activities are:

public virtual bool CanInsertActivities(HitTestInfo insertLocation, 
  ReadOnlyCollection<Activity> activitiesToInsert);
public virtual bool CanMoveActivities(HitTestInfo moveLocation, 

orums

  ReadOnlyCollection<Activity> activitiesToMove);
public virtual bool CanRemoveActivities(
  ReadOnlyCollection<Activity> activitiesToRemove);
 
protected virtual void OnContainedActivitiesChanged(
  ActivityCollectionChangeEventArgs listChangeArgs);
protected virtual void OnContainedActivitiesChanging(
  ActivityCollectionChangeEventArgs listChangeArgs);
protected virtual void OnContainedActivityChanged(
  ActivityChangedEventArgs e);
 
public virtual void InsertActivities(HitTestInfo insertLocation, 
  ReadOnlyCollection<Activity> activitiesToInsert);
public virtual void MoveActivities(HitTestInfo moveLocation, 
  ReadOnlyCollection<Activity> activitiesToMove);
public virtual void RemoveActivities(ReadOnlyCollection<Activity> activitiesToRemove);

The methods starting with “Can” and returning a boolean allow you to specify if activities can be acted upon in the composite activity being designed. What action is clear form the name of the method. Mind however that the Move is “Move between designers” and NOT “Move within a designer”.

However, don’t let these methods and their names fool you: they are there for your use and the framework only uses them in two cases:
The first case is the CanRemoveActivities method which gets called if you have an ActivityDesigner selected inside your FreeFormActivityDesigner inherited object and you press the Delete key. If you return false from the method call, your selected activity will not get removed.
The second case is the CanInsertActivities method which only gets called if you Paste an activity in your FreeFormActivityDesigner inherited object.

Bug or Feature?
You’d think that implementing these methods would offer you some benefit during editing of the composite activity but you’d be wrong.
If you want to prevent an activity from being inserted you have to override the OnDragOver method and call the CanInsertActivities from there. Based on the result you’d set the Effect property of the ActivityDragEventArgs argument to Move|Copy or None if insertion is allowed or not.
What I wonder is why Microsoft didn’t implement this behaviuor in there DragDropManager messagefilter.
Same goes for the CanMoveActivities. This method is supposed to signal weather activities can be moved from one designer to another. Again you’d think it would be used during drag/drop manipulations but it isn’t. You’re again on your own to implement this functionality.

So one wonders: is this a bug or a feature. And if it is a feature, then what can be gained by it?

The “On” prefixed methods are more or less self-documenting: the OnContainedActivities methods get called when the contained activities collection is changing or changed. The OnContainedActivityChanged is called when a contained activity is changed. Mind the nuance: the OnContainedActivities methods are called when the collection of contained activities changes, the OnContainedActivityChanged when an activity itself is changed. This last also means changing a property, moving the activity etc…

The last action oriented methods are called when effectively performing the action. The MoveActivities method is called when moving an activity from another designer inside your designer and NOT when moving an activity from your designer into another designer. Infact, in this last case not even the RemoveActivities method is called!!!

Making Connections

The FreeFormActivityDesigner allows you to make connections as you want it, in contrast with for example the Microsoft supplied SequentialActivityDesigner which makes the connections for you. In the last one, there is a default vertical line representing the connection between the start and the end, and you drop activities on this vertical line.

With the FreeFormActivityDesigner you can drop Activities anywhere you want, and they do not get connected by default. Then if you hover over the activity, connectionponts are made visible. Pressing the left mousebutton on such a point and dragging the mouse creates a connection which you can then attach to another activity.

But what is really happening under the hood, and how can you interfere with it?

There are two stages in making connections between activities:

  1. A graphical stage inside the FreeFormActiviyDesigner in which a graphical representation is created of the connection and in which it is also
    decided if a connection can be made. More on this last further
  2. A “business” stage in which the activities are connected. Allthough there is no built-in support to decide if a connection can be made, you should of course also validate this here, or (actually “and”) in the ActivityValidator inherited class for you activity.

Making connections between activity designers

When you press the left mousebutton on an activities connectionpoint and start moving the mouse (keeping the button pressed), deep down in the bowels of the workflow design experience the CreateConnector method of the FreeFormActivityDesigner to which the activity is a child is called with the ConnectionPoint data of the connection point pressed.
You are now given the chance to return your own custom Connector derived class. If for some reason the source connection point is not allowed to function as a source, you simply return null from this method.

When you continue to move the mouse, two methods are continouosly polled:

  • The CanConnectContainedDesigners method of your FreeFormActivityDesigner derived class
  • The CanConnect of your ActivityDesigner derived class

These two give you the chance to tell the designer environment if the connection between the activities can be made. If you return true, then the connector will snap to the target connection point and if you return false nothing will happen.

Connection points

As stated above, connections are made between connection points and your desicion if connections can be made is based on these connection points.
The question is of course how to specify and identify connection points.

Specifying connection points is also a two stage process:

  1. First, you specify the coordinates of the points on the designers edge which can be used as connection points by overriding the GetConnections(DesignerEdges edges) member of the ActivityDesigner class
  2. Next, you specify which connection points will be made visible when the user hovers the mouse over the ActivityDesigner by overriding the GetConnectionPoints(DesignerEdges edges) member

Identifying connection points is done on a designer-edge basis. As you can see from the signature of the above memtioned members, connection points are asked by specifying the edge along which they reside. Mind you however that this DesignerEdges enumeration has the FlagsAttribute attribute, so combinations of it’s members are allowed. Thus, you typically will have code like this:

if((edges & DesignerEdges.Top) == DesignerEdges.Top)
{
  // Add your points for the top here
}
if ((edges & DesignerEdges.Bottom) == DesignerEdges.Bottom)
{
  // Add your points for the bottom here
}

The second property which identifies the connection points is it’s connection index. It’s what? It’s “connection index”. Remember you specified the possible points on the designer edge that could be used as conneciton points. Remember also that you returned these on an DesignerEdge basis and in a collection. Now this “connection index” is the index in this collection to which the connection points map. Or visually:

So, to get the point coordinates mapping to the provided ConnectionPoint, you get the collection of points for the corresponding DesignerEdge, that is ConnectionEdge, and retrieve the point by index in the collection using ConnectionIndex:

Dictionary<DesignerEdges, List<Point>> pointsByEdge;
ConnectionPoint c;
 
Point p  = pointsByEdge[c.ConnectionEdge][c.ConnectionIndex];

Making connections between activities

Of course, all this makes no sense if these connections somehow eventually don’t show up in tree of activities to execute.

To manage this tree you have these methods to override in the FreeFormActivityDesigner:

protected virtual void OnConnectorAdded (ConnectorEventArgs e)
protected internal virtual void OnConnectorChanged (ConnectorEventArgs e)
protected virtual void OnConnectorAdded (ConnectorEventArgs e)
protected internal virtual void OnContainedDesignersConnected (
    ConnectionPoint source,
    ConnectionPoint target
)

And this single method in the ActivityDesigner:

protected virtual void OnConnected (
  ConnectionPoint source,
  ConnectionPoint target
)

What you do inside these methods is of course dependent on the type of CompositeActivity you are designing. But generaly you will somehow “connect”/”disconnect” the two activities uner design. How you do this is entirely up to you.

Downloads

In the accompening code download you will find some sample code mostly demonstrating making connection.

Links

FreeformActivityDesigner and Connectors question…
Designer wish list?
FlowChartworkflow – Custom FreeformActivityDesigner sample

Exploring Workflow Foundation Part 6.2: Custom Designers – The ActivityDesigner class and customization with drawing

While starting to experiment with the FreeFormActivityDesigner (about which the following post will handle), I did some further exploration of the ActivityDesigner base class and what can be done with custom drawing. That will be the subject of this post.

Overriding the OnPaint event

This one is rather simple and complex at the same time.

Overriding the OnPaint method gives you with the argument the chance to implement your own custom drawing functionality. There is however one caveat: the coordinates for positioning drawing elements on the screen are logical coordinates. Thus, the upper^left corner of your designer is not at coordinates (0,0) but at (ActivityDesigner.Bounds.X,ActivityDesigner.Bounds.Y)

So, to draw a black circle for your activity, you use following code:

protected override void OnPaint(ActivityDesignerPaintEventArgs e)
{
  e.Graphics.FillEllipse(new SolidBrush(Color.Black), Bounds);
}

Now, isn’t this simple? Yes it is.

Then where is the complexity? If you are knowledgeable with the Graphics object I suppose there is no complexity. But if you’re not, you have another subject to study.

Recap: Designer – Activity interaction

In the previous post I allready showed you how to get at the activity associated with the designer. Afterall: the “raison de vivre” of the ActivityDesigner is the Activity it represents.

When setting properties of the Activity, the designer framewotk also triggers a redraw of the associated ActivityDesigner and thus calls the OnPaint method. This allows you to update the visuals of your designer using the properties of the activity.

Glyphs: why do we need them

When starting to experiment with this custom drawing I also started wondering why one would need custom glyphs. The reason however is very simple: the Graphics object you receive in the OnPaint method only allows you to draw inside the rectangular Bounds defined box of the ActivityDesigner. Glyphs however allow you to draw outside of this box.

An application of this could be if you for example want to render connection ports on the boundary of your custom ActivityDesigner.

Glyphs are associated with an ActivityDesigner and can draw relative to this associated designer. You can see this by the signature of the methods:

public override System.Drawing.Rectangle GetBounds(ActivityDesigner designer, bool activated)
protected override void OnPaint(System.Drawing.Graphics graphics, 
  bool activated, 
  AmbientTheme ambientTheme, 
  ActivityDesigner designer)

As you can see from these signatures, they all have an ActivityDesigner parameter passed in. This is the designer associated with the glyph. It allows you to get it’s bounds and position your glyph relative to it.

Downloads

In the associated code you can find sample code for the above explanations. Hope you enjoy it.

Exploring Workflow Foundation Part 6.1: Custom Designers – The ActivityDesigner class and customization without drawing

After having written your own custom activity which has some unique features, you will definitly want to write your own designer for it. There are however a lot of base classes to choose from depending on the type of custom activity you created.

For a simple activity however you will probably choose the standard ActivityDesigner class. Other classes at your disposal are the SequentialActivityDesigner and ParallelActivityDesigner. And if you really want to freak out you can choose the FreeformActivityDesigner. These however will be for later posts.

I will start with the simplest of all: the plain ActivityDesigner.

But first, in a GUI world where theming support is “the feature” to support: here are…

Designer Themes

Designer themes allow you to set a set of standard properties that will be used by the designers of your workflow. Or as the MSDN states it:

Designer themes define the look and feel of a designer. This includes any background styles, foreground styles, fonts, and icons defined for the designer.

In short: they provide, as all themes, a convenient way of having a common look and feel for your designers.

To allow you to customize some of the settings a few properties are assignable. Those can be set in the constructor to allow you to provide your own values. Most of them have to do with colors:

  • BackColorEnd
  • BackColorStart
  • BorderColor

There are also a lot of other properties which are readonly and thus are not customizable by setting them in the constructor.

Defining a theme is thus as simple as setting a few properties:

public class MyActivityTheme : ActivityDesignerTheme
{
    public MyActivityTheme(WorkflowTheme theme)
        : base(theme)
    {
        base.Initialize();
        this.BackColorStart = Color.FromArgb(79, 63, 255);
        this.BackColorEnd = Color.FromArgb(55, 202, 255);
        this.BorderColor = Color.FromArgb(255, 63, 204);
 
    }
}

During development I noticed that changing the designer theme of a designer doesn’t have any visual effect. I tought I was doing something wrong untill I came across this post:
ActivityDesignerTheme and VS restart

I’m using Visual Studio 2008 and I still have this issue: apparently they didn’t look at it like they said they would … but the solution provided in the last third post does work:

Right click the activity in the designer view: the popup menu that appears has an item called “Select Custom Theme …”
insert picture SelectCustomeTheme
Click it and a dialog window will open. Click the Workflow Designers > Themes on the left side and at the right side select the “Operating System Color Theme”.
insert picture SelectCustomeThemeDialog
Repeat the same but now select the “Default Theme” et voila: your view should be updated. And now it’s waiting for Microsoft to solve the issue…

Custom Designers without drawing

Applying a theme to a designer is simple: you set the ActivityDesignerThemeAttribute to the designer class:

[ActivityDesignerTheme(typeof(MyActivityTheme))]
public class ThemedActivityDesigner : ActivityDesigner
{
}

Customization of your designer can be simple too. The ActivityDesigner class hase some properties which you can set in the constructor of your inheriting designer class or override in its implementation and which customize some of the visuals of your designer without the need for implementing your own drawing code.

If you look at a standard simple activity like the DelayActivity you can differentiate three basic areas on the designer surface:

  1. The Designer area: this is the complete area taken by the Designer in the Workflow designer
  2. The Image area: this area shows an image, mostly the designer icon from the toolbox, as a visual presentation of the type of designer
  3. The Text area: this area shows the text associated with the designer, which is mostly the name of the activity instance it represents

These areas and there content can be manipulated by following properties:

  • ImageRectangle
  • Image
  • TextRectangle
  • Text
  • MinimumSize
  • Size

Applying some of this knowledge in code gives us:

[ActivityDesignerTheme(typeof(MyActivityTheme))]
public class ThemedActivityDesigner : ActivityDesigner
{
    private static Size TextSize = new Size(100, 50);
 
    protected override void Initialize(System.Workflow.ComponentModel.Activity activity)
    {
        base.Initialize(activity);
        this.Text = base.Text + " by HungryForKnowledge";
        Assembly a = Assembly.GetExecutingAssembly();
        Stream imgStream = 
            a.GetManifestResourceStream("HFK.CustomDesigners.DesignerImage.bmp");
        this.Image = new Bitmap(imgStream);
    }
 
    protected override Rectangle TextRectangle
    {
        get
        {
            return new Rectangle(this.Bounds.Right - TextSize.Width,
                this.Bounds.Top + (this.Bounds.Height - TextSize.Height) / 2, 
                TextSize.Width, TextSize.Height);
        }
    }
 
    public override Size MinimumSize
    {
        get
        {
            return new Size(200, 100);
        }
    }
}

Which results in this view in the workflow designer of visual studio:

There is an important thing to notice here:

In the code you can see how I use the Bounds property for calculating the text rectangle. This is because the coordinates of the rectangle are not interpreted by the workflow designer as being releative to the avtivity designers surface. So if you specify a rectangle of Rectangle(0, 0, 10, 10), then the origin is not the left top of your designers surface, and the text will not be visible. The top left corner of your surface starts at the upper left corner of your designers Bounds property, and its with and height are those of the designers Bounds property. Thus specifying a 10 by 10 rectangle at the left top of your design surface requires following code:

new Rectangle(this.Bounds.Left, this.Bounds.Top,
                    10, 10)

Custom Designers and executing actions

If you have a custom designer for your activity you will likely want to peform actions with your designer that ultimately influence some property or behaviour of the activity associated with your designer.

You have two opportunities to execute actions:

  1. Execute actions in response to errors in the configuration of the custom activity
  2. Execute actions which somehow do something

The first type of actions are customizable through the DesignerActions property of the ActivityDesigner class. They result in an error glyph being shown which you can expand to show the available actions.

The second type of actions are configurable through the Verbs property and result in inserting entries in the popup menu shown by right clicking the designer surface.

Adding them is simple:

For the DesignerActions you override the DesignerActions property of the ActivityDEsigner class and return a collection filled with objects of type DesignerAction. Be carefull however: returning a non empty collection automatically adds the ConfigErrorGlyph (the red circle with an exclamation mark) to your activity designer. Remember, designer actions are used to signal and correct configuration errors. So you should only return a non-empty list if the activity associated with your designer has such errors.

In code:

protected override ReadOnlyCollection<DesignerAction> DesignerActions
{
    get
    {
        List<DesignerAction> DesignerActionList = 
            new List<DesignerAction>();
 
        DesignerActionList.Add(
            new DesignerAction(this, 1, "There is an error!"));
        return DesignerActionList.AsReadOnly();
    }
}
 
protected override void OnExecuteDesignerAction(
    DesignerAction designerAction)
{
    if (designerAction.ActionId == 1)
        MessageBox.Show(designerAction.Text);
}

Adding custom Verbs is equally simple: Override the Verbs poperty and return a collection of objects of type ActivityDesignerVerb to which you attached the eventhandler to handle the specified command.

In Code:

protected override ActivityDesignerVerbCollection Verbs
{
    get
    {
        ActivityDesignerVerbCollection newVerbs = 
            new ActivityDesignerVerbCollection();
        newVerbs.AddRange(base.Verbs);
        newVerbs.Add(new ActivityDesignerVerb(this, 
            DesignerVerbGroup.General, 
            "Custom Action", 
            new EventHandler(CustomContextMenuEvent)));
        return newVerbs;
    }
}
 
private void CustomContextMenuEvent(object sender, EventArgs e)
{
    MessageBox.Show("Perform some custom action");
}

Custom Designers and Glyphs

Glyphs allow you to somehow extend the visual presentation of your designer in a crosscutting-kind-of-manner. You can design a glyph and apply it to any activity designer that could use it. As such they are ideal for visually expressing conditions that are cross-cutting to activities.

An example is the standard provided ConfigErrorGlyph glyph. It draws a red circle around a white exclamation mark, indicating the activity is badly configured. Because most activities have properties that must have some correct value, this is ideal to be expressed by a glyph. The alternative would be for each activities designer to implement its own drawingcode for indicating the error and thus very likeley each activity designer having its own way of visualizing the error condition.

Microsoft provides you with some standard glyph implementations you can use:

  • SelectionGlyph
  • CommentGlyph
  • ConfigErrorGlyph
  • LockedActivityGlyph
  • ReadOnlyActivityGlyph
  • ShadowGlyph

As you can see from tehre names they most of them signal states which can be applied to various activities.

The ActivityDesigner class has a Glyph property which you can override and allows you to attach some glyphs to your designer class.

In Code:

protected override ActivityDesignerGlyphCollection Glyphs 
{
    get
    {
        ActivityDesignerGlyphCollection glyphs = 
            new ActivityDesignerGlyphCollection();
        glyphs.AddRange(base.Glyphs);
        glyphs.Add(new ShadowGlyph());
 
        return glyphs;
    }
}

There is however a caveat with this property: the workflow designer uses this property to know which glyphs to show and if you override it without calling the base-class version you will lose the automtic addition of glyphs, like the ConfigErrorGlyph when returning a non-empty collection from the DesignerActions property.

Designer – Activity interaction

Of course, if you have a designer for your activity, you will probably want to do something with your activity in your designer. For this, the designer has an Activity property which gives you access to the activity being designed.

Downloads

Code

Links

How to Write a Designer Component for Custom Workflow Activity
Adding SmartTag Verbs to an Activity Designer
Windows workflow foundation animated activity designer source

Exploring Workflow Foundation Part 5: Custom activity validators

For completeness I can not pass the activity validation, so this is what this post will be about. However, as in the first part of this series I will make it rather easy on myself and provide some links to other posts which describe the things involved quite well.

Adding validation to your activity

The basic things involved in adding validation to your custom activities is again descibed in Keyvan Nayyeri excellent series: How to Write a Validator for Custom Workflow Activity. Morgan Skinner goes a little further in his post Activity Validation describing how to connect a validation error of a property to the property, and to the property window in the designer.

Your options for validation

To validate activities you actually have two possibilities:

  1. Use a custom validator
  2. Use the ValidationOptionAttribute attribute

The first option is described in the blog posts mentioned above. How and when to use the attribute is described on The Problem Solver blog in the post Validating workflow activity metadata

Use case: validating the type of your parent activity

In the provided sample code you can find an example on how to validate that the parent activity of an activity is of a certain type. Hope you enjoy it.

Downloads

The Code

Links

How to Write a Validator for Custom Workflow Activity
Activity Validation
Validating custom activities in the Workflow designer
Validating workflow activity metadata

Exploring Workflow Foundation Part 4: Custom Composite Activities

In the previous posts I introduced custom activities and how they are executed inside a workflow. The samples given where simple activities with no child activities.

In this post I will investigate custom composite activities and what your options are on executing their child activities.

Execution Semantics

The out of the box implementation of Windows Workflow Foundation supports two types of execution semantics: the Sequential workflow and the Statemachine workflow.

Again, I will not give a detailed overview of what the difference is between the two, becaused a lot of people have done an excellent job before me:

There are of course other modes of execution possible, which have other execution semantics. Here again, some people have done a great job on showing how to do this:

Once again: ActivityExecutionContext

If you studdy the above examples you will see that the ActivityExecutionContext is a very important class during the execution of workflows. In my first post, I allready mentioned the articles ActivityExecutionContext in Workflows and Advanced Workflow: Enabling Tricky Scenarios which explains more about the meaning of this class.

The main reason in composite activities for executing a child activity in its own execution context is when you want to execute the child activity more then once. As you know an activity goes through several states and eventually ends in the closed state out of which it can not be revived. If you want to execute your child activity more then once, you shouls somehow be able to create a clone of it and executte this clone. This is exactly what happens when you create a new execution context for an activity.

The general pattern for executing an activity in a new execution context is as follows:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
  // Your stuff ...
  
  newContext = executionContext.ExecutionContextManager.CreateExecutionContext(customActivityOne);
  newContext.ExecuteActivity(newContext.Activity);
  
  // More of your stuff ...
  
}

When creating the new execution context a clone of the provided activity is made, after which you can execute this clone in the newly created context. The original activity remains in it’s original state.

This all looks pretty simple, however, when starting to experiment myself with execution contexts and custom composite activities there where some issues in using execution contexts into which I ran and crippeled my code. Following I will provide some cave-at’s to look out for.

Always close the door behind you

I admit I’ve been cheating: the above code pattern is actually not really complete. If you create a new execution context for an activity, then you must eventually also close this execution context when the activity is executed, that is, the activity enters the closed state. You can do this by registering your composite activity, which acts as the parent of the executed child activity, to be notified of state changes of the child:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
  // Your stuff ...

  // Get notified of your child activity entering the closed state ...
  customActivityOne.RegisterForStatusChange(Activity.ClosedEvent, this);
  
  newContext = executionContext.ExecutionContextManager.CreateExecutionContext(customActivityOne);
  newContext.ExecuteActivity(newContext.Activity);
  
  // More of your stuff ...
  
  return ActivityExecutionStatus.Executing;
  
}

Then you also define a method which will get called when the child enters the closed state. Inside this method you can then close the created execution context:

void 
IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent(
  object sender, 
  ActivityExecutionStatusChangedEventArgs e)
{
  ActivityExecutionContext context = sender as ActivityExecutionContext;
 
  // remove notification because the child is no in the closed state
  e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
 
  // Close the door behind you: complete the execution context
  context.ExecutionContextManager.CompleteExecutionContext(
    context.ExecutionContextManager.GetExecutionContext(e.Activity));
 
  context.CloseActivity();
}

Never slam the door into someones face

Allthough the above code is complete, there is however still a cave-at which requires your attention: a composite activity must never move to the closed state when its child activities must still be executed or are still executing.

You can see this by the return code of our composite activity’s Execute method:

  return ActivityExecutionStatus.Executing;

Close ALL doors

But if you can not return ActivityExecutionStatus.Executing from the Execute method, then how can you make you composite activity move to the closed state. The answer is in the method called when the child activity moves to the closed state. At the end of this method there is the following line:

  context.CloseActivity();

The context given to the registered method is NOT the execution context of the child activity, but is the context into which the composite activity is executed. So by calling the CloseActivity method of this parameter, we actually close our composite activity.

Cloning a closed activity: it maybe a copy, but it’s parents are not your parents…

As mentioned above creating a new execution context for an activity actually creates a clone of the activity. If the cloned activity was not closed then the clone is a complete copy of the cloned activity. This means that if your composite activity registered for notification it will also be notified of changes in the cloned activity.

However, if your cloned activity was in the closed state, cloning the activity resets several dependency properties and because registering for notification happens via a dependency property, you lose the notification. This scenario happens when you want to execute the child activity more then once and you clone the allready executed child. This starts a series of events which finaly result in your workflow ending in the ideled state:

  1. You clone the allready executed child and lose the notification registration
  2. If the child eventually moves to the closed state, your composite activity is not notified
  3. Your workflow runs out of things to do
  4. Allthough your workflow ruuns out of things to do, it doesn’t close because the composite activitiy’s execution context ClosActivity() method is never called. Remember this got called during notification ot the child activity moving to the closed state. So, no notification, no calling of this method and no closing of the composite activity
  5. The workflow runtime sees that your workflow hasn’t got anything to do, but not all its activities have closed. The result is that your workflow is idled and can never finish.

The solution is to reregister for notification but on the clone itself and not on the cloned activity.

Thus, you get following code:

  newContext = executionContextManager.CreateExecutionContext(activityToClone);
 
  // Because creating a new ActivityExecutionContext from a closed activity resets
  //  all known dependency properties, we must reregister for the ClosedEvent
  //  (see also the activity CustomCompositeActivityWrongAttempt)
  //  This reregistering must also happen on th eclone, so we use the 
  //  newContext.Activity property which is the activity that will be executed
  //  in the new context and thus the clone
  newContext.Activity.RegisterForStatusChange(Activity.ClosedEvent, this);
   newContext.ExecuteActivity(newContext.Activity);

Downloads

Following is some sample code demonstarting the various scenario’s described above, including some eronious code to demonstrate what happens if you do things wrong:

The code

Links

ActivityExecutionContext
ActivityExecutionContext in Workflows
Advanced Workflow: Enabling Tricky Scenarios

Custom composite activities
Windows Workflow Foundation: Creating a Custom Composite Activity
Creating workflows with custom execution semantics
Custom Activity Designer

Various
Handling Faults in Windows Workflow Custom Activities

Exploring Workflow Foundation Part 3: Running your Custom Activity

In this post I will explore what exactly happens when you’ve added your custom activity to workflow and you execute this workflow.

Loading and executing your custom activity
gt;>>
Activity item = activity.Clone();
Queue queue = new Queue();
queue.Enqueue(item);
w
The code needed to load and execute you workflow is quite simple:

// Instanciate the workflow runtime
WorkflowRuntime workflowRuntime = new WorkflowRuntime();
 
//Create a handle to signal the ending og the workflow
AutoResetEvent waitHandle = new AutoResetEvent(false);
 
// Attach ecventhandlers that signal the workflow ended
workflowRuntime.WorkflowCompleted += 
    delegate(object sender, WorkflowCompletedEventArgs e)
{
    Console.WriteLine("Workflow completed");
    waitHandle.Set();
};
workflowRuntime.WorkflowTerminated += 
    delegate(object sender, WorkflowTerminatedEventArgs e)
{
    Console.WriteLine(e.Exception.Message);
    waitHandle.Set();
};
 
// Make a dictionary with the parameters for the activities
//  inside the workflow
Dictionary<String, Object> arguments = new Dictionary<string, object>();
arguments.Add("VariableName", "VariableValue");
 
// Create the workflow ...
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
                    typeof(WorkflowConsoleApplication.MyWorkflow), arguments);
// ... and execute the workflow
instance.Start();
 
// Wait untill the workflow ended
waitHandle.WaitOne();

The actual core code in the above are following three lines:

WorkflowRuntime workflowRuntime = new WorkflowRuntime();
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
                    typeof(WorkflowConsoleApplication.MyWorkflow), arguments);
instance.Start();

Now, lets see what happens here…

WorkflowRuntime.CreateWorkflow

Following are the steps executed during creation of the workflow:

  • Step 1: Start the runtime if not allready started by:
    • Adding all registered services and some default services needed by the runtime
    • Call the Start method of all these services
    • Call the OnStarted method of all these services
  • Step 2: An internal WorkflowExecutor object is created which will be responsible for executing your workflow.
  • Step 3: A WorkflowInstance object is created
  • Step 4: The WorkflowExecutor object and the WorkflowInstance object are associated with each other
    • Step 4.1: Two instances of the root Activity of your workflow are created, one of which will be effectively executed and the other serving as a template. For this, the latter one is associated with the WorkflowDefinitionProperty of the former.
    • Step 4.2: Hook up more properties and dependency properties
    • Step 4.3: Create a Scheduler object which holds the queue of work to be performed
    • Step 4.4: Create a WorkflowQueuingService
    • Step 4.5: Create a TimerEventsubscriptionCollection
  • Step 5: Call the Initialize method of the root activity in the workflow.
  • Step 6: Return the WorkflowInstance object

Next, we start out WorkflowInstance…

WorkflowInstance.Start

Following are the steps executed during the start method:

  • Step 1: Get the appropriate WorkflowExecutor object for the WorkflowInstance
  • Step 2: Call the WorkflowExecutor’s Start method
    • Step 2.1: Create an ActivityExecutionContext for the WorkflowExecutor’s root activity, which at this point is our workflows root activity
    • Step 2.2: Call the context’s ExecuteActivity method with the same root activity
      • Step 2.2.1: Check if the activity is in the Initilazed state and throw an exception if not
      • Step 2.2.2: Put the activity in the Executing state by calling it’s SetStatus method
      • Step 2.2.3: Schedule the activity for execution which wraps the activity in an ActivityExecutorOperation object and adds this object to the Scheduler queue of activities to execute
      • Step 2.2.4: Report the scheduling back up to the runtime so it can trigger the appropriate events, like WorkflowCompleted, WorkflowTerminated and other.

Handling the queued activities

In the above we analized how activities are added to the work queue, but we didn’t see anyone taking activities from this queue and actually performing the work in the activity.

The act of executing the activites is actually split in two seperate loops:

A first loop is created by the OnStarted method of the DefaultWorkflowSchedulerService. Remember this method was called during creation of our workflow, see Step 1 in WorkflowRuntime.CreateWorkflow. For this, the DefaultWorkflowSchedulerService service requests a thread from the ThreadPool and hands it a method to execute. In this method following steps are executed:

  • Step 1: Get a workitem from the queue of items to perform
  • Step 2: Perform the work

As you can see, nothing much happening here.

The filling of this queue with workitems actually happens during the Start method of the WorkflowExecutor. Steps 2.1 and 2.2 of topic WorkflowInstance.Start, which explain what happens inside the WorkflowExecutor.Start method, are actually wrapped inside a C# using construct.

As every C# developper knows, a using construct wraps the creation of an object and it’s disposal into a code-block:

using (new SomeClass)
{
    // More code to execute...
 
} // Dispose method of class SomeClass calles here

Inside the WorkflowExecutor’s Start method, a similar construct with a class named ScheduleWork is made and the Dispose method of this class contains the code that activates the loop resulting in the effective execution of the activities:

  • Step 1: Get an ActivityExecutorOperation to run from the Scheduler
  • Step 2: Run the ActivityExecutorOperation
  • Step 2.1: Get the activity for the ActivityExecutorOperation
  • Step 2.2: If the type of ActivityExecutorOperation is Execute, call the activity’s Execute method

Downloads

Following is some sample code for executing activities:

The code

Links

WorkflowInstance.GetWorkflowDefinition