Types, Metatypes and Bears, Redux

I made a quick post a few months back where I tried to talk about the way the designer works and lets us design types, as well as simply configure instances of types.

There were a couple of key points that I wanted to make there in that post:

  • The workflow designer can configure instances of activity graphs, and create entire types as well
  • Types are designed by editing an instance of a type that represents the type being designed, the metatype
  • There is some XAML trickery required to serialize and deserialize this
  • This same type of work is done to enable the DynamicActivity capability

A few folks have noticed (and sent me mail), that things look a little different in Beta2.  While we are going to have a more thorough, “here’s everything that changed” doc, I want to go ahead and update at least some of the things that I’ve been talking about here.

What’s New

In reality, very little is new, we’ve primarily moved stuff around now.  One thing that you may remember is that the DesignTimeXamlReader was not public in beta1, and if you are looking around, you may not find it.  We have made this functionality public however.  Thus, see the “what’s changed" bit.

What’s Changed

We took a long look at things and realized we had a bunch of XAML stuff all over the place.  We felt it would be a good idea to try to consolidate that into one place in WF, so System.Activities.XamlIntegration.ActivityXamlServices becomes your one stop shop for most things Activity and XAML related.  Let’s take a quick look and see what’s in there:

ActivityXamlServices Members

[This documentation is for preview only, and is subject to change in later releases. Blank topics are included as placeholders.]

Creates an instance of an activity tree described in XAML.

The ActivityXamlServices type exposes the following members.

clip_image001[4] Methods





Overloaded. Maps an x:Class activity tree to an ActivityBuilder or ActivityBuilder<(Of <(TResult>)>).



Maps an ActivityBuilder or ActivityBuilder<(Of <(TResult>)>) from the specified writer to an x:Class activity tree.



Overloaded. Maps an x:Class activity tree to an DynamicActivity or DynamicActivity<(Of <(TResult>)>).



Overloaded. Creates an instance of a declarative workflow.



Load is used to generally take some XAML and return an Activity which you can then use to execute.  If Load encounters a XAML stream for <Activity x:Class, it will subsequently generate a DynamicActivity.  This functions basically the same way WorkflowXamlServices.Load() did in beta1. 

You also see CreateBuilderReader, and CreateBuilderWriter, which are used to surface the DesignTimeXaml capabilities that we used in beta1.  These will return an instance of a XamlReader/Writer that handles the transformation between the metatype and the <Activity x:Class XAML.   The metatype has changed names from ActivitySchemaType to ActivityBuilder.

The table below should help summarize the uses and changes between beta1 and beta2.  In this area, I don’t expect any changes between what you see now, and what you will see in RTM.




metatype (type to build types) ActivitySchemaType ActivityBuilder
Mechanism to load DynamicActivity WorkflowXamlServices.Load() ActivityXamlServices.Load()
Mechanism to load ActivityBuilder use WorkflowDesigner.Load() to get an ActivitySchemaType Use the reader from CreateBuilderReader() to pass into XamlServices.Load()
Mechanism to save ActivityBuilder to XAML Create a new DesignTimeXamlWriter, pass that to XamlServices.Save() Use the writer returned from CreateBuilderWriter() to pass into XamlServices.Save()

To explore this, use CreateBuilderReader() and XamlServices.Load() on a workflow that you’ve built in the designer and poke around a bit to see what’s going on.

Here is some sample code that walks through this:

    1:  ActivityBuilder ab1 = new ActivityBuilder();
    2:  ab1.Name = "helloWorld.Foo";
    3:  ab1.Properties.Add(new DynamicActivityProperty { Name = "input1", Type = typeof(InArgument<string>) });
    4:  ab1.Properties.Add(new DynamicActivityProperty { Name = "input2", Type = typeof(InArgument<string>) });
    5:  ab1.Properties.Add(new DynamicActivityProperty { Name = "output", Type = typeof(OutArgument<string>) });
    6:  ab1.Implementation = new Sequence
    7:  {
    8:      Activities =
    9:      {
   10:          new WriteLine { Text = "Getting Started " },
   11:          new Delay { Duration = TimeSpan.FromSeconds(4) },
   12:          new WriteLine { Text = new VisualBasicValue<string> { ExpressionText= "input1 + input2" }},
   13:          new Assign<string> { To = new VisualBasicReference<string> { ExpressionText = "output" },
   14:                       Value = new VisualBasicValue<string> {ExpressionText= "input1 + input2 + \"that's it folks\"" } }
   15:      }
   17:  };
   18:  StringBuilder sb = new StringBuilder();
   19:  StringWriter tw = new StringWriter(sb);
   20:  XamlWriter xw = ActivityXamlServices.CreateBuilderWriter(
   21:      new XamlXmlWriter(tw, new XamlSchemaContext()));
   22:  XamlServices.Save(xw , ab1);
   23:  string serializedAB = sb.ToString();
   25:  DynamicActivity da2 = ActivityXamlServices.Load(new StringReader(serializedAB)) as DynamicActivity;
   26:  var result = WorkflowInvoker.Invoke(da2, new Dictionary<string,object> { {"input1","hello"}, {"input2", "world" }});
   27:  Console.WriteLine("result text is {0}", result["output"]);
   30:  ActivityBuilder ab = XamlServices.Load(
   31:      ActivityXamlServices.CreateBuilderReader(
   32:          new XamlXmlReader(new StringReader(serializedAB)))) as ActivityBuilder;
   34:  Console.WriteLine("there are {0} arguments in the activity builder", ab.Properties.Count);
   35:  Console.WriteLine("Press enter to exit");
   36:  Console.ReadLine();


Good luck, and happy metatyping!