zoukankan      html  css  js  c++  java
  • Windows Workflow Foundation(一)(转载)

    组织并执行一系列的操作或者活动的最自然的方式——那就是工作流——同时也是构造一个工作流程的可执行表现形式的最佳途径。
      Windows Workflow Foundation(以下简称WWF)提供了一个编程框架和工具以开发和执行各种不同的基于工作流的应用程序,比如文档管理、线型的商业应用、贸易单据流程、IT管理、B2B应用以及消费者应用。

    有状态的、持久化的、不间断运行的应用程序

      WWF简化了创造有状态的,不间断运行的异步工作流应用程序的过程。WWF运行时引擎管理工作流的运行,为工作流的长期运行提供保障,并能抵抗机器的重启。WWF运行时服务提供了一系列的附加功能,例如WWF服务为能温和且正确的处理错误提供了事务和持久化。

    工作流模型

      WWF为开发人员提供了一个工作流模型,来描述应用程序所需要的处理过程。通过使用工作流模型所提供的流程控件、状态管理、事务和同步器,开发人员可以分离应用程序逻辑和业务逻辑,构造一个高层次的抽象,达到提高开发者效率的目的。

    组件的重用

      WWF为开发者提供了一系列的活动——活动是一种包含了工作单元的可配置逻辑结构。这种结构封装了开发者可能经常性用到的一些部件,这样就节省了开发者的时间。
      如果遇到一些特殊的需求或场景,WWF同样为开发自定义的活动提供了简单的方法。
      通过将工作流引擎载入进程,WWF可以使任何应用程序和服务容器运行工作流。
      运行时服务组件被设计成可插件形式的,这个可使应用程序以最合适的方式来提供它们的服务。WWF还提供了一组运行时服务的默认实现,这些服务能满足大部分类型的应用程序。
     
    另外,WWF还提供了对ASP.NETout-of-the-box(啥意思?)支持,让构造和运行能在IISASP.NET环境的工作流变得简单。

     

     顺序工作流(sequential workflow是为执行一种由一系列预定义的步骤组成的任务而设计的。这种体系结构是模拟基于过程的应用程序的。这一节将用几个步骤来编写一个简单的开支报告程序,这个小程序使用WinFrom做界面,用顺序工作流做业务逻辑。

     这个小程序有一个TextBox来输入开支报告的总数,一个Button点击提交报告。工作流将评估开支,如果开支小于1000则提请领班审批,如果大于等于1000则提请经理审批。之后,工作流会发送一个审批意见,此时,出现一个Label显示审批意见,两个Button分别表示通过和拒绝审批。当某一个按钮被点击的时候,应用程序会通知回应工作流,工作流继续处理发生的事件。

     

    开始构造顺序工作流

     

    创建工作流类

      WWF SDK中定义了一个SequentialWorkFlow类,我们定义一个ExpenseRoportWorkflow类,并继承自SequentialWorkflow,这样就创建一个顺序工作流。如:

     

     1using System;
     2using
     System.Workflow.Activities;
     3using
     System.Workflow.Activities.Rules;
     4

     5namespace
     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集合中。

    使用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
     7this.EvaluateExpenseReportAmount.Activities.Add(this.ifNeedsLeadApproval);
     8

     9this.EvaluateExpenseReportAmount.Activities.Add(this
    .elseNeedsManagerApproval);
    10

    11this.EvaluateExpenseReportAmount.ID = "EvaluateExpenseReportAmount"
    ;
    12

    13//
     
    14

    15//
     ifNeedsLeadApproval
    16

    17// 

    18
    19this.ifNeedsLeadApproval.Activities.Add(this.invokeGetLeadApproval);
    20

    21ifElseLogicStatement.Condition += new System.Workflow.Activities.ConditionalExpression(this
    .DetermineApprovalContact);
    22

    23this.ifNeedsLeadApproval.Condition =
     ifElseLogicStatement;
    24

    25this.ifNeedsLeadApproval.ID = "ifNeedsLeadApproval"
    ;
    26

    27//
     
    28

    29//
     elseNeedsManagerApproval
    30

    31// 

    32
    33this.elseNeedsManagerApproval.Activities.Add(this.invokeGetManagerApproval);
    34

    35this.elseNeedsManagerApproval.Condition = null
    ;
    36

    37this.elseNeedsManagerApproval.ID = "elseNeedsManagerApproval"
    ;
    38

     WWFIfElse活动中,有两种评估条件表达式的方式。一种是RoleCondition,这个对象通过使用一组规则来判断条件表达式的结果;另一种就是使用CodeCondition活动。CodeCondition使用一个回调方法,这个回调方法返回一个代表评估结果的布尔值。上面的例子就是使用CodeCondition来决定条件表达式的值。如果Amount参数小于1000,回调方法返回true,否则返回false。以下的代码就是这个回调函数的定义,你可以把它加到工作流类的定义中。

     1private 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
     7this.invokeGetLeadApproval.ID = "invokeGetLeadApproval";
     8

     9this.invokeGetLeadApproval.InterfaceType = typeof
    (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
    10

    11this.invokeGetLeadApproval.MethodName = "GetLeadApproval"
    ;   
    12

    13//
     
    14

    15//
     invokeGetManagerApproval
    16

    17// 

    18
    19this.invokeGetManagerApproval.ID = "invokeGetManagerApproval";
    20

    21this.invokeGetManagerApproval.InterfaceType = typeof
    (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
    22

    23this.invokeGetManagerApproval.MethodName = "GetManagerApproval"
    ;
    24

    25

     以下代码定义了IExpenseReportService接口

     1using System;
     2

     3using
     System.Workflow.ComponentModel;
     4

     5using
     System.Workflow.Runtime.Messaging;
     6

     7
     
     8

     9namespace
     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活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个approvalrejection事件被引发,工作流继续执行,返回审批结果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
     7this.listenApproveReject.Activities.Add(this.approveEventDriven);
     8

     9this.listenApproveReject.Activities.Add(this
    .rejectEventDriven);
    10

    11this.listenApproveReject.ID = "listenApproveReject"
    ;
    12

    13//
     
    14

    15//
     approveEventDriven
    16

    17// 

    18
    19this.approveEventDriven.Activities.Add(this.approveEvent);
    20

    21this.approveEventDriven.ID = "approveEventDriven"
    ;
    22

    23//
     
    24

    25//
     approveEvent
    26

    27// 

    28
    29this.approveEvent.EventName = "ExpenseReportApproved";
    30

    31this.approveEvent.ID = "approveEvent"
    ;
    32

    33this.approveEvent.InterfaceType = typeof
    (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
    34

    35this.approveEvent.Roles = null
    ;
    36

    37this.approveEvent.Invoked += new System.EventHandler(this
    .approveEvent_Invoked);
    38

    39//
     
    40

    41//
     rejectEventDriven
    42

    43// 

    44
    45this.rejectEventDriven.Activities.Add(this.rejectEvent);
    46

    47this.rejectEventDriven.ID = "rejectEventDriven"
    ;
    48

    49//
     
    50

    51//
     rejectEvent
    52

    53// 

    54
    55this.rejectEvent.EventName = "ExpenseReportRejected";
    56

    57this.rejectEvent.ID = "rejectEvent"
    ;
    58

    59this.rejectEvent.InterfaceType = typeof
    (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
    60

    61this.rejectEvent.Roles = null
    ;
    62

    63this.rejectEvent.Invoked += new System.EventHandler(this
    .rejectEvent_Invoked);
    64

    65

    使用EventSinkActivity时,为了在工作流中加入一些附加逻辑,你可以为Invoked事件创建一个事件处理程序。一下是事件处理程序的代码

     1private void approveEvent_Invoked(object sender, EventArgs e)
     2

     3
    {
     4

     5    this.Parameters["Result"].Value = "Report Approved"
    ;
     6

     7}

     8
     9
     
    10

    11private 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
     7this.Activities.Add(this.EvaluateExpenseReportAmount);
     8

     9this.Activities.Add(this
    .listenApproveReject);
    10

    11this.DynamicUpdateCondition = null
    ;
    12

    13this.ID = "ExpenseReportWorkflow"
    ;
    14

    15

    创建宿主程序

     

    WWF需要一个宿主程序来运行工作流。当程序开始运行,WWF运行时引擎也随之启动。而之前构造好的工作流,则到用户点击了Submit按钮后才真正启动。

    建立一个新的源文件,取名Program。以下的代码包含了完整的WinForm应用程序。IExpenseReportService接口GetLeadApprovalGetmanagerApproval方法已经定义在另一个文件中。宿主程序实现了这个接口。在approvalrejected按钮的click事件中,将引发ExpenseReprotApprovalExpenseReprotRejected事件。

      1using System;
      2

      3using
     System.ComponentModel;
      4

      5using
     System.Drawing;
      6

      7using
     System.Windows.Forms;
      8

      9using
     System.Collections.Generic;
     10

     11using
     System.Workflow.Runtime;
     12

     13using
     System.Workflow.Runtime.Hosting;
     14

     15using
     System.Workflow.Runtime.Messaging;
     16

     17
     
     18

     19namespace
     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(1313
    );
    140

    141            this.label1.Name = "label1"
    ;
    142

    143            this.label1.Size = new System.Drawing.Size(3913
    );
    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(1369);
    156

    157            this.result.Name = "result"
    ;
    158

    159            this.result.ReadOnly = true
    ;
    160

    161            this.result.Size = new System.Drawing.Size(16220
    );
    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(1354
    );
    176

    177            this.label2.Name = "label2"
    ;
    178

    179            this.label2.Size = new System.Drawing.Size(3313
    );
    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(5695
    );
    194

    195            this.submitButton.Name = "submitButton"
    ;
    196

    197            this.submitButton.Size = new System.Drawing.Size(7523
    );
    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(109
    );
    214

    215            this.approvalState.Name = "approvalState"
    ;
    216

    217            this.approvalState.Size = new System.Drawing.Size(4513
    );
    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(1125
    );
    232

    233            this.approveButton.Name = "approveButton"
    ;
    234

    235            this.approveButton.Size = new System.Drawing.Size(7523
    );
    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(8625
    );
    252

    253            this.rejectButton.Name = "rejectButton"
    ;
    254

    255            this.rejectButton.Size = new System.Drawing.Size(7523
    );
    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(1329);
    270

    271            this.amount.MaxLength = 9
    ;
    272

    273            this.amount.Name = "amount"
    ;
    274

    275            this.amount.Size = new System.Drawing.Size(16220
    );
    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(3124
    );
    296

    297            this.panel1.Name = "panel1"
    ;
    298

    299            this.panel1.Size = new System.Drawing.Size(17266
    );
    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(187201
    );
    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<stringobject> properties = new Dictionary<stringobject>();
    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(nullnew 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(nullnew 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

    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)注册到工作流中了。运行时,引擎就遍历所有已经注册的服务,如果实现了这个接口,这调用它。

    另外,注册服务不一定要用AddService,也可以用WorkflowRuntime.StartWorkflowType)。


    来自 http://windwolfreal.cnblogs.com/archive/2005/12/18/299378.html 

  • 相关阅读:
    Python之操作MySQL数据库
    Python之进程与线程
    Python之socket网络编程
    网络基础之网络协议
    Python之log的处理方式
    11 python 操作mysql数据库
    10 线程 协程 socketserver 基于udp的socketserver
    9 异常处理 操作系统 进程线程 队列+生产消费者模型 进程同步 回调函数
    6 常用模块 (time,random,os,sys,shutil, json&pickle, shelve,xml,configparser,hashlib,suprocess,logging)
    ubuntu16上安装安装Docker图形化管理界面-Shipyard中文版
  • 原文地址:https://www.cnblogs.com/ghd258/p/299437.html
Copyright © 2011-2022 走看看