Welcome to GotSpeech.NET Sign in | Join | Help

Marshall Harrison - "the gotspeech guy"

Site news, Speech Server insight and assorted ramblings
Creating subflows

Recently I was working on setting up an application where I wanted different pieces of the call flow to be placed in separate work flows or at least segmented out so that multiple developers can work on the different pieces. The desire was to do this in a way that I could easily add the different pieces into the main work flow. Think of this as having the main menu in the main work flow then having components and sub menus setting off in a separate work flows that I could drag and drop into the main workflow as needed. In order for this to work I had two requirements:

  1. Each subflow needs to be able to see and set values in the main work flow
  2. The main work flow needs to be able to see and set values in the subflows

This post is going to show you how I accomplished this. First though I want to note that there are probably other ways of doing this and you may have hit on a better way of accomplishing this task. Also note that the term "subflow" is not terminology you will find in Microsoft documentation.

The first thing I did was create a new speech workflow application and dragged a few  activities into the designer. There is nothing special here and it is something that you have done before.

Next you right click the project and chose Add/New Item fro the popup menu. This opens the new item window and presents a list of templates. Highlight "Voice Response Sequence Activity" and set the name for your subflow then click Add. Visual studio will create a new partial class and add it to your project. 

After building  your project you should see an new item in the toolbox that has a purple sprocket or cog for an icon that is named the same as the new class you created. This can be added to your workflow by dragging and dropping it into the designer like you drop any other component.

By default the component shows in your workflow as just a container - in other words in the main workflow you want be able to see any of the activities in your subflow -

But if you look closely at the first code example shown below you will see a line that starts with "[Designer (typeof(System.Workflow........]". All you have to do is comment out that line and your subflow will now show it's activities (you must rebuild your app first). You should also notice either a "+" or a "-" next to the name of the sublfow. You can click on this to show or hide the activities in the subflow.

Now that we have the necessary pieces we need to establish a way that they can communicate with each other. This is not as simple as you may think because if you simply create public variables with setter/getter logic you will find that variables are not getting set property. You will encounter problems with scope of the objects and the timing of when they are created as the work flow engine creates/clones your subflows. To make this work you will need to create the public variables and a static InstanceDependencyProperty so that they can accessed when your subflow isn't running. It's really very simple as this code example shows.

Note that in the code above we also declared a private variable called parentWorkflow. Once the subflow starts you need to set it so that it points to the main workflow like I show below.

 

If you don't so this then you won't be able to reference variables (including the call related stuff) in the main workflow. Once you have done this you have full use of IntelliSense for variables residing in the main work flow. Note - I realize there is a syntax error in the code shown below but I didn't want to upload another image and it doesn't affect what I'm trying to demonstrate.

To access subflow variables from the main workflow you simply reference the public variable you declared in the subflow. The magic behind the InstanceDependencyProperty that you created above makes this all work.

That's all there is to it and as I said earlier there may be other ways of doing this but this is what I settled on and it works for me.

I would love to hear how you approach this process and if you have any improevements then let me know.

Posted: Monday, January 14, 2008 5:18 PM by marshallharrison

Comments

kstep said:

Hello Marshall,

This was a great post.

Just to add to it, there is a quick way to create properties for Custom Activities or as you described them, sub-workflows that is a bit easier than trying to remember all the syntax that surrounds them by using a pre-loaded snippet.  The only difference though is that it uses a DependencyProperty class rather than an InstanceDependencyProperty class.  I have used this in my code and it works well.

1. Right-click on the code editor and choose Insert Snippet. You will see in the list one that is labeled Workflow.

2. Double-Click on "Workflow" and you will see "Dependency Property - Property".

3. Double-Click on "Dependency Property - Property" and it will insert the stub code necessary for a Sub-Workflow or Custom activity property.

4. You can now fill in the necessary code items by tabbing through the different parts of the snippet.

# January 14, 2008 10:55 PM

marshallharrison said:

Great I figured there were other ways. I don't use snippets much as I use CodeRush with Refactor (http://www.devexpress.com/Products/NET/IDETools/CodeRush/)

# January 14, 2008 11:23 PM

marshallharrison said:

I tried your method but I didn't have Workflow as an option.

# January 14, 2008 11:35 PM

kstep said:

Interesting that you were not able to see the Snippets for Workflow.  There are only two. But by the same token , they can be created quite easily and used that way as well.

Never heard of CodeRush. I will look into it.

# January 15, 2008 9:01 AM

Anthony said:

Do you need to cache parentWorkflow into a private member?  Can't you just access ((Workflow1)this.Workflow) from within statementActivity2_TurnStarting?  Is the reason for caching just to simplify the syntax a bit?  If so, I'd be inclined to use a property instead, as below, so that it's a method in the vtable (which will probably get inlined) rather than a pointer on every instance.

private Workflow1 ParentWorkflow

{

   get

   {

       return (Workflow1)this.Workflow;

   }

}

# March 11, 2008 12:00 AM

Marshall Harrison - "the gotspeech guy" said:

Today (5/10) is the last day of the 18th week of 2008. Of those 18 weeks I've spent 10 of them on the

# May 10, 2008 10:53 AM
Anonymous comments are disabled