天天看點

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,這樣就建立一個順序工作流。如:

   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集合中。 使用IfElse活動

 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活動用條件表達式來控制工作流中流程的運作。工作流将根據條件表達式的結果來決定執行條件分支(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

 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

 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活動的分支都使用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

 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

 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方法底部。使用EventSinkActivity時,為了在工作流中加入一些附加邏輯,你可以為Invoked事件建立一個事件處理程式。一下是事件處理程式的代碼

完成順序工作流  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

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

繼續閱讀