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
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
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:
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).
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.
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!
Comments
Anonymous
June 17, 2009
ASP.NET/VS2010 and NETFW 4 Updates to FTP publishing in Visual Studio 2010 WF/WCF/REST/Identity Management Learning by example with 4 - All about the code samples you can download for WF/WCF 4.0 Transactions are bad for REST Announcing FabrikamShipping,Anonymous
June 17, 2009
The comment has been removedAnonymous
July 16, 2009
Really interesting post, thanks for it !! I'm waiting with a lot of interest the next articles.