はじめに
Workflow Foundation(以下 WF)は.NET 3.0で提供されましたが、.NET 4になり更新されたWF4はそれまでのWF3とはまったく異なるモデルを採用したこともあり、内部構造および利用方法はそれまでと別物になりました。今回はWF4で提供されVisual Studio上でも利用されているワークフローデザイナーを、どのようにして自作アプリケーション上で利用するのかを紹介します。
対象読者
- 異なる開発手法に興味のある方
- Workflow Foundationに興味のある方
- VB、C#でプログラミングを行ったことがある方
必要な環境
サンプルコードを実行するには、Express Editionを含むVisual Studio 2010が必要です。
デザイナー上でワークフローを作成するには、Professional以上のVisual Studio 2010が必要です。ただしWF 4ではワークフローデザイナーの自作が容易になっており、Experss Editionでも作成可能です。
リホスティングデザイナの作成
非常にシンプルなWFデザイナー
WF4で提供されているデザイナー機能には、以下のものがあります。
- 
      ワークフローデザイナー(WorkflowDesignerクラス)
      メインとなるワークフローのデザイナー部です。アクティビティのドラッグ&ドロップや、デザインビューの拡大・縮小・画像への出力などの機能があります。このワークフローデザイナーで、ワークフロー作成のほとんどの作業を行います。 
- 
      ツールボックス(ToolboxControlクラス)
      アクティビティを登録することができるツールボックスです。ここからワークフローデザイナーにドラッグ&ドロップすることで、ワークフローを作成することができます。Visual Studioのツールボックスとは別コントロールです。 
- 
      プロパティグリッド(WorkflowDesigner.PropertyInspectorViewプロパティ)
      ワークフローデザイナー上で選択しているアクティビティのプロパティを表示するグリッドです。ワークフローデザイナー上の操作と連動しています。 
これらの機能はツールボックスを除き、Visual Studio 2010でも利用されています。
 
    これらの標準機能を利用するにあたり、特に難しいことはありません。非常に簡易なコーディングで上記サンプル画面のように、基本機能を有したアプリケーションを作成できるようになっています。
<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="600" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition Width="400*" />
            <ColumnDefinition Width="200" />
        </Grid.ColumnDefinitions>
        <Border Name="ToolboxArea" Grid.Column="0"></Border>
        <Border Name="DesignerArea" Grid.Column="1"></Border>
        <Border Name="PropertyArea" Grid.Column="2"></Border>
    </Grid>
</Window>
  
Imports System.Activities
Imports System.Activities.Presentation
Imports System.Activities.Statements
Imports System.Activities.Core.Presentation
Public Class MainWindow
    Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
       ‘メタデータへの登録
        Dim metadata As New DesignerMetadata()
        metadata.Register() 
        'ワークフローデザイナーの作成
        Dim wfDesign As New WorkflowDesigner
        DesignerArea.Child = wfDesign.View
        'ツールボックスの作成
        Dim wfToolbox As New Toolbox.ToolboxControl
        ToolboxArea.Child = wfToolbox
        'プロパティグリッドの作成
        Dim wfProp = wfDesign.PropertyInspectorView
        PropertyArea.Child = wfProp
        'サンプル表示
        wfDesign.Load(New Sequence)
        Dim tlCategory As New Toolbox.ToolboxCategory("CodeZine")
        tlCategory.Add(New Toolbox.ToolboxItemWrapper(GetType(WriteLine)))
        wfToolbox.Categories.Add(tlCategory)
    End Sub
End Class
  
using System.Windows;
using System.Windows.Input;
using System.Activities;
using System.Activities.Core.Presentation;
using System.Activities.Statements;
using System.Activities.Presentation;
using System.Activities.XamlIntegration;
namespace WFDesignerSample
{
     public partial class MainWindow : Window
    {
        private WorkflowDesigner wfDesign; 
        public MainWindow()
        {
            InitializeComponent();
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //メタデータの読み込み
            var metadata = new DesignerMetadata();
            metadata.Register();
            //ワークフローデザイナーの作成
            wfDesign = new WorkflowDesigner();
            DesignerArea.Child = wfDesign.View;
            //ツールボックスの作成
            var wfToolbox = new System.Activities.Presentation.Toolbox.ToolboxControl();
            ToolboxArea.Child = wfToolbox;
            //プロパティグリッドの作成
            var wfProp = wfDesign.PropertyInspectorView;
            PropertyArea.Child = wfProp;
            //サンプル表示
            wfDesign.Load(new Sequence());
            var tlCategory = new System.Activities.Presentation.Toolbox.ToolboxCategory("CodeZine");
            tlCategory.Add(new System.Activities.Presentation.Toolbox.ToolboxItemWrapper(typeof(WriteLine)));
            wfToolbox.Categories.Add(tlCategory);
        }
    }
}
  XAML側で定義している内容は、ウインドゥの内部に3列に分けたグリッドを配置しているだけであり、他には何も行っていません。それぞれ提供されているコントロールを生成し、WPFウインドゥ上に設定するだけで基本機能はすべて利用可能となっているのが分かると思います。またワークフローデザイナーで選択しているアクティビティのプロパティが、プロパティグリッド上に表示されていますが、この点についても特に何らかのコードや設定が必要というわけではなく、ウインドゥ上に配置した時点からそのまま利用することができます。
注意点としては、WorkflowDesignerクラスのLoadメソッドは、インスタンス生成後一度しか利用できないという点があります。もしWorkflowDesginerを再作成せずに2度目のLoadメソッド呼び出しを行うと、InvalidOperationExceptionが発生します。新規に別のワークフローを作成するような場合は、WorkflowDesignerクラスのインスタンスを新たに生成し、再度Loadメソッドで呼びなおす必要があります。
またもう一つの注意点として、メタデータへの登録が必要である点があります。これを行うことで、アクティビティに関連づいているアクティビティデザイナーや、検証ロジックなど、属性やコードを利用して指定している内容が反映されます。逆にメタデータの登録を行わない状態では、アクティビティデザイナーが表示されなかったり検証がうまく動作しないなどが発生しますので、この点も気を付けてください。
このようにWF4では、独自のデザイナーアプリケーションを作成することを今までよりもはるかに容易に行うことが可能です。
今回のサンプルでは、各コントロールの配置をすべてコード側から行っていますが、ツールボックスはXAML側で記述できます。その場合は以下のようにXAMLを記述します。
名前空間の定義にxmlns:sapt="clr-namespace:System.Activities.Presentation.Toolbox;assembly=System.Activities.Presentation" を追記した上で以下の記述を行います。
    ・・・
    <Border Name="ToolboxArea" Grid.Column="0">
      <sapt:ToolboxControl>
        <sapt:ToolboxCategory CategoryName="CodeZine">
          <sapt:ToolboxItemWrapper AssemblyName="System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" >
            <sapt:ToolboxItemWrapper.ToolName>
              System.Activities.Statements.WriteLine
            </sapt:ToolboxItemWrapper.ToolName>
          </sapt:ToolboxItemWrapper>
        </sapt:ToolboxCategory>
      </sapt:ToolboxControl>
    </Border>
      
 
              
               
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                              
                               
                              
                               
                              
                               
                              
                               
                              
                               
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
															
														 
															
														.png) 
     
     
     
     
     
													 
													 
													 
													 
													 
										
									
 
                    