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环境的工作流变得简单。

顺序工作流(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活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个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

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接口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 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

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)。




本文转自高海东博客园博客,原文链接:http://www.cnblogs.com/ghd258/archive/2005/12/18/299437.html,如需转载请自行联系原作者

相关文章
|
3月前
|
vr&ar C# 图形学
WPF与AR/VR的激情碰撞:解锁Windows Presentation Foundation应用新维度,探索增强现实与虚拟现实技术在现代UI设计中的无限可能与实战应用详解
【8月更文挑战第31天】增强现实(AR)与虚拟现实(VR)技术正迅速改变生活和工作方式,在游戏、教育及工业等领域展现出广泛应用前景。本文探讨如何在Windows Presentation Foundation(WPF)环境中实现AR/VR功能,通过具体示例代码展示整合过程。尽管WPF本身不直接支持AR/VR,但借助第三方库如Unity、Vuforia或OpenVR,可实现沉浸式体验。例如,通过Unity和Vuforia在WPF中创建AR应用,或利用OpenVR在WPF中集成VR功能,从而提升用户体验并拓展应用功能边界。
64 0
|
3月前
|
开发者 C# Windows
WPF与游戏开发:当桌面应用遇见游戏梦想——利用Windows Presentation Foundation打造属于你的2D游戏世界,从环境搭建到代码实践全面解析新兴开发路径
【8月更文挑战第31天】随着游戏开发技术的进步,WPF作为.NET Framework的一部分,凭借其图形渲染能力和灵活的UI设计,成为桌面游戏开发的新选择。本文通过技术综述和示例代码,介绍如何利用WPF进行游戏开发。首先确保安装最新版Visual Studio并创建WPF项目。接着,通过XAML设计游戏界面,并在C#中实现游戏逻辑,如玩家控制和障碍物碰撞检测。示例展示了创建基本2D游戏的过程,包括角色移动和碰撞处理。通过本文,WPF开发者可更好地理解并应用游戏开发技术,创造吸引人的桌面游戏。
171 0
|
3月前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
226 0
|
3月前
|
存储 开发者 C#
WPF与邮件发送:教你如何在Windows Presentation Foundation应用中无缝集成电子邮件功能——从界面设计到代码实现,全面解析邮件发送的每一个细节密武器!
【8月更文挑战第31天】本文探讨了如何在Windows Presentation Foundation(WPF)应用中集成电子邮件发送功能,详细介绍了从创建WPF项目到设计用户界面的全过程,并通过具体示例代码展示了如何使用`System.Net.Mail`命名空间中的`SmtpClient`和`MailMessage`类来实现邮件发送逻辑。文章还强调了安全性和错误处理的重要性,提供了实用的异常捕获代码片段,旨在帮助WPF开发者更好地掌握邮件发送技术,提升应用程序的功能性与用户体验。
55 0
|
C# Windows
Windows Presentation Foundation (WPF)中的命令(Commands)简述
原文:Windows Presentation Foundation (WPF)中的命令(Commands)简述 -----------------------------------------------------...
827 0
|
Windows
你还记得windows workflow foundation吗
很多年前,windows workflow foundation还叫WWF,而直译过来的名称让很多人以为它就是用来开发工作流或者干脆就是审批流的。 博主当年还是个懵懂的少年,却也知道微软不会大力推一个面向如此具象的业务场景的技术,于是特地找了一本《WF本质论》,当看到“程序即数据”这个论断时,被深深震撼了。
1185 0
下一篇
无影云桌面