Introduction to WF Designer Rehosting (Part 2)

standard beta disclaimer. This is written against the beta1 API’s. If this is 2014, the bits will look different. When the bits update, I will make sure to have a new post that updates these (or points to SDK samples that do)

In yesterday’s post, we went over the core components of the designer.  Let’s now take that and build that rehosts the designer, and then we’ll circle back around and talk about what we did and what comes next.

Start VS, Create a new project, and select a WPF project

image

Inside the VS project add references to the System.Activities.* assemblies.  For now, that list looks like

  • System.Activities.dll
  • System.Activities.Design.Base.dll
  • System.Activities.Design.dll
  • System.Activities.Core.Design.dll

image

You might think the list of design assemblies is excessive.  We’ll be collapsing probably into two design assemblies, one with the designer infrastructure and one with the activity designers in subsequent milestones.

Create some layout in the WPF window to hold the various designer elements.  I usually do a three column grid for toolbox, property grid and designer canvas.

The XAML for this looks roughly like this:

 <Window x:Class="BlogPostRehosting.Window1"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="664" Width="831">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
    </Grid>
</Window>

Now that we’ve got the layout down, let’s get down to business.  First let’s just get an app that displays the workflow designer and then we will add some other interesting features. We wanted to make it easy to get a canvas onto your host application, and to program against it.  The key type that we use is WorkflowDesigner, this encapsulates all of the functionality, and operating context, required.  Let’s take a quick look at the type definition

 

Name Description
Context Gets or sets an EditingContext object that is a collection of services shared between all elements contained in the designer and used to interact between the host and the designer. Services are published and requested through the EditingContext.
ContextMenu Gets the context menu for this designer.
DebugManagerView Provides a DebuggerServicethat is used for runtime debugging.
PropertyGridFontAndColorData Sets the property grid font and color data.
PropertyInspectorView Returns a UI element that allows the user to view and edit properties of the workflow.
Text Gets or sets the XAML string representation of the workflow.
View Returns a UI element that allows the user to view and edit the workflow visually.

 

The editing context is where we will spend more time in the future, for now the View is probably what’s most interesting, as this is the primary designer canvas.  There are also some useful methods to load and persist the workflow as well.

Let’s start off real simple, and write some code that will display a basic sequence, and we’ll get more sophisticated as we go along.

    1:  using System.Windows;
    2:  using System.Windows.Controls;
    3:  using System.Activities.Design;
    4:  using System.Activities.Core.Design;
    5:  using System.Activities.Statements;
    6:   
    7:  namespace BlogPostRehosting
    8:  {
    9:      /// <summary>
   10:      /// Interaction logic for Window1.xaml
   11:      /// </summary>
   12:      public partial class Window1 : Window
   13:      {
   14:          public Window1()
   15:          {
   16:              InitializeComponent();
   17:              LoadWorkflowDesigner();
   18:          }
   19:   
   20:          private void LoadWorkflowDesigner()
   21:          {
   22:              WorkflowDesigner wd = new WorkflowDesigner();
   23:              (new DesignerMetadata()).Register();
   24:              wd.Load(new Sequence 
   25:                              { 
   26:                                  Activities = 
   27:                                  {
   28:                                      new Persist(), 
   29:                                      new WriteLine()
   30:                                  }
   31:                              });
   32:              Grid.SetColumn(wd.View, 1);
   33:              grid1.Children.Add(wd.View);
   34:          }
   35:      }
   36:  }

Let’s walk through this line by line:

  • Line 22, construct the workflow designer
  • Line 23, Call Register on the DesignerMetadata class.  Note that this associates all of the out of the box activities with their out of the box designers.  This is optional as a host may wish to provide custom editors for all or some of the out of box activities, or may not be using the out of box activities.
  • Line 24-31, Call Load, passing in an instance of an object graph to display.  This gives the host some flexibility, as this instance could come from XAML, a database, JSON, user input, etc.  We simply create a basic sequence with two activities
  • Line 32, set the column for the view
  • Line 33, add the view to the display

This gives us the following application:

image

Now, that was pretty simple, but we’re also missing some key things, namely, the property grid.  It’s important to note however that this has all of the functionality of the designer (the variables designer, the overview map, etc.  This will react just the same as if you were building the workflow in VS. 

Let’s add the property grid by adding the following two lines:

 Grid.SetColumn(wd.PropertyInspectorView, 2);
grid1.Children.Add(wd.PropertyInspectorView);

This will let us see the property grid (so things get a little more interesting).

image

So, we’re able to display the workflow and interact with it, but we probably also want to have a constrained authoring experience (not just editing), so that comes in the form of the ToolboxControl.  For the sake of this blog post, we’ll use this in XAML, but we certainly can code against it imperatively as well. 

 <Window x:Class="BlogPostRehosting.Window1"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sad="clr-namespace:System.Activities.Design;assembly=System.Activities.Design"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="Window1" Height="664" Width="831">
    <Window.Resources>
        <sys:String x:Key="AssemblyName">System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</sys:String>
    </Window.Resources>
    <Grid Name="grid1">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="2*"/>
        </Grid.ColumnDefinitions>
        <sad:ToolboxControl>
            <sad:ToolboxControl.Categories>
                <sad:ToolboxCategoryItemsCollection CategoryName="Basic">
                    <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.Sequence"/>
                    <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.If"/>
                    <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.Parallel"/>
                    <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.WriteLine"/>
                    <sad:ToolboxItemWrapper AssemblyName="{StaticResource AssemblyName}" ToolName="System.Activities.Statements.Persist"/>
                </sad:ToolboxCategoryItemsCollection>
            </sad:ToolboxControl.Categories>
        </sad:ToolboxControl>
    </Grid>
</Window>

 

 

This lets me specify the items I want to allow a user to drop.

image

The thing that is interesting to point out here is that we’ve built a full featured, constrained editor (with things like copy paste, undo/redo, etc) with not too much code.

Next time, we’ll get into to doing some more interesting bits as well to interact with the item being edited, serialize to XAML, and explore the editing context some more.  Let me know what you think!