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,这样就创建一个顺序工作流。如:
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集合中。
1public ExpenseReportWorkflow()
  2
  3{
  4
  5    InitializeComponent();
  6
  7}
  8
  9 
 10
 11private 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 = ;
 26
 27     = "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
  7this.EvaluateExpenseReportAmount.Activities.Add(this.ifNeedsLeadApproval);
  8
  9this.EvaluateExpenseReportAmount.Activities.Add(this.elseNeedsManagerApproval);
 10
  = "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
  = "ifNeedsLeadApproval";
 26
 27// 
 28
 29// elseNeedsManagerApproval
 30
 31// 
 32
 33this.elseNeedsManagerApproval.Activities.Add(this.invokeGetManagerApproval);
 34
 35this.elseNeedsManagerApproval.Condition = null;
 36
  = "elseNeedsManagerApproval";
 38
  
WWF在IfElse活动中,有两种评估条件表达式的方式。一种是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
   = "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
  = "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活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个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
  7this.listenApproveReject.Activities.Add(this.approveEventDriven);
  8
  9this.listenApproveReject.Activities.Add(this.rejectEventDriven);
 10
  = "listenApproveReject";
 12
 13// 
 14
 15// approveEventDriven
 16
 17// 
 18
 19this.approveEventDriven.Activities.Add(this.approveEvent);
 20
  = "approveEventDriven";
 22
 23// 
 24
 25// approveEvent
 26
 27// 
 28
 29this.approveEvent.EventName = "ExpenseReportApproved";
 30
  = "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
  = "rejectEventDriven";
 48
 49// 
 50
 51// rejectEvent
 52
 53// 
 54
 55this.rejectEvent.EventName = "ExpenseReportRejected";
 56
  = "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
  = "ExpenseReportWorkflow";
 14
 15
  
创建宿主程序
WWF需要一个宿主程序来运行工作流。当程序开始运行,WWF运行时引擎也随之启动。而之前构造好的工作流,则到用户点击了Submit按钮后才真正启动。
建立一个新的源文件,取名Program。以下的代码包含了完整的WinForm应用程序。IExpenseReportService接口GetLeadApproval和GetmanagerApproval方法已经定义在另一个文件中。宿主程序实现了这个接口。在approval和rejected按钮的click事件中,将引发ExpenseReprotApproval或ExpenseReprotRejected事件。
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(13, 13);
 140
 141             = "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             = "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             = "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             = "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             = "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             = "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";
 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             = "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             = "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)注册到工作流中了。运行时,引擎就遍历所有已经注册的服务,如果实现了这个接口,这调用它。
另外,可以注册一个以上的服务,工作流引擎将调用所有实现了相应接口的接口方法。
 
 
                     
            
        













 
                    

 
                 
                    