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

Advertisements

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