组织并执行一系列的操作或者活动的最自然的方式——那就是工作流——同时也是构造一个工作流程的可执行表现形式的最佳途径。
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,这样就创建一个顺序工作流。如:
1 using System;
2 using System.Workflow.Activities;
3 using System.Workflow.Activities.Rules;
4 5 namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow
6 {
7 [RuleConditionsAttribute(
typeof (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.ExpenseReportWorkflow))]
8 public sealed partial
class ExpenseReportWorkflow : System.Workflow.Activities.SequentialWorkflow
9 {
10 public ExpenseReportWorkflow()
11 {
12 13 }
14 }
15 }
16 声明工作流参数
在一个工作流运行时,它可以从宿主应用程序中接收参数。参数是ParameterDeclaration类型的对象,一旦工作流初始化完成,参数的值就能通过工作流的Parameters集合来访问。
这里的开始报告程序用了两个参数。第一个参数是开支的总数;第二个是一个传出参数,用来放置审批意见。
定义一个新的方法InitializeComponent,并在构造ExpenseRoportWorkflow类的构造函数中调用它。一下的例子示范了怎样定义两个参数并把它们加到Parameters集合中。
1 public ExpenseReportWorkflow()
2 3 {
4 5 InitializeComponent();
6 7 }
8 9 10 11 private void InitializeComponent()
12 13 {
14 15 System.Workflow.ComponentModel.ParameterDeclaration Amount =
new System.Workflow.ComponentModel.ParameterDeclaration();
16 17 System.Workflow.ComponentModel.ParameterDeclaration Result =
new System.Workflow.ComponentModel.ParameterDeclaration();
18 19 // 20 21 // Workflow Parameters 22 23 // 24 25 Amount.Direction = System.Workflow.ComponentModel.ParameterDirection.In;
26 27 Amount.Name = "Amount";
28 29 Amount.Type =
typeof(
int);
30 31 Amount.Value =
null;
32 33 Result.Direction = System.Workflow.ComponentModel.ParameterDirection.Out;
34 35 Result.Name = "Result";
36 37 Result.Type =
typeof(
string);
38 39 Result.Value =
null;
40 41 this.Parameters.Add(Amount);
42 43 this.Parameters.Add(Result);
44 45 }
46 47 使用IfElse活动
IfElse活动用条件表达式来控制工作流中流程的运行。工作流将根据条件表达式的结果来决定执行条件分支(IfElseBranch)中的哪一个活动。
例子中将使用IfElse活动。通过判断从宿主应用程序中传入的Amount参数的值是否小于1000,来决定是否将审报发送到领班,否则发送到经理。
创建IfElse活动
1.定义4个私有变量
类型 | 名称 |
IfElse | evaluateExpenseReportAmount |
IfElseBranch | ifNeedsLeadApproval |
IfElseBranch | elseNeedsManagerApproval |
CodeCondition | ifElseLogicStatement |
2.在InitializeComponent中用默认构造函数实例以上4个对象。
以下的代码示例了怎样创建IfElse活动,并用IfElseBranch活动联系两个逻辑分支。你需要把以下代码放到InitializeComponent方法底部。
1 // 2 3 // EvaluateExpenseReportAmount 4 5 // 6 7 this.EvaluateExpenseReportAmount.Activities.Add(
this .ifNeedsLeadApproval);
8 9 this.EvaluateExpenseReportAmount.Activities.Add(
this .elseNeedsManagerApproval);
10 11 this.EvaluateExpenseReportAmount.ID = "EvaluateExpenseReportAmount" ;
12 13 // 14 15 // ifNeedsLeadApproval 16 17 // 18 19 this.ifNeedsLeadApproval.Activities.Add(
this .invokeGetLeadApproval);
20 21 ifElseLogicStatement.Condition +=
new System.Workflow.Activities.ConditionalExpression(
this .DetermineApprovalContact);
22 23 this.ifNeedsLeadApproval.Condition = ifElseLogicStatement;
24 25 this.ifNeedsLeadApproval.ID = "ifNeedsLeadApproval" ;
26 27 // 28 29 // elseNeedsManagerApproval 30 31 // 32 33 this.elseNeedsManagerApproval.Activities.Add(
this .invokeGetManagerApproval);
34 35 this.elseNeedsManagerApproval.Condition =
null ;
36 37 this.elseNeedsManagerApproval.ID = "elseNeedsManagerApproval" ;
38 WWF在IfElse活动中,有两种评估条件表达式的方式。一种是RoleCondition,这个对象通过使用一组规则来判断条件表达式的结果;另一种就是使用CodeCondition活动。CodeCondition使用一个回调方法,这个回调方法返回一个代表评估结果的布尔值。上面的例子就是使用CodeCondition来决定条件表达式的值。如果Amount参数小于1000,回调方法返回true,否则返回false。以下的代码就是这个回调函数的定义,你可以把它加到工作流类的定义中。
1 private bool DetermineApprovalContact(
object sender, EventArgs e)
2 3 {
4 5 if ( Convert.ToInt32(
this.Parameters["Amount"].Value) < 1000 )
6 7 return true ;
8 9 10 11 return false ;
12 13 }
14 构造IfElse分支(IfElseBranch)活动
创建完IfElse活动之后,我们来构造IfElseBranch活动
在这个例子中,每一IfElse活动的分支都使用InvokeMethodActivity活动来通知宿主程序——工作流需要领班或经理的审批才能继续执行。InvokeMethodActivity被设计成调用一个在WWF运行时中的服务接口。我们在同一份代码文件中定义了这个接口。当我们在之后构造宿主程序时,宿主类将实现这个接口,以便能建立工作流和宿主程序的通信(这一段文档上写的很模糊,我reflect后看了源码才明白过来,在最后将补充描述一下)。
构建IfElseBranch活动 1. 在类中定义两个私有字段
类型 | 名称 |
InvokeMethodActivity | invokeGetLeadApproval |
InvokeMethodActivity | invokeGetManagerApproval |
2. 在InitializeComponent中用默认构造函数实例化这两个对象
以下的代码示例了怎样在父活动(IfElse)中创建IfElseBranch活动,并把两个的InvokeMethodActivity联系到对应的IfElseBranch活动上,每个InvokeMethodActivity将调用定义在IExpenseReportService接口中的方法,接口会在稍微实现。你需要把以下代码放到InitializeComponent方法底部。
1 // 2 3 // invokeGetLeadApproval 4 5 // 6 7 this.invokeGetLeadApproval.ID = "invokeGetLeadApproval" ;
8 9 this.invokeGetLeadApproval.InterfaceType =
typeof (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
10 11 this.invokeGetLeadApproval.MethodName = "GetLeadApproval" ;
12 13 // 14 15 // invokeGetManagerApproval 16 17 // 18 19 this.invokeGetManagerApproval.ID = "invokeGetManagerApproval" ;
20 21 this.invokeGetManagerApproval.InterfaceType =
typeof (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
22 23 this.invokeGetManagerApproval.MethodName = "GetManagerApproval" ;
24 25 以下代码定义了IExpenseReportService接口
1 using System;
2 3 using System.Workflow.ComponentModel;
4 5 using System.Workflow.Runtime.Messaging;
6 7 8 9 namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow
10 11 {
12 13 [DataExchangeService]
14 15 public interface IExpenseReportService
16 17 {
18 19 void GetLeadApproval();
20 21 void GetManagerApproval();
22 23 event EventHandler<WorkflowMessageEventArgs> ExpenseReportApproved;
24 25 event EventHandler<WorkflowMessageEventArgs> ExpenseReportRejected;
26 27 }
28 29 }
30 31 监听宿主事件
在这个阶段,工作流已经从宿主程序接受了两个参数(译者注:其中一个为out参数,此时设为null),评估了Amount参数,作出了到底该提请谁确认审批的决定,并通知了宿主程序在继续接下来的处理之前,确认审批。这里,Listen活动和EventSinkActivity活动往往配合使用,来监听宿主程序触发指定的事件。接着,一个approval或rejection事件被引发,工作流继续执行,返回审批结果Result,并终止流程。
Listen活动的每个分支是一个EventDriven活动。EventDriven活动只能使用实现了IEventActivity接口的活动。Listen活动的每个分支中的EventDriven各有一个EventSinkActivity,它们是用来监听宿主程序触发的ExpenseReportApproved或者ExpenseReportRejected事件的。这种工作流和宿主的通信方法其实类似于之前的InvokeMethodActivity的过程,只不过前者是工作流监听宿主事件,而后者是宿主事件工作流中注册的接口。
我们已经在前面的步骤中,把接口和两个事件的定义都完成了。在这里,我们将创建一个Listen活动并和两个EventDriven分支建立连接。每个分支包含一个EventSinkActivity活动,每个EventSink监听一种对应的事件。此外,我们还将创建一些事件处理程序,来处理AfterInvoke事件(译者注:这里的AfterInvoke事件应为Invoked事件)。这些事件处理程序将会把Result参数的值设为approval或者rejected。
构造监听活动
1.在工作流类中定义5个私有字段
类型 | 名称 |
Listen | listenApproveReject |
EventDriven | approveEventDriven |
EventDriven | rejectEventDriven |
EventSinkActivity | approveEvent |
EventSinkActivity | rejectEvent |
2. 在InitializeComponent中实例化。
以下的代码示例了怎样在创建Listen活动和EventSinkActivity活动,来监听宿主程序发出的事件。你需要把以下代码放到InitializeComponent方法底部。
1 // 2 3 // listenApproveReject 4 5 // 6 7 this.listenApproveReject.Activities.Add(
this .approveEventDriven);
8 9 this.listenApproveReject.Activities.Add(
this .rejectEventDriven);
10 11 this.listenApproveReject.ID = "listenApproveReject" ;
12 13 // 14 15 // approveEventDriven 16 17 // 18 19 this.approveEventDriven.Activities.Add(
this .approveEvent);
20 21 this.approveEventDriven.ID = "approveEventDriven" ;
22 23 // 24 25 // approveEvent 26 27 // 28 29 this.approveEvent.EventName = "ExpenseReportApproved" ;
30 31 this.approveEvent.ID = "approveEvent" ;
32 33 this.approveEvent.InterfaceType =
typeof (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
34 35 this.approveEvent.Roles =
null ;
36 37 this.approveEvent.Invoked +=
new System.EventHandler(
this .approveEvent_Invoked);
38 39 // 40 41 // rejectEventDriven 42 43 // 44 45 this.rejectEventDriven.Activities.Add(
this .rejectEvent);
46 47 this.rejectEventDriven.ID = "rejectEventDriven" ;
48 49 // 50 51 // rejectEvent 52 53 // 54 55 this.rejectEvent.EventName = "ExpenseReportRejected" ;
56 57 this.rejectEvent.ID = "rejectEvent" ;
58 59 this.rejectEvent.InterfaceType =
typeof (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService);
60 61 this.rejectEvent.Roles =
null ;
62 63 this.rejectEvent.Invoked +=
new System.EventHandler(
this .rejectEvent_Invoked);
64 65 使用EventSinkActivity时,为了在工作流中加入一些附加逻辑,你可以为Invoked事件创建一个事件处理程序。一下是事件处理程序的代码
1 private void approveEvent_Invoked(
object sender, EventArgs e)
2 3 {
4 5 this.Parameters["Result"].Value = "Report Approved" ;
6 7 }
8 9 10 11 private void rejectEvent_Invoked(
object sender, EventArgs e)
12 13 {
14 15 this.Parameters["Result"].Value = "Report Rejected" ;
16 17 }
18 19 完成顺序工作流 这个工作流包括两个主要的步骤:第一,监听宿主程序的递交审批事件,并把传入的值作为工作流参数;第二,监听通过或拒绝审批消息。一下的代码示例了怎样通过把之前创建好的活动加到工作流活动集中,来完成工作流的构造。
1 // 2 3 // ExpenseReportWorkflow 4 5 // 6 7 this.Activities.Add(
this .EvaluateExpenseReportAmount);
8 9 this.Activities.Add(
this .listenApproveReject);
10 11 this.DynamicUpdateCondition =
null ;
12 13 this.ID = "ExpenseReportWorkflow" ;
14 15 创建宿主程序
WWF需要一个宿主程序来运行工作流。当程序开始运行,WWF运行时引擎也随之启动。而之前构造好的工作流,则到用户点击了Submit按钮后才真正启动。
建立一个新的源文件,取名Program。以下的代码包含了完整的WinForm应用程序。IExpenseReportService接口GetLeadApproval和GetmanagerApproval方法已经定义在另一个文件中。宿主程序实现了这个接口。在approval和rejected按钮的click事件中,将引发ExpenseReprotApproval或ExpenseReprotRejected事件。
1 using System;
2 3 using System.ComponentModel;
4 5 using System.Drawing;
6 7 using System.Windows.Forms;
8 9 using System.Collections.Generic;
10 11 using System.Workflow.Runtime;
12 13 using System.Workflow.Runtime.Hosting;
14 15 using System.Workflow.Runtime.Messaging;
16 17 18 19 namespace Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflowHost
20 21 {
22 23 public class MainForm : Form, Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.IExpenseReportService
24 25 {
26 27 private System.Windows.Forms.Label label1;
28 29 private System.Windows.Forms.TextBox result;
30 31 private System.Windows.Forms.Label label2;
32 33 private System.Windows.Forms.Button submitButton;
34 35 private System.Windows.Forms.Label approvalState;
36 37 private System.Windows.Forms.Button approveButton;
38 39 private System.Windows.Forms.Button rejectButton;
40 41 private System.Windows.Forms.TextBox amount;
42 43 private System.Windows.Forms.Panel panel1;
44 45 46 47 private System.ComponentModel.IContainer components =
null ;
48 49 50 51 private delegate void GetApprovalDelegate();
52 53 private WorkflowRuntime workflowRuntime =
null ;
54 55 private WorkflowInstance workflowInstance =
null ;
56 57 58 59 public MainForm()
60 61 {
62 63 InitializeComponent();
64 65 66 67 // Collapse approve/reject panel 68 69 this.Height -=
this .panel1.Height;
70 71 72 73 workflowRuntime =
new WorkflowRuntime();
74 75 workflowRuntime.AddService(
this );
76 77 workflowRuntime.StartRuntime();
78 79 80 81 workflowRuntime.WorkflowCompleted +=
new EventHandler<WorkflowCompletedEventArgs> (workflowRuntime_WorkflowCompleted);
82 83 }
84 85 86 87 protected override void Dispose(
bool disposing)
88 89 {
90 91 if (disposing && (components !=
null ))
92 93 {
94 95 components.Dispose();
96 97 }
98 99 base .Dispose(disposing);
100 101 }
102 103 104 105 private void InitializeComponent()
106 107 {
108 109 this.label1 =
new System.Windows.Forms.Label();
110 111 this.result =
new System.Windows.Forms.TextBox();
112 113 this.label2 =
new System.Windows.Forms.Label();
114 115 this.submitButton =
new System.Windows.Forms.Button();
116 117 this.approvalState =
new System.Windows.Forms.Label();
118 119 this.approveButton =
new System.Windows.Forms.Button();
120 121 this.rejectButton =
new System.Windows.Forms.Button();
122 123 this.amount =
new System.Windows.Forms.TextBox();
124 125 this.panel1 =
new System.Windows.Forms.Panel();
126 127 this .panel1.SuspendLayout();
128 129 this .SuspendLayout();
130 131 // 132 133 // label1 134 135 // 136 137 this.label1.AutoSize =
true ;
138 139 this.label1.Location =
new System.Drawing.Point(13, 13 );
140 141 this.label1.Name = "label1" ;
142 143 this.label1.Size =
new System.Drawing.Size(39, 13 );
144 145 this.label1.TabIndex = 1 ;
146 147 this.label1.Text = "Amount" ;
148 149 // 150 151 // result 152 153 // 154 155 this.result.Location =
new System.Drawing.Point(13, 69 );
156 157 this.result.Name = "result" ;
158 159 this.result.ReadOnly =
true ;
160 161 this.result.Size =
new System.Drawing.Size(162, 20 );
162 163 this.result.TabIndex = 1 ;
164 165 this.result.TabStop =
false ;
166 167 // 168 169 // label2 170 171 // 172 173 this.label2.AutoSize =
true ;
174 175 this.label2.Location =
new System.Drawing.Point(13, 54 );
176 177 this.label2.Name = "label2" ;
178 179 this.label2.Size =
new System.Drawing.Size(33, 13 );
180 181 this.label2.TabIndex = 3 ;
182 183 this.label2.Text = "Result" ;
184 185 // 186 187 // submitButton 188 189 // 190 191 this.submitButton.Enabled =
false ;
192 193 this.submitButton.Location =
new System.Drawing.Point(56, 95 );
194 195 this.submitButton.Name = "submitButton" ;
196 197 this.submitButton.Size =
new System.Drawing.Size(75, 23 );
198 199 this.submitButton.TabIndex = 2 ;
200 201 this.submitButton.Text = "Submit" ;
202 203 this.submitButton.Click +=
new System.EventHandler(
this .submitButton_Click);
204 205 // 206 207 // approvalState 208 209 // 210 211 this.approvalState.AutoSize =
true ;
212 213 this.approvalState.Location =
new System.Drawing.Point(10, 9 );
214 215 this.approvalState.Name = "approvalState" ;
216 217 this.approvalState.Size =
new System.Drawing.Size(45, 13 );
218 219 this.approvalState.TabIndex = 4 ;
220 221 this.approvalState.Text = "Approval" ;
222 223 // 224 225 // approveButton 226 227 // 228 229 this.approveButton.Enabled =
false ;
230 231 this.approveButton.Location =
new System.Drawing.Point(11, 25 );
232 233 this.approveButton.Name = "approveButton" ;
234 235 this.approveButton.Size =
new System.Drawing.Size(75, 23 );
236 237 this.approveButton.TabIndex = 0 ;
238 239 this.approveButton.Text = "Approve" ;
240 241 this.approveButton.Click +=
new System.EventHandler(
this .approveButton_Click);
242 243 // 244 245 // rejectButton 246 247 // 248 249 this.rejectButton.Enabled =
false ;
250 251 this.rejectButton.Location =
new System.Drawing.Point(86, 25 );
252 253 this.rejectButton.Name = "rejectButton" ;
254 255 this.rejectButton.Size =
new System.Drawing.Size(75, 23 );
256 257 this.rejectButton.TabIndex = 1 ;
258 259 this.rejectButton.Text = "Reject" ;
260 261 this.rejectButton.Click +=
new System.EventHandler(
this .rejectButton_Click);
262 263 // 264 265 // amount 266 267 // 268 269 this.amount.Location =
new System.Drawing.Point(13, 29 );
270 271 this.amount.MaxLength = 9 ;
272 273 this.amount.Name = "amount" ;
274 275 this.amount.Size =
new System.Drawing.Size(162, 20 );
276 277 this.amount.TabIndex = 0 ;
278 279 this.amount.KeyPress +=
new System.Windows.Forms.KeyPressEventHandler(
this .amount_KeyPress);
280 281 this.amount.TextChanged +=
new System.EventHandler(
this .amount_TextChanged);
282 283 // 284 285 // panel1 286 287 // 288 289 this.panel1.Controls.Add(
this .approvalState);
290 291 this.panel1.Controls.Add(
this .approveButton);
292 293 this.panel1.Controls.Add(
this .rejectButton);
294 295 this.panel1.Location =
new System.Drawing.Point(3, 124 );
296 297 this.panel1.Name = "panel1" ;
298 299 this.panel1.Size =
new System.Drawing.Size(172, 66 );
300 301 this.panel1.TabIndex = 8 ;
302 303 // 304 305 // MainForm 306 307 // 308 309 this.AcceptButton =
this .submitButton;
310 311 this.AutoScaleDimensions =
new System.Drawing.SizeF(6F, 13F);
312 313 this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
314 315 this.ClientSize =
new System.Drawing.Size(187, 201 );
316 317 this.Controls.Add(
this .panel1);
318 319 this.Controls.Add(
this .amount);
320 321 this.Controls.Add(
this .submitButton);
322 323 this.Controls.Add(
this .label2);
324 325 this.Controls.Add(
this .result);
326 327 this.Controls.Add(
this .label1);
328 329 this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
330 331 this.MaximizeBox =
false ;
332 333 this.MinimizeBox =
false ;
334 335 this.Name = "MainForm" ;
336 337 this.Text = "Simple Expense Report" ;
338 339 this.panel1.ResumeLayout(
false );
340 341 this .panel1.PerformLayout();
342 343 this.ResumeLayout(
false );
344 345 this .PerformLayout();
346 347 348 349 }
350 351 352 353 private void submitButton_Click(
object sender, EventArgs e)
354 355 {
356 357 Type type =
typeof (Microsoft.Samples.Workflow.Quickstarts.SequentialWorkflow.ExpenseReportWorkflow);
358 359 360 361 // Construct workflow parameters 362 363 Dictionary<
string,
object> properties =
new Dictionary<
string,
object> ();
364 365 properties.Add("Amount", Int32.Parse(
this .amount.Text));
366 367 properties.Add("Result",
string .Empty);
368 369 370 371 // Start the workflow 372 373 workflowInstance = workflowRuntime.StartWorkflow(type, properties);
374 375 }
376 377 378 379 void workflowRuntime_WorkflowCompleted(
object sender, WorkflowCompletedEventArgs e)
380 381 {
382 383 if (
this .result.InvokeRequired)
384 385 this.result.Invoke(
new EventHandler<WorkflowCompletedEventArgs>(
this .workflowRuntime_WorkflowCompleted), sender, e);
386 387 else 388 389 {
390 391 this.result.Text = e.OutputParameters["Result" ].ToString();
392 393 394 395 // Clear fields 396 397 this.amount.Text =
string .Empty;
398 399 400 401 // Disable buttons 402 403 this.approveButton.Enabled =
false ;
404 405 this.rejectButton.Enabled =
false ;
406 407 }
408 409 }
410 411 412 413 private void approveButton_Click(
object sender, EventArgs e)
414 415 {
416 417 // Raise the ExpenseReportApproved event back to the workflow 418 419 ExpenseReportApproved(
null,
new WorkflowMessageEventArgs(
this .workflowInstance.InstanceId));
420 421 this.Height -=
this .panel1.Height;
422 423 this.submitButton.Enabled =
true ;
424 425 }
426 427 428 429 private void rejectButton_Click(
object sender, EventArgs e)
430 431 {
432 433 // Raise the ExpenseReportRejected event back to the workflow 434 435 ExpenseReportRejected(
null,
new WorkflowMessageEventArgs(
this .workflowInstance.InstanceId));
436 437 this.Height -=
this .panel1.Height;
438 439 this.submitButton.Enabled =
true ;
440 441 }
442 443 444 445 IExpenseReportService Members #region IExpenseReportService Members
446 447 448 449 public void GetLeadApproval()
450 451 {
452 453 if (
this.approvalState.InvokeRequired)
454 455 this.approvalState.Invoke(
new GetApprovalDelegate(
this.GetLeadApproval));
456 457 else 458 459 {
460 461 this.approvalState.Text = "Lead approval needed";
462 463 this.approveButton.Enabled =
true;
464 465 this.rejectButton.Enabled =
true;
466 467 468 469 // expand the panel 470 471 this.Height +=
this.panel1.Height;
472 473 this.submitButton.Enabled =
false;
474 475 }
476 477 }
478 479 480 481 public void GetManagerApproval()
482 483 {
484 485 if (
this.approvalState.InvokeRequired)
486 487 this.approvalState.Invoke(
new GetApprovalDelegate(
this.GetManagerApproval));
488 489 else 490 491 {
492 493 this.approvalState.Text = "Manager approval needed";
494 495 this.approveButton.Enabled =
true;
496 497 this.rejectButton.Enabled =
true;
498 499 500 501 // expand the panel 502 503 this.Height +=
this.panel1.Height;
504 505 this.submitButton.Enabled =
false;
506 507 }
508 509 }
510 511 512 513 public event EventHandler<WorkflowMessageEventArgs> ExpenseReportApproved;
514 515 public event EventHandler<WorkflowMessageEventArgs> ExpenseReportRejected;
516 517 518 519 #endregion 520 521 522 523 private void amount_KeyPress(
object sender, KeyPressEventArgs e)
524 525 {
526 527 if (!Char.IsControl(e.KeyChar) && (! Char.IsDigit(e.KeyChar)))
528 529 e.KeyChar = Char.MinValue;
530 531 }
532 533 534 535 private void amount_TextChanged(
object sender, EventArgs e)
536 537 {
538 539 submitButton.Enabled = amount.Text.Length > 0 ;
540 541 }
542 543 }
544 545 546 547 static class Program
548 549 {
550 551 /**/ /// <summary> 552 553 /// The main entry point for the application. 554 555 /// </summary> 556 557 [STAThread]
558 559 static void Main()
560 561 {
562 563 Application.EnableVisualStyles();
564 565 Application.Run(
new MainForm());
566 567 }
568 569 }
570 571 }
572 573
Ps:上面还有个问题没有解释清楚,我reflect了一下,看了源码才知道个大概。
那就是IfElseBranch中的InvokeMethodActivity。
InvokeMethodActivity中有一个Type类型的InterfaceType属性,使用时,需要设置这个属性,并把MethodName设为这个接口中的一个方法的名称。运行时,工作流引擎(也就是WorkflowRuntime)将通过反射调用这个接口。
但引擎怎么知道调用接口的哪个实现呢?你看
workflowRuntime = new WorkflowRuntime();
workflowRuntime.AddService(this);
workflowRuntime.StartRuntime();
原来,初始化引擎时,我们已经把实现了这个interface的类型的实例(this)注册到工作流中了。运行时,引擎就遍历所有已经注册的服务,如果实现了这个接口,这调用它。
另外,注册服务不一定要用AddService,也可以用WorkflowRuntime.StartWorkflow(Type)。
本文转自高海东博客园博客,原文链接:http://www.cnblogs.com/ghd258/archive/2005/12/18/299437.html,如需转载请自行联系原作者