One of the coolest things about Windows Workflow Foundation is the workflow designer. It’s visual programming. You drag and drop your components onto the design surface and configure the properties of each module to create a functional application. Microsoft makes it easy to re-host the workflow designer in your own applications, providing you with an easy way to include customization and configuration tools for your workflows.
I found a great bare-bones tutorial at MSDN: Rehosting the Workflow Designer.
There’s some important functionality missing from this tutorial, though. There are no mechanisms for opening or saving workflow files, and the toolbox is only populated with two controls. So, I’ve taken the MSDN tutorial, compressed the steps, and added some additional key functionality.
- Create a new WPF Application. I called mine “WorkflowEditor.”
- Add references
- System.Activities
- System.Activities.Core.Presentation
- System.Activities.Presentation
- Copy/paste the XAML below into MainWindow.xaml
- Copy/paste the code-behind below into MainWindow.xaml.cs
That’s it! Once you’re this far, you should be able to run the program. If you’re looking for a more in-depth, step-by-step explanation of the code, follow the link above to the MSDN tutorial. The only differences below are that I added some reflection code to extract the “standard” workflow activities, and I added Open and Save buttons.
<Window x:Class="adamprescott.net.WorkflowEditor.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="500" Width="800"> <Grid Name="grid1"> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="4*" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="36" /> <RowDefinition /> </Grid.RowDefinitions> <ToolBar Grid.Row="0" Grid.ColumnSpan="3"> <Button Click="OnOpenClick">Load</Button> <Button Click="OnSaveClick">Save</Button> </ToolBar> </Grid> </Window>
using System; using System.Activities; using System.Activities.Core.Presentation; using System.Activities.Presentation; using System.Activities.Presentation.Toolbox; using System.Activities.Statements; using System.Linq; using System.Reflection; using System.Windows.Controls; using Microsoft.Win32; namespace adamprescott.net.WorkflowEditor { public partial class MainWindow { private WorkflowDesigner _designer; public MainWindow() { InitializeComponent(); RegisterMetadata(); InitializeDesigner(); AddToolBox(); } private void AddDesigner() { _designer = new WorkflowDesigner(); Grid.SetColumn(_designer.View, 1); Grid.SetRow(_designer.View, 1); grid1.Children.Add(_designer.View); } private void RegisterMetadata() { var dm = new DesignerMetadata(); dm.Register(); } private ToolboxControl GetToolboxControl() { var toolbox = new ToolboxControl(); PopulateToolboxCategoryFromAssembly(toolbox, typeof(Assign).Assembly, "Standard"); return toolbox; } private void PopulateToolboxCategoryFromAssembly(ToolboxControl toolbox, Assembly assembly, string categoryName) { var tools = assembly.GetTypes() .Where(t => t.IsSubclassOf(typeof(Activity)) && t.IsPublic && !t.IsAbstract && HasParameterlessContructor(t)) .Select(t => new ToolboxItemWrapper(t.FullName, t.Assembly.FullName, null, t.Name)) .OrderBy(t => t.DisplayName); var category = new ToolboxCategory(categoryName); foreach (var t in tools) { category.Add(t); } toolbox.Categories.Add(category); } private bool HasParameterlessContructor(Type t) { var ctors = t.GetConstructors(); var parameterless = ctors.Where(c => c.GetParameters().Count() == 0) .FirstOrDefault(); return parameterless != null; } private void AddToolBox() { var tc = GetToolboxControl(); Grid.SetColumn(tc, 0); Grid.SetRow(tc, 1); grid1.Children.Add(tc); } private void AddPropertyInspector() { Grid.SetColumn(_designer.PropertyInspectorView, 2); Grid.SetRow(_designer.PropertyInspectorView, 1); grid1.Children.Add(_designer.PropertyInspectorView); } private void OnOpenClick(object sender, System.Windows.RoutedEventArgs e) { var dlg = new OpenFileDialog(); if (dlg.ShowDialog().Value) { InitializeDesigner(); _designer.Load(dlg.FileName); } } private void OnSaveClick(object sender, System.Windows.RoutedEventArgs e) { var dlg = new SaveFileDialog(); if (dlg.ShowDialog().Value) { _designer.Save(dlg.FileName); } } private void InitializeDesigner() { AddDesigner(); AddPropertyInspector(); } } }