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

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

  1. Hi,

    Thanks for very good article. While I understand what you are explaining, I’m still not sure where to start. I downloaded the re-hosting sample from Microsoft and while it’s very good to explain the basics, it really lacks in terms of customizing the designer. I’ve spent quite a few hours now trying to figure out how to get rid of the grey background surrounding the actual workflow designer where you drag & drop activities. You would be kind enough showing me how to do this? Once I have a basic sample, I’m sure I’ll take off but right now I’m just not sure as to where to start to achieve this.

    The workflow designer that I’ll be using will only be using sequential workflows but from your sample it looks like I have to create a new class and this is where I’m confused when looking at the MS Re-hosting sample as I cannot see where they are doing this in their sample.

    Can you please shed some light as I’m seriously confused and if you have a basic example that shows me how to change the designer rather than an activity, I’d really appreciated it.

    Regards,

    Thierry

    1. Hi Thierry,

      I’m not sure I understand what toy mean with “the grey background surrounding the actual workflow designer where you drag & drop activities”. I suppose you mean the grey border between the sequential workflow designer and the edge of the designer tab-window if used in visual studio.

      This grey border is probably not part of the sequential workflow designer but of the application with which you design workflows.

      While writing this answer I suddenly understood your confusion. The therm “designer” is, as with many therms in IT, overloaded. You have to make a difference between the application with which you want to design workflows, and the workflow being designed. What I have described here is about modifying the visual aspects of a workflow while it is being designed. What you want to do is change the visual aspects of the application with which you want to design workflows. What you are trying to accomplish, if at all possible, will indeed probably need designer rehosting.

      Unfortunately, I have not yet experimented with designer re-hosting.

      Hope this shed the expected light on what you are trying to accomplish.

      Regards,

      Serge

  2. Could I ask you a question, please? I couldn’t find an answer for it anywhere.
    How can I inherit the class SequenceDesigner and using it get the same result as using the original SequenceDesigner?

    1. Alex,

      I don’t understand what you want. Why would you inherit the SequenceDesigner to get the same result as the original?

      Could you be a bit more specific?

      Regards,

      Serge

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s