大学时,因为毕业论文的关系,开始接触workflow,后来断断续续的关注着这个的发展。前几天dudu的一篇WWF(Windows 
                Workflow Foundation)的简介[翻译] 又勾起了我对workflow的兴趣。看到园子里有这么多关注wwf的朋友们,又发现网上鲜有关于wwf的中文资料,决定挤出点时间把wwf的doc给整个翻译过来,给园子里跟我一样洋文不怎么行的兄弟们点便利。
              小弟才疏,又是第一次写blog,大家见谅了~。为尽量保持原意,我就有什么翻译什么了……那就开始吧~
              Windows Workflow Foundation之旅 (一) - 概述
              翻译自--ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/55da2060-f10f-4ef4-a923-b38e4504516b.htm
               组织并执行一系列的操作或者活动的最自然的方式——那就是工作流——同时也是构造一个工作流程的可执行表现形式的最佳途径。
              Windows Workflow Foundation(以下简称WWF)提供了一个编程框架和工具以开发和执行各种不同的基于工作流的应用程序,比如文档管理、线型的商业应用、贸易单据流程、IT管理、B2B应用以及消费者应用。
              有状态的、持久化的、不间断运行的应用程序
               WWF简化了创造有状态的,不间断运行的异步工作流应用程序的过程。WWF运行时引擎管理工作流的运行,为工作流的长期运行提供保障,并能抵抗机器的重启。WWF运行时服务提供了一系列的附加功能,例如WWF服务为能温和且正确的处理错误提供了事务和持久化。
              工作流模型
               WWF为开发人员提供了一个工作流模型,来描述应用程序所需要的处理过程。通过使用工作流模型所提供的流程控件、状态管理、事务和同步器,开发人员可以分离应用程序逻辑和业务逻辑,构造一个高层次的抽象,达到提高开发者效率的目的。
              组件的重用
               WWF为开发者提供了一系列的活动——活动是一种包含了工作单元的可配置逻辑结构。这种结构封装了开发者可能经常性用到的一些部件,这样就节省了开发者的时间。
              如果遇到一些特殊的需求或场景,WWF同样为开发自定义的活动提供了简单的方法。
              通过将工作流引擎载入进程,WWF可以使任何应用程序和服务容器运行工作流。
              运行时服务组件被设计成可插件形式的,这个可使应用程序以最合适的方式来提供它们的服务。WWF还提供了一组运行时服务的默认实现,这些服务能满足大部分类型的应用程序。
              另外,WWF还提供了对ASP.NET的out-of-the-box(啥意思?)支持,让构造和运行能在IIS和ASP.NET环境的工作流变得简单。
              Windows Workflow Foundation之旅(二)——指南1(构造一个顺序工作流)
              翻译自 ms-help://MS.WinWF.v1.EN/WinWF_GettingStarted/html/9c3e5551-4eff-4977-89ac-f81ab092d996.htm
              顺序工作流(sequential workflow)是为执行一种由一系列预定义的步骤组成的任务而设计的。这种体系结构是模拟基于过程的应用程序的。这一节将用几个步骤来编写一个简单的开支报告程序,这个小程序使用WinFrom做界面,用顺序工作流做业务逻辑。
               这个小程序有一个TextBox来输入开支报告的总数,一个Button点击提交报告。工作流将评估开支,如果开支小于1000则提请领班审批,如果大于等于1000则提请经理审批。之后,工作流会发送一个审批意见,此时,出现一个Label显示审批意见,两个Button分别表示通过和拒绝审批。当某一个按钮被点击的时候,应用程序会通知回应工作流,工作流继续处理发生的事件。
              开始构造顺序工作流
              创建工作流类
               WWF SDK中定义了一个SequentialWorkFlow类,我们定义一个ExpenseRoportWorkflow类,并继承自SequentialWorkflow,这样就创建一个顺序工作流。如:
              声明工作流参数
               
                 1
using System;
                 2
using System.Workflow.Activities;
                 3
using System.Workflow.Activities.Rules;
                 4
                 5
namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow
                 6

{
                 7
    [RuleConditionsAttribute(typeof(Microsoft.Samples.
                Workflow.Quickstarts.SequentialWorkflow.ExpenseReportWorkflow))]
                 8
    public sealed partial class ExpenseReportWorkflow :
                 System.Workflow.Activities.SequentialWorkflow
                 9
    
{
                10
        public ExpenseReportWorkflow()
                11
        
{
                12
                13
        }
                14
    }
                15
}
                16
 
               
				 在一个工作流运行时,它可以从宿主应用程序中接收参数。参数是ParameterDeclaration类型的对象,一旦工作流初始化完成,参数的值就能通过工作流的Parameters集合来访问。
               这里的开始报告程序用了两个参数。第一个参数是开支的总数;第二个是一个传出参数,用来放置审批意见。
               定义一个新的方法InitializeComponent,并在构造ExpenseRoportWorkflow类的构造函数中调用它。一下的例子示范了怎样定义两个参数并把它们加到Parameters集合中。
              
               
              
               
                 

                
                 1
public ExpenseReportWorkflow()
                 2
                 3

{
                 4
                 5
    InitializeComponent();
                 6
                 7
}
                 8
                 9
 
                10
                11
private void InitializeComponent()
                12
                13

{
                14
                15
    System.Workflow.ComponentModel.ParameterDeclaration Amount = 
                new System.Workflow.ComponentModel.ParameterDeclaration();
                16
                17
    System.Workflow.ComponentModel.ParameterDeclaration Result = 
                new System.Workflow.ComponentModel.ParameterDeclaration();
                18
                19
    //
                20
                21
    // Workflow Parameters
                22
                23
    //
                24
                25
    Amount.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
                26
                27
    Amount.Name = "Amount";
                28
                29
    Amount.Type = typeof(int);
                30
                31
    Amount.Value = null;
                32
                33
    Result.Direction = System.Workflow.ComponentModel.ParameterDirection.Out;
                34
                35
    Result.Name = "Result";
                36
                37
    Result.Type = typeof(string);
                38
                39
    Result.Value = null;
                40
                41
    this.Parameters.Add(Amount);
                42
                43
    this.Parameters.Add(Result);
                44
                45
}
                46
                47
 
              使用IfElse活动
               IfElse活动用条件表达式来控制工作流中流程的运行。工作流将根据条件表达式的结果来决定执行条件分支(IfElseBranch)中的哪一个活动。
               例子中将使用IfElse活动。通过判断从宿主应用程序中传入的Amount参数的值是否小于1000,来决定是否将审报发送到领班,否则发送到经理。
              创建IfElse活动
               1.定义4个私有变量
              
                 
                  类型 
                     | 
                  名称 | 
                
                 
                  | IfElse | 
                  evaluateExpenseReportAmount | 
                
                 
                  | IfElseBranch | 
                  ifNeedsLeadApproval | 
                
                 
                  | IfElseBranch | 
                  elseNeedsManagerApproval | 
                
                 
                  | CodeCondition | 
                  ifElseLogicStatement | 
                
              
              2.在InitializeComponent中用默认构造函数实例以上4个对象。
               以下的代码示例了怎样创建IfElse活动,并用IfElseBranch活动联系两个逻辑分支。你需要把以下代码放到InitializeComponent方法底部。
               
                 1
// 
                 2
                 3
// EvaluateExpenseReportAmount
                 4
                 5
// 
                 6
                 7
this.EvaluateExpenseReportAmount.Activities.Add(this.ifNeedsLeadApproval);
                 8
                 9
this.EvaluateExpenseReportAmount.Activities.Add(this.elseNeedsManagerApproval);
                10
                11
this.EvaluateExpenseReportAmount.ID = "EvaluateExpenseReportAmount";
                12
                13
// 
                14
                15
// ifNeedsLeadApproval
                16
                17
// 
                18
                19
this.ifNeedsLeadApproval.Activities.Add(this.invokeGetLeadApproval);
                20
                21
ifElseLogicStatement.Condition += new System.Workflow.Activities.
                ConditionalExpression(this.DetermineApprovalContact);
                22
                23
this.ifNeedsLeadApproval.Condition = ifElseLogicStatement;
                24
                25
this.ifNeedsLeadApproval.ID = "ifNeedsLeadApproval";
                26
                27
// 
                28
                29
// elseNeedsManagerApproval
                30
                31
// 
                32
                33
this.elseNeedsManagerApproval.Activities.Add(this.invokeGetManagerApproval);
                34
                35
this.elseNeedsManagerApproval.Condition = null;
                36
                37
this.elseNeedsManagerApproval.ID = "elseNeedsManagerApproval";
                38
 
              WWF在IfElse活动中,有两种评估条件表达式的方式。一种是RoleCondition,这个对象通过使用一组规则来判断条件表达式的结果;另一种就是使用CodeCondition活动。CodeCondition使用一个回调方法,这个回调方法返回一个代表评估结果的布尔值。上面的例子就是使用CodeCondition来决定条件表达式的值。如果Amount参数小于1000,回调方法返回true,否则返回false。以下的代码就是这个回调函数的定义,你可以把它加到工作流类的定义中。
              
               
                 1
private bool DetermineApprovalContact(object sender, EventArgs e)
                 2
                 3

{
                 4
                 5
    if ( Convert.ToInt32(this.Parameters["Amount"].Value) < 1000 )
                 6
                 7
        return true;
                 8
                 9
 
                10
                11
    return false;
                12
                13
}
                14
 
               
                 
              构造IfElse分支(IfElseBranch)活动
               创建完IfElse活动之后,我们来构造IfElseBranch活动
               在这个例子中,每一IfElse活动的分支都使用InvokeMethodActivity活动来通知宿主程序——工作流需要领班或经理的审批才能继续执行。InvokeMethodActivity被设计成调用一个在WWF运行时中的服务接口。我们在同一份代码文件中定义了这个接口。当我们在之后构造宿主程序时,宿主类将实现这个接口,以便能建立工作流和宿主程序的通信(这一段文档上写的很模糊,我reflect后看了源码才明白过来,在最后将补充描述一下)。
               构建IfElseBranch活动
               1. 在类中定义两个私有字段
              
                 
                  类型 
                     | 
                  名称 | 
                
                 
                  | InvokeMethodActivity | 
                  invokeGetLeadApproval | 
                
                 
                  |  InvokeMethodActivity | 
                  invokeGetManagerApproval | 
                
              
              2. 在InitializeComponent中用默认构造函数实例化这两个对象
               以下的代码示例了怎样在父活动(IfElse)中创建IfElseBranch活动,并把两个的InvokeMethodActivity联系到对应的IfElseBranch活动上,每个InvokeMethodActivity将调用定义在IExpenseReportService接口中的方法,接口会在稍微实现。你需要把以下代码放到InitializeComponent方法底部。
               
               
                 1
// 
                 2
                 3
// invokeGetLeadApproval
                 4
                 5
// 
                 6
                 7
this.invokeGetLeadApproval.ID = "invokeGetLeadApproval";
                 8
                 9
this.invokeGetLeadApproval.InterfaceType = typeof(Microsoft.
                Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
                10
                11
this.invokeGetLeadApproval.MethodName = "GetLeadApproval";   
                12
                13
// 
                14
                15
// invokeGetManagerApproval
                16
                17
// 
                18
                19
this.invokeGetManagerApproval.ID = "invokeGetManagerApproval";
                20
                21
this.invokeGetManagerApproval.InterfaceType = typeof(Microsoft.
                Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
                22
                23
this.invokeGetManagerApproval.MethodName = "GetManagerApproval";
                24
                25 
              以下代码定义了IExpenseReportService接口
               
               
                 1
using System;
                 2
                 3
using System.Workflow.ComponentModel;
                 4
                 5
using System.Workflow.Runtime.Messaging;
                 6
                 7
 
                 8
                 9
namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow
                10
                11

{
                12
                13
    [DataExchangeService]
                14
                15
    public interface IExpenseReportService
                16
                17
    
{
                18
                19
        void GetLeadApproval();
                20
                21
        void GetManagerApproval();
                22
                23
        event EventHandler<WorkflowMessageEventArgs> ExpenseReportApproved;
                24
                25
        event EventHandler<WorkflowMessageEventArgs> ExpenseReportRejected;
                26
                27
    }
                28
                29
}
                30
                31
 
                
              监听宿主事件
              在这个阶段,工作流已经从宿主程序接受了两个参数(译者注:其中一个为out参数,此时设为null),评估了Amount参数,作出了到底该提请谁确认审批的决定,并通知了宿主程序在继续接下来的处理之前,确认审批。这里,Listen活动和EventSinkActivity活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个approval或rejection事件被引发,工作流继续执行,返回审批结果Result,并终止流程。
              Listen活动的每个分支是一个EventDriven活动。EventDriven活动只能使用实现了IEventActivity接口的活动。Listen活动的每个分支中的EventDriven各有一个EventSinkActivity,它们是用来监听宿主程序触发的ExpenseReportApproved或者ExpenseReportRejected事件的。这种工作流和宿主的通信方法其实类似于之前的InvokeMethodActivity的过程,只不过前者是工作流监听宿主事件,而后者是宿主事件工作流中注册的接口。
              我们已经在前面的步骤中,把接口和两个事件的定义都完成了。在这里,我们将创建一个Listen活动并和两个EventDriven分支建立连接。每个分支包含一个EventSinkActivity活动,每个EventSink监听一种对应的事件。此外,我们还将创建一些事件处理程序,来处理AfterInvoke事件(译者注:这里的AfterInvoke事件应为Invoked事件)。这些事件处理程序将会把Result参数的值设为approval或者rejected。
              构造监听活动
              1.在工作流类中定义5个私有字段
              
                 
                  类型 
                     | 
                  名称 | 
                
                
                  Listen 
                   | 
                  listenApproveReject | 
                
                 
                  | EventDriven | 
                  approveEventDriven | 
                
                 
                  | EventDriven | 
                  rejectEventDriven | 
                
                 
                  | EventSinkActivity | 
                  approveEvent | 
                
                 
                  | EventSinkActivity | 
                  rejectEvent | 
                
              
              2. 在InitializeComponent中实例化。
               以下的代码示例了怎样在创建Listen活动和EventSinkActivity活动,来监听宿主程序发出的事件。你需要把以下代码放到InitializeComponent方法底部。
              
               
               
                 1
// 
                 2
                 3
// listenApproveReject
                 4
                 5
// 
                 6
                 7
this.listenApproveReject.Activities.Add(this.approveEventDriven);
                 8
                 9
this.listenApproveReject.Activities.Add(this.rejectEventDriven);
                10
                11
this.listenApproveReject.ID = "listenApproveReject";
                12
                13
// 
                14
                15
// approveEventDriven
                16
                17
// 
                18
                19
this.approveEventDriven.Activities.Add(this.approveEvent);
                20
                21
this.approveEventDriven.ID = "approveEventDriven";
                22
                23
// 
                24
                25
// approveEvent
                26
                27
// 
                28
                29
this.approveEvent.EventName = "ExpenseReportApproved";
                30
                31
this.approveEvent.ID = "approveEvent";
                32
                33
this.approveEvent.InterfaceType = typeof(Microsoft.Samples.
                Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
                34
                35
this.approveEvent.Roles = null;
                36
                37
this.approveEvent.Invoked += new System.EventHandler
                (this.approveEvent_Invoked);
                38
                39
// 
                40
                41
// rejectEventDriven
                42
                43
// 
                44
                45
this.rejectEventDriven.Activities.Add(this.rejectEvent);
                46
                47
this.rejectEventDriven.ID = "rejectEventDriven";
                48
                49
// 
                50
                51
// rejectEvent
                52
                53
// 
                54
                55
this.rejectEvent.EventName = "ExpenseReportRejected";
                56
                57
this.rejectEvent.ID = "rejectEvent";
                58
                59
this.rejectEvent.InterfaceType = typeof(Microsoft.Samples.
                Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
                60
                61
this.rejectEvent.Roles = null;
                62
                63
this.rejectEvent.Invoked += new System.EventHandler
                (this.rejectEvent_Invoked);
                64
                65
 
               
                
              使用EventSinkActivity时,为了在工作流中加入一些附加逻辑,你可以为Invoked事件创建一个事件处理程序。一下是事件处理程序的代码。
              
               
               
                 1
private void approveEvent_Invoked(object sender, EventArgs e)
                 2
                 3

{
                 4
                 5
    this.Parameters["Result"].Value = "Report Approved";
                 6
                 7
}
                 8
                 9
 
                10
                11
private void rejectEvent_Invoked(object sender, EventArgs e)
                12
                13

{
                14
                15
    this.Parameters["Result"].Value = "Report Rejected";
                16
                17
}
                18
                19
 
               
                
              完成顺序工作流
               这个工作流包括两个主要的步骤:第一,监听宿主程序的递交审批事件,并把传入的值作为工作流参数;第二,监听通过或拒绝审批消息。一下的代码示例了怎样通过把之前创建好的活动加到工作流活动集中,来完成工作流的构造。
              
               
               
                 1
// 
                 2
                 3
// ExpenseReportWorkflow
                 4
                 5
// 
                 6
                 7
this.Activities.Add(this.EvaluateExpenseReportAmount);
                 8
                 9
this.Activities.Add(this.listenApproveReject);
                10
                11
this.DynamicUpdateCondition = null;
                12
                13
this.ID = "ExpenseReportWorkflow";
                14
                15
 
                
               
				
              创建宿主程序
              WWF需要一个宿主程序来运行工作流。当程序开始运行,WWF运行时引擎也随之启动。而之前构造好的工作流,则到用户点击了Submit按钮后才真正启动。
              建立一个新的源文件,取名Program。以下的代码包含了完整的WinForm应用程序。IExpenseReportService接口GetLeadApproval和GetmanagerApproval方法已经定义在另一个文件中。宿主程序实现了这个接口。在approval和rejected按钮的click事件中,将引发ExpenseReprotApproval或ExpenseReprotRejected事件。
               
              
               
              
               
                  1
using System;
                  2
                  3
using System.ComponentModel;
                  4
                  5
using System.Drawing;
                  6
                  7
using System.Windows.Forms;
                  8
                  9
using System.Collections.Generic;
                 10
                 11
using System.Workflow.Runtime;
                 12
                 13
using System.Workflow.Runtime.Hosting;
                 14
                 15
using System.Workflow.Runtime.Messaging;
                 16
                 17
 
                 18
                 19
namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflowHost
                 20
                 21

{
                 22
                 23
    public class MainForm : Form, Microsoft.Samples.
                Workflow.Quickstarts.SequentialWorkflow.
                IExpenseReportService
                 24
                 25
    
{
                 26
                 27
        private System.Windows.Forms.Label label1;
                 28
                 29
        private System.Windows.Forms.TextBox result;
                 30
                 31
        private System.Windows.Forms.Label label2;
                 32
                 33
        private System.Windows.Forms.Button submitButton;
                 34
                 35
        private System.Windows.Forms.Label approvalState;
                 36
                 37
        private System.Windows.Forms.Button approveButton;
                 38
                 39
        private System.Windows.Forms.Button rejectButton;
                 40
                 41
        private System.Windows.Forms.TextBox amount;
                 42
                 43
        private System.Windows.Forms.Panel panel1;
                 44
                 45
 
                 46
                 47
        private System.ComponentModel.IContainer components = null;
                 48
                 49
 
                 50
                 51
        private delegate void GetApprovalDelegate();
                 52
                 53
        private WorkflowRuntime workflowRuntime = null;
                 54
                 55
        private WorkflowInstance workflowInstance = null;
                 56
                 57
 
                 58
                 59
        public MainForm()
                 60
                 61
        
{
                 62
                 63
            InitializeComponent();
                 64
                 65
 
                 66
                 67
            // Collapse approve/reject panel
                 68
                 69
            this.Height -= this.panel1.Height;
                 70
                 71
 
                 72
                 73
            workflowRuntime = new WorkflowRuntime();
                 74
                 75
            workflowRuntime.AddService(this);
                 76
                 77
            workflowRuntime.StartRuntime();
                 78
                 79
 
                 80
                 81
            workflowRuntime.WorkflowCompleted += new EventHandler
                <WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
                 82
                 83
        }
                 84
                 85
 
                 86
                 87
        protected override void Dispose(bool disposing)
                 88
                 89
        
{
                 90
                 91
            if (disposing && (components != null))
                 92
                 93
            
{
                 94
                 95
                components.Dispose();
                 96
                 97
            }
                 98
                 99
            base.Dispose(disposing);
                100
                101
        }
                102
                103
 
                104
                105
        private void InitializeComponent()
                106
                107
        
{
                108
                109
            this.label1 = new System.Windows.Forms.Label();
                110
                111
            this.result = new System.Windows.Forms.TextBox();
                112
                113
            this.label2 = new System.Windows.Forms.Label();
                114
                115
            this.submitButton = new System.Windows.Forms.Button();
                116
                117
            this.approvalState = new System.Windows.Forms.Label();
                118
                119
            this.approveButton = new System.Windows.Forms.Button();
                120
                121
            this.rejectButton = new System.Windows.Forms.Button();
                122
                123
            this.amount = new System.Windows.Forms.TextBox();
                124
                125
            this.panel1 = new System.Windows.Forms.Panel();
                126
                127
            this.panel1.SuspendLayout();
                128
                129
            this.SuspendLayout();
                130
                131
            // 
                132
                133
            // label1
                134
                135
            // 
                136
                137
            this.label1.AutoSize = true;
                138
                139
            this.label1.Location = new System.Drawing.Point(13, 13);
                140
                141
            this.label1.Name = "label1";
                142
                143
            this.label1.Size = new System.Drawing.Size(39, 13);
                144
                145
            this.label1.TabIndex = 1;
                146
                147
            this.label1.Text = "Amount";
                148
                149
            // 
                150
                151
            // result
                152
                153
            // 
                154
                155
            this.result.Location = new System.Drawing.Point(13, 69);
                156
                157
            this.result.Name = "result";
                158
                159
            this.result.ReadOnly = true;
                160
                161
            this.result.Size = new System.Drawing.Size(162, 20);
                162
                163
            this.result.TabIndex = 1;
                164
                165
            this.result.TabStop = false;
                166
                167
            // 
                168
                169
            // label2
                170
                171
            // 
                172
                173
            this.label2.AutoSize = true;
                174
                175
            this.label2.Location = new System.Drawing.Point(13, 54);
                176
                177
            this.label2.Name = "label2";
                178
                179
            this.label2.Size = new System.Drawing.Size(33, 13);
                180
                181
            this.label2.TabIndex = 3;
                182
                183
            this.label2.Text = "Result";
                184
                185
            // 
                186
                187
            // submitButton
                188
                189
            // 
                190
                191
            this.submitButton.Enabled = false;
                192
                193
            this.submitButton.Location = new System.Drawing.Point(56, 95);
                194
                195
            this.submitButton.Name = "submitButton";
                196
                197
            this.submitButton.Size = new System.Drawing.Size(75, 23);
                198
                199
            this.submitButton.TabIndex = 2;
                200
                201
            this.submitButton.Text = "Submit";
                202
                203
            this.submitButton.Click += new System.EventHandler(this.submitButton_Click);
                204
                205
            // 
                206
                207
            // approvalState
                208
                209
            // 
                210
                211
            this.approvalState.AutoSize = true;
                212
                213
            this.approvalState.Location = new System.Drawing.Point(10, 9);
                214
                215
            this.approvalState.Name = "approvalState";
                216
                217
            this.approvalState.Size = new System.Drawing.Size(45, 13);
                218
                219
            this.approvalState.TabIndex = 4;
                220
                221
            this.approvalState.Text = "Approval";
                222
                223
            // 
                224
                225
            // approveButton
                226
                227
            // 
                228
                229
            this.approveButton.Enabled = false;
                230
                231
            this.approveButton.Location = new System.Drawing.Point(11, 25);
                232
                233
            this.approveButton.Name = "approveButton";
                234
                235
            this.approveButton.Size = new System.Drawing.Size(75, 23);
                236
                237
            this.approveButton.TabIndex = 0;
                238
                239
            this.approveButton.Text = "Approve";
                240
                241
            this.approveButton.Click += new System.EventHandler(this.approveButton_Click);
                242
                243
            // 
                244
                245
            // rejectButton
                246
                247
            // 
                248
                249
            this.rejectButton.Enabled = false;
                250
                251
            this.rejectButton.Location = new System.Drawing.Point(86, 25);
                252
                253
            this.rejectButton.Name = "rejectButton";
                254
                255
            this.rejectButton.Size = new System.Drawing.Size(75, 23);
                256
                257
            this.rejectButton.TabIndex = 1;
                258
                259
            this.rejectButton.Text = "Reject";
                260
                261
            this.rejectButton.Click += new System.EventHandler(this.rejectButton_Click);
                262
                263
            // 
                264
                265
            // amount
                266
                267
            // 
                268
                269
            this.amount.Location = new System.Drawing.Point(13, 29);
                270
                271
            this.amount.MaxLength = 9;
                272
                273
            this.amount.Name = "amount";
                274
                275
            this.amount.Size = new System.Drawing.Size(162, 20);
                276
                277
            this.amount.TabIndex = 0;
                278
                279
            this.amount.KeyPress += new System.Windows.Forms.
                KeyPressEventHandler(this.amount_KeyPress);
                280
                281
            this.amount.TextChanged += new System.EventHandler
                (this.amount_TextChanged);
                282
                283
            // 
                284
                285
            // panel1
                286
                287
            // 
                288
                289
            this.panel1.Controls.Add(this.approvalState);
                290
                291
            this.panel1.Controls.Add(this.approveButton);
                292
                293
            this.panel1.Controls.Add(this.rejectButton);
                294
                295
            this.panel1.Location = new System.Drawing.Point(3, 124);
                296
                297
            this.panel1.Name = "panel1";
                298
                299
            this.panel1.Size = new System.Drawing.Size(172, 66);
                300
                301
            this.panel1.TabIndex = 8;
                302
                303
            // 
                304
                305
            // MainForm
                306
                307
            // 
                308
                309
            this.AcceptButton = this.submitButton;
                310
                311
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
                312
                313
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
                314
                315
            this.ClientSize = new System.Drawing.Size(187, 201);
                316
                317
            this.Controls.Add(this.panel1);
                318
                319
            this.Controls.Add(this.amount);
                320
                321
            this.Controls.Add(this.submitButton);
                322
                323
            this.Controls.Add(this.label2);
                324
                325
            this.Controls.Add(this.result);
                326
                327
            this.Controls.Add(this.label1);
                328
                329
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
                330
                331
            this.MaximizeBox = false;
                332
                333
            this.MinimizeBox = false;
                334
                335
            this.Name = "MainForm";
                336
                337
            this.Text = "Simple Expense Report";
                338
                339
            this.panel1.ResumeLayout(false);
                340
                341
            this.panel1.PerformLayout();
                342
                343
            this.ResumeLayout(false);
                344
                345
            this.PerformLayout();
                346
                347
 
                348
                349
        }
                350
                351
 
                352
                353
        private void submitButton_Click(object sender, EventArgs e)
                354
                355
        
{
                356
                357
            Type type = typeof(Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.ExpenseReportWorkflow);
                358
                359
 
                360
                361
            // Construct workflow parameters
                362
                363
            Dictionary<string, object> properties = new Dictionary<string, object>();
                364
                365
            properties.Add("Amount", Int32.Parse(this.amount.Text));
                366
                367
            properties.Add("Result", string.Empty);
                368
                369
 
                370
                371
            // Start the workflow
                372
                373
            workflowInstance = workflowRuntime.StartWorkflow(type, properties);
                374
                375
        }
                376
                377
 
                378
                379
        void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
                380
                381
        
{
                382
                383
            if (this.result.InvokeRequired)
                384
                385
                this.result.Invoke(new EventHandler<WorkflowCompletedEventArgs>
                (this.workflowRuntime_WorkflowCompleted), sender, e);
                386
                387
            else
                388
                389
            
{
                390
                391
                this.result.Text = e.OutputParameters["Result"].ToString();
                392
                393
 
                394
                395
                // Clear fields
                396
                397
                this.amount.Text = string.Empty;
                398
                399
 
                400
                401
                // Disable buttons
                402
                403
                this.approveButton.Enabled = false;
                404
                405
                this.rejectButton.Enabled = false;
                406
                407
            }
                408
                409
        }
                410
                411
 
                412
                413
        private void approveButton_Click(object sender, EventArgs e)
                414
                415
        
{
                416
                417
            // Raise the ExpenseReportApproved event back to the workflow
                418
                419
            ExpenseReportApproved(null, new WorkflowMessageEventArgs
                (this.workflowInstance.InstanceId));
                420
                421
            this.Height -= this.panel1.Height;
                422
                423
            this.submitButton.Enabled = true;
                424
                425
        }
                426
                427
 
                428
                429
        private void rejectButton_Click(object sender, EventArgs e)
                430
                431
        
{
                432
                433
            // Raise the ExpenseReportRejected event back to the workflow
                434
                435
            ExpenseReportRejected(null, new WorkflowMessageEventArgs
                (this.workflowInstance.InstanceId));
                436
                437
            this.Height -= this.panel1.Height;
                438
                439
            this.submitButton.Enabled = true;
                440
                441
        }
                442
                443
 
                444
                445
        IExpenseReportService Members#region IExpenseReportService Members
                446
                447
 
                448
                449
        public void GetLeadApproval()
                450
                451
        
{
                452
                453
            if (this.approvalState.InvokeRequired)
                454
                455
                this.approvalState.Invoke(new GetApprovalDelegate(this.GetLeadApproval));
                456
                457
            else
                458
                459
            
{
                460
                461
                this.approvalState.Text = "Lead approval needed";
                462
                463
                this.approveButton.Enabled = true;
                464
                465
                this.rejectButton.Enabled = true;
                466
                467
 
                468
                469
                // expand the panel
                470
                471
                this.Height += this.panel1.Height;
                472
                473
                this.submitButton.Enabled = false;
                474
                475
            }
                476
                477
        }
                478
                479
 
                480
                481
        public void GetManagerApproval()
                482
                483
        
{
                484
                485
            if (this.approvalState.InvokeRequired)
                486
                487
                this.approvalState.Invoke(new GetApprovalDelegate(this.GetManagerApproval));
                488
                489
            else
                490
                491
            
{
                492
                493
                this.approvalState.Text = "Manager approval needed";
                494
                495
                this.approveButton.Enabled = true;
                496
                497
                this.rejectButton.Enabled = true;
                498
                499
 
                500
                501
                // expand the panel
                502
                503
                this.Height += this.panel1.Height;
                504
                505
                this.submitButton.Enabled = false;
                506
                507
            }
                508
                509
        }
                510
                511
 
                512
                513
        public event EventHandler<WorkflowMessageEventArgs> ExpenseReportApproved;
                514
                515
        public event EventHandler<WorkflowMessageEventArgs> ExpenseReportRejected;
                516
                517
 
                518
                519
        #endregion
                520
                521
 
                522
                523
        private void amount_KeyPress(object sender, KeyPressEventArgs e)
                524
                525
        
{
                526
                527
            if (!Char.IsControl(e.KeyChar) && (!Char.IsDigit(e.KeyChar)))
                528
                529
                e.KeyChar = Char.MinValue;            
                530
                531
        }
                532
                533
 
                534
                535
        private void amount_TextChanged(object sender, EventArgs e)
                536
                537
        
{
                538
                539
            submitButton.Enabled = amount.Text.Length > 0;
                540
                541
        }
                542
                543
    }
                544
                545
 
                546
                547
    static class Program
                548
                549
    
{
                550
                551
        /**//// <summary>
                552
                553
        /// The main entry point for the application.
                554
                555
        /// </summary>
                556
                557
        [STAThread]
                558
                559
        static void Main()
                560
                561
        
{
                562
                563
            Application.EnableVisualStyles();
                564
                565
            Application.Run(new MainForm());
                566
                567
        }
                568
                569
    }
                570
                571
}
                572
                573
 
               
                 
               
                
              Ps:上面还有个问题没有解释清楚,我reflect了一下,看了源码才知道个大概。
                那就是IfElseBranch中的InvokeMethodActivity。
              InvokeMethodActivity中有一个Type类型的InterfaceType属性,使用时,需要设置这个属性,并把MethodName设为这个接口中的一个方法的名称。运行时,工作流引擎(也就是WorkflowRuntime)将通过反射调用这个接口。
              但引擎怎么知道调用接口的哪个实现呢?你看
              workflowRuntime = new WorkflowRuntime();
              workflowRuntime.AddService(this);
              workflowRuntime.StartRuntime();
              原来,初始化引擎时,我们已经把实现了这个interface的类型的实例(this)注册到工作流中了。运行时,引擎就遍历所有已经注册的服务,如果实现了这个接口,这调用它。
              另外,可以注册一个以上的服务,工作流引擎将调用所有实现了相应接口的接口方法。