One of WF’s usability problems in the .Net 4.0 release was the complexity of creating custom ActivityDesigner classes for working with custom activities that expose ActivityAction or ActivityFunc properties. (If you’re wondering what ActivityAction or ActivityFunc are, step this way.)

Although the WF team went to great effort to try to be helpful in documenting how to do this, the reality was still that implementing custom designers for activity delegates still would take a lot of time and patience. Generally you had to:

  1. Ensure the ActivityDelegate object exists at design time, so that you can bind a WorkflowItemPresenter to its Handler property, possibly by implementing a suitable IActivityTemplateFactory, or possibly by other tricks.
  2. Ensure the ActivityDelegate object’s argument objects also exist at design time, and expose their names in a way which is obvious to the workflow author, so that inside of the handler you can write expressions referencing them. IActivityTemplateFactory was helpful here too.
  3. Futz around in WPF exposing all the arguments and the handler using WorkflowItemPresenter.
  4. Futz around in WPF even more figuring out how to make it look pretty - doing things like making your designer support collapse/expand, customizing or localizing the hint text on the WorkflowItemPresenter, have good layout for all the arguments, and add any other custom UI that your designer is meant to have.
  5. Last, but not least, test and debug it, making sure that it actually lets you build a valid workflow, and you don’t have design time issues with undo/redo/cut/copy/paste/expand/collapse etc.

Now after two years of .Net 4.0, and learning all the nitty-gritty bits above, I was very surprised to find out that today that in .Net 4.5, doing a custom Activity Designer for an activity with a ActivityAction or ActivityFunc is suddenly incredibly easy!

Let’s see how:

public class DelegatingActivity : NativeActivity

{

    public DelegatingActivity()

    {

    }

 

    public ActivityAction<string> Delegate

    {

        get;

        set;

    }

 

    protected override void Execute(NativeActivityContext context)

    {

        context.ScheduleAction<string>(this.Delegate, "InjectingInArgumentValue");

    }

 

    protected override void CacheMetadata(NativeActivityMetadata metadata)

    {

        base.CacheMetadata(metadata);

    }

}

 


And we’re done!

“Wait Tim, what do you mean done? You didn’t define a custom designer anywhere?!”

That’s right! It’s no longer necessary, because the standard built in ActivityDesigner class (which is the default activity designer for all subclasses of type ‘Activity’) has some new functionality!

In ‘before and after’ style, here is the default ActivityDesigner rendition of the above activity in .Net 4.0, compared with .Net 4.5.

image

Magic!

Notice that by default you don’t see the arguments for the delegate. But that’s OK! Arguments are not actually necessary until your delegate has a Handler, i.e. an activity inside to do some work.

As soon as we drag+drop an activity (in this case WriteLine) into the delegate’s handler WIP voila – delegate arguments appear!

image

And once the argument is there we can go on and do the other stuff we might regularly do such as renaming the DelegateInArgument, and using it in expressions inside of the delegate’s Handler. Like so:

image

Is any extra step needed to turn on all of this good stuff? As far as I can tell, nope, it’s really nothing except writing the activity class correctly.

More questions -

Does it support multiple delegates? Yes!

image

Does it support DelegateOutArguments and ActivityFuncs? Yes!

image

Does it support dynamic collections of ActivityDelegates? Don’t know (note to self, find out).

Is it possible to create a custom designer that inherits all of this functionality, and augments it or customizes it? Don’t know, but I intend to find out at some point.

So that’s all for this post, I hope you’ll find this post cool and useful next time you are working with activity delegates and .Net 4.5.