Monday, November 19, 2007

Developing custom state activities

I've banged on before about how extensible Windows Workflow is (assuming you can work out how to extend it), but there is at least one place where it isn't quite as extensible as one would like, state activities.

I was trying to create a custom state activity that contained a state initialization activity and an event driven activity and various other activities within those. The first problem I encountered was when I inherited from StateActivity. Trying to add any activities at design-time failed. This seems to be down to the state activity designer. One solution would have been to write my own designer class, but this is complicated by the fact that the standard state designer is internal so can't be inherited from. I guess I could have decompiled the code using Reflector and made the required mods in my own code, but that seemed too much like hard work. So I decided to construct the activity in code instead.

Then I bumped into the second problem. Adding activities in code is pretty straightforward, just do it in the constructor after the call to InitializeComponent, like so.

public UserActivity()
{
  InitializeComponent();
  CanModifyActivities = true;
  try
  {
    // add activities here
  }
  finally
  {
    CanModifyActivities = false;
  }
}

Having said that, if you're doing all your activity construction in code, you can actually get rid of the call to InitializeComponent and get rid of the designer.cs file, since you're not going to need any design-time support.

Anyway that's the theory. In practice, it doesn't quite work out like this, because the StateActivity's validator doesn't like you to add child activities to a state activity. At least this class is public so I could inherit from it, but there aren't really any extensibility points to allow for modification of its behaviour. So I initially wrote my own validator class, that doesn't do anything at all. I later realised that if the validator wasn't going to do anything, I could actually just use ActivityValidator instead. This isn't perfect since it means none of the child activities I've added will be validated, but it works well enough for my scenario.

One further problem (although it isn't an issue for me) is that an end user of your custom state activity won't be able to add their own child activities. I guess the solution to this once again is to write your own designer.

Another related problem is the addition of a set state activity. Since my users can't add one themselves, I have to add one but I don't know the name of the state to transition to until the activity is in use. This was pretty easy to solve, just add a TargetStateName to my activity and pass that value through to my own set state activity.

public string TargetStateName
{
  get
  {
    return setState.TargetStateName;
  }
  set
  {
    setState.TargetStateName = value;
  }
}

My final problem was that the state activity was displayed with all of its child activities visible to the end user, although they are not editable. I still haven't solved this issue, although I suspect the solution will yet again involve writing my own designer class. If only that standard state designer wasn't internal...

5 comments:

Anonymous said...

This is a very good article.

What I'm trying to do is to group several state activities into one custom state activity. At the same time, I want each state activity still can handle events raised from the host.

Where do you think I should start from?

Thanks!

Doogal Bell said...

That's an interesting question and as far as I'm aware you can't add child state activities to a state activity. Do they have to be state activities or you could you have some kind of enumerated type property on your custom state activity that describes what state it's in?

dexter04 said...

Hi bell, it seems that your custom state activity catch my attention. Im really trying to create a custom eventdriven activity that i can put into certain states during runtime. The problem is almost the same as yours. I cannot add handle events and set state event to an eventdriven activity. And also setting up the targetstate as property so that the user can set it during runtime. Can you share your code so that I can refer to it in doing this near impossible application? Thanks in advance!!!!

Doogal Bell said...

Hi Dexter

Send me an email and I'll email the code to you. It may not compile because it uses some Sharepoint stuff but it should give you an idea of what to do.

My email is doogal at doogal dot co dot uk

Anonymous said...

Doogal,

I know it's been over a year - but if you still have the code would you kindly email it to me at sp at filestar dot eu

Thanks in advance,
Sharad