Windows Workflow Foundation(一)

组织并执行一系列的操作或者活动的最自然的方式——那就是工作流——同时也是构造一个工作流程的可执行表现形式的最佳途径。
  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环境的工作流变得简单。
 
 顺序工作流(sequentialworkflow是为执行一种由一系列预定义的步骤组成的任务而设计的。这种体系结构是模拟基于过程的应用程序的。这一节将用几个步骤来编写一个简单的开支报告程序,这个小程序使用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方法底部。 WWF在IfElse活动中,有两种评估条件表达式的方式。一种是RoleCondition,这个对象通过使用一组规则来判断条件表达式的结果;另一种就是使用CodeCondition活动。CodeCondition使用一个回调方法,这个回调方法返回一个代表评估结果的布尔值。上面的例子就是使用CodeCondition来决定条件表达式的值。如果Amount参数小于1000,回调方法返回true,否则返回false。以下的代码就是这个回调函数的定义,你可以把它加到工作流类的定义中。构造IfElse分支(IfElseBranch)活动
 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
 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活动的分支都使用InvokeMethodActivity活动来通知宿主程序——工作流需要领班或经理的审批才能继续执行。InvokeMethodActivity被设计成调用一个在WWF运行时中的服务接口。我们在同一份代码文件中定义了这个接口。当我们在之后构造宿主程序时,宿主类将实现这个接口,以便能建立工作流和宿主程序的通信(这一段文档上写的很模糊,我reflect后看了源码才明白过来,在最后将补充描述一下)。

    构建IfElseBranch活动
 1. 在类中定义两个私有字段

类型
名称
InvokeMethodActivity
invokeGetLeadApproval
InvokeMethodActivity
invokeGetManagerApproval

 2. 在InitializeComponent中用默认构造函数实例化这两个对象
 以下的代码示例了怎样在父活动(IfElse)中创建IfElseBranch活动,并把两个的InvokeMethodActivity联系到对应的IfElseBranch活动上,每个InvokeMethodActivity将调用定义在IExpenseReportService接口中的方法,接口会在稍微实现。你需要把以下代码放到InitializeComponent方法底部。 以下代码定义了IExpenseReportService接口监听宿主事件
 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
 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方法底部。使用EventSinkActivity时,为了在工作流中加入一些附加逻辑,你可以为Invoked事件创建一个事件处理程序。一下是事件处理程序的代码
完成顺序工作流
 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
 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接口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(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.StartWorkflow(Type)。
 
英文版 Windows Workflow Foundation (WF) is a revolutionary part of the .NET 4 Framework that allows you to orchestrate human and system interactions as a series of workflows that can be easily mapped, analyzed, adjusted, and implemented. As business problems become more complex, the need for workflow-based solutions has never been more evident. WF provides a simple and consistent way to model and implement complex problems. As a developer, you focus on developing the business logic for individual workflow tasks. The runtime handles the execution of those tasks after they have been composed into a workflow. Pro WF: Windows Workflow in .NET 4 provides you with the skills you need to incorporate WF in your applications, using a lively tutorial style with each example illustrated in C#. This book gets you up to speed with WF 4 quickly and comprehensively. Learn about WF 4s new designer, its updated programming paradigm, and the completely new set of activities that can enable and extend your workflows. This book also includes detailed coverage of how to customize your workflows and access them in a variety of ways and situations so you can maximize the advantages of this technology. What youll learn * WF 4 basics * New activities and changes to existing activities in WF 4 * Customizing your workflows * Accessing your workflows in a variety of ways in a variety of situations * Using WF with Web Services and ASP.NET * Integrating WCF and WF Who this book is for This book is for intermediate to advanced .NET developers who need to learn how to use the latest version of Windows Workflow Foundation (WF 4). This book is not a primer on .NET or the C# language. To get the most out of the examples presented in this book, it is necessary to have a good working knowledge of .NET 2.0 or higher. All of the examples are presented in C#. Table of Contents * A Quick Tour of Windows Workflow Foundation * Foundation Overview * Activities * Workflow Hosting * Procedural Flow Control * Collection-Related Activities * Flowchart Modeling Style * Host Communication * Workflow Services * Workflow Services Advanced Topics * Workflow Persistence * Customizing Workflow Persistence * Transactions, Compensation, and Exception Handling * Workflow Tracking * Enhancing the Design Experience * Advanced Custom Activities * Hosting the Workflow Designer * WF 3.x Interop and Migration
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值