65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Visual Studio 2005 中创建顺序工作流 - 示例

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.25/5 (7投票s)

2007年6月2日

CPOL

10分钟阅读

viewsIcon

62630

本文介绍了使用 .NET Framework 3.0 中的 Windows Workflow Foundation 在 Visual Studio 2005 中创建顺序工作流的步骤。

作者笔记

这篇分三部分的文章是 MSDN 库中关于创建顺序工作流教程的改编版( http://msdn2.microsoft.com/en-us/library/ms734794.aspx)。在此重新发布相同示例的主要目的是让读者更清晰地理解执行创建工作流的步骤。我的许多朋友和社区同行都表示对理解工作流创建过程中定义的步骤感到非常不满。他们引用了一些示例页面,这些页面非常不精确,不足以清楚地遵循这些步骤。

MSDN 教程包含以下 URL 中的三个练习:

在本文中,在保持主要练习不变的情况下,我详细解释了步骤、任务和编码部分,并包含了一些图示,以便任何人都可以轻松地开始创建顺序工作流。

引言

此示例应用程序是一个 **简单的费用报告**,包含一个用于输入金额的文本字段和一个用于提交费用报告的按钮。该工作流使用规则来评估金额,并在金额小于 1000 时要求主管批准,或者在金额大于或等于 1000 时要求经理批准。如果需要批准,工作流会与应用程序通信,并显示一个包含“批准”和“拒绝”按钮的下拉面板。当点击其中一个按钮时,应用程序会将响应通知工作流,工作流将继续处理该事件。

此演练包含三个主要步骤以及每个步骤下的子任务。

  1. 创建费用报告项目。
    • 创建 Windows 应用程序
    • 创建顺序工作流
    • 托管顺序工作流
  2. 创建费用报告服务。
    • 定义工作流参数
    • 定义 IExpenseReportService 接口
  3. 创建费用报告顺序工作流类库
    • 创建 CallExternalMethod 活动
    • 创建 HandleExternalEvent 活动

1. 创建费用报告项目

创建 Windows 应用程序

在 Visual Studio 2005 中,选择“新建项目”;在“C# 项目”类型下,选择“Windows 应用程序”。

将项目中出现的默认窗体的名称更改为“MainForm”,根据需要拖放控件,并按照下图所示设计布局。

Screenshot - pic1.jpg

下面是窗体中定义的控件及其属性的表格。

Control

属性

表单

名称

MainForm

文本

简单的费用报告

Label

名称

Label1

文本

金额

Label

名称

Label2

文本

结果

文本框

名称

amount

文本

文本框

名称

结果

文本

Label

名称

ApprovalState

文本

审批

Button

名称

submitButton

文本

提交

Button

名称

approveButton

文本

批准

Button

名称

rejectButton

文本

拒绝

Panel

名称

Panel1

approveButtonrejectButton 添加到 panel1 中。

为金额文本框的 KeyPressTextChanged 事件添加两个事件处理程序方法。

private void amount_KeyPress(object sender, KeyPressEventArgs e) 
{
    if (!Char.IsControl(e.KeyChar) && (!Char.IsDigit(e.KeyChar))) 
        e.KeyChar = Char.MinValue; 
} 
private void amount_TextChanged(object sender, EventArgs e) 
{ 
    submitButton.Enabled = amount.Text.Length > 0; 
}

确保已替换 Program.cs 中运行应用程序默认窗体的代码。

Application.Run(new MainForm());

最后,要完成第一步,请添加对以下程序集的引用:

  • System.Workflow.Activities
  • System.Workflow.ComponentModel
  • System.Workflow.Runtime

构建应用程序。

创建顺序工作流

在 Visual Studio 2005 中,从菜单中选择“新建项目”;在“C# 项目”类型下,选择“工作流”,然后在右侧模板面板中选择“顺序工作流库”。

将项目命名为 ExpenseReportWorkflow。

在默认创建的工作流设计器文件 (Workflow1.designer.cs) 中,如下修改 IntializeComponent 方法中的代码:

this.Name = "ExpenseReportWorkflow"; 
this.CanModifyActivities = false; 

构建项目。

托管顺序工作流

要执行工作流,您必须创建一个 WorkflowRuntime 类的实例。然后,创建一个宿主类,并通过 CreateWorkflow 方法传入工作流的类型规范。然后,您可以调用返回的 WorkflowInstance 对象的 Start 方法来开始执行工作流。由于顺序工作流不依赖于外部事件来执行,因此它将立即开始。

托管 Windows Workflow 运行时引擎

回到您的 Windows 应用程序,并添加对您在上一步中创建的类库 (ExpenseReportWorkflow.dll) 的引用。

MainForm 类中,声明字段如下:

private WorkflowRuntime workflowRuntime = null; 
private WorkflowInstance workflowInstance = null; 

在构造函数方法中,调用 InitializeComponent 方法后,添加以下代码。这将启动运行时,但在应用程序通知它启动工作流之前不会执行任何工作流。

this.workflowRuntime = new WorkflowRuntime(); 
workflowRuntime.StartRuntime(); 

workflowRuntime 对象的 WorkflowCompleted 事件创建一个通用事件处理程序。

workflowRuntime.WorkflowCompleted += 
      new EventHandler<WorkflowCompletedEventArgs> 
(workflowRuntime_WorkflowCompleted);

同时,添加相应的事件处理程序方法。

void workflowRuntime_WorkflowCompleted(object sender, 
WorkflowCompletedEventArgs e) 
{ 
}

submitButton_Click 方法中,创建一个本地 Type 对象,使其等于您在上一步中创建的工作流的类型。创建该类型的工作流对象并启动它。

Type type = typeof(ExpenseReportWorkflow); 
// Start the workflow 

workflowInstance = workflowRuntime.CreateWorkflow(type);
workflowInstance.Start(); 

现在,再次构建项目。

2. 创建费用报告服务

为了实现 Windows 窗体应用程序和顺序工作流之间的通信,请定义一个标记有 ExternalDataExchangeAttribute 的接口,并在您的窗体类中实现该接口。为了简化窗体和工作流之间的通信,您将使用 ExternalDataExchangeService,通过将 Windows 窗体类添加为工作流可以访问的服务。

定义工作流参数

此练习中的第一个任务是在工作流中创建两个属性。这些属性允许您从 Windows 窗体应用程序传递参数,或从工作流接收参数。

宿主应用程序可以在工作流执行之前设置该属性,方法是在调用 CreateWorkflow 方法时传入参数集合。要在工作流完成执行后将参数返回宿主应用程序,您可以为该属性定义一个 get 方法。然后,宿主应用程序可以访问传递到 WorkflowCompleted 事件的 WorkflowCompletedEventArgs 对象。WorkflowCompletedEventArgs 对象包含工作流中具有 get 方法的所有属性。

在 ExpenseReportWorkflow 类库项目的工作流类中,包含一个名为“amount”的属性,并带有一个 set 访问器。

private int reportAmount = 0; 
public int Amount 
{ 
    set 
    { 
        this.reportAmount = value; 
    } 
}

包含另一个名为 Result 的属性,并带有一个 get 访问器。

private string reportResult = ""; 
public string Result 
{ 
    get 
    { 
        return this.reportResult; 
    } 
} 

回到您的 Windows 应用程序 SimpleExpenseReport;在 submitButton_Click 中,修改代码如下。

Type type = typeof(ExpenseReportWorkflow.ExpenseReportWorkflow); 
// Construct workflow parameters 

Dictionary<string, object> properties = 
                  new Dictionary<string, object>(); 
properties.Add("Amount", Int32.Parse(this.amount.Text)); 
// Start the workflow 

workflowInstance = workflowRuntime.CreateWorkflow(type, properties); 
workflowInstance.Start(); 

新代码将为 ExpenseReportWorkflow 类型创建工作流实例,并在 Dictionary 对象中添加 amount 属性。

MainForm 类中的 workflowRuntime_WorkflowCompleted 方法中,显示从工作流返回的结果并清除金额文本字段。

if (this.result.InvokeRequired) 
{ 
    this.result.Invoke(new EventHandler<WorkflowCompletedEventArgs> 
    (this.workflowRuntime_WorkflowCompleted), sender, e); 
} 
else 
{ 
    this.result.Text = e.OutputParameters["Result"].ToString(); 
    // Clear fields 

    this.amount.Text = string.Empty; 
}

再次构建项目以检查任何错误。

定义 IExpenseReportService 接口

在此第二项任务中,定义前面提到的服务接口,并实现工作流用于调用在 Windows 窗体类中定义的方法的方法。此外,为宿主应用程序定义了两个事件,以在发生某些事件时通知工作流。这将使宿主应用程序和工作流能够相互通信。

通过选择“项目”->“添加新项”->“接口”,在 ExpenseReportWorkflow 项目中添加一个新的接口文件。

要使用 ExternalDataExchange 属性修饰该接口,请导入以下命名空间。

using System.Workflow.Activities; 
[ExternalDataExchange] 
public interface IExpenseReportService 
{ 
    void GetLeadApproval(string message); 
    void GetManagerApproval(string message); 
    event EventHandler<ExternalDataEventArgs> ExpenseReportApproved; 
    event EventHandler<ExternalDataEventArgs> ExpenseReportRejected; 
} 

修改 MainForm 类,使其实现 IExpenseReportService 接口。

public class MainForm : Form, IExpenseReportService 

添加以下委托声明,并声明在窗体运行期间发生的事件。请记住,我们在窗体上有两个按钮“批准”和“拒绝”来引发相应的事件。

private delegate void GetApprovalDelegate(string message); 
private event EventHandler<ExternalDataEventArgs> reportApproved; 
private event EventHandler<ExternalDataEventArgs> reportRejected; 

同时,添加这两个事件的事件处理程序。

public event EventHandler<ExternalDataEventArgs> ExpenseReportApproved 
{ 
    add 
    { 
        reportApproved += value; 
    } 
    remove 
    { 
        reportApproved -= value; 
    } 
} 

public event EventHandler<ExternalDataEventArgs> ExpenseReportRejected 
{ 
    add 
    { 
        reportRejected += value; 
    } 
    remove 
    { 
        reportRejected -= value; 
    } 
}

下一步非常重要。由于窗体是服务 IExpenseReportService 的实现,因此应将其添加到 ExternalDataExchangeService 类中。

为此,请在 MainForm 类中为 ExternalDataExchangeService 创建一个实例。

private ExternalDataExchangeService exchangeService = null; 

在构造函数方法中修改代码如下。

this.workflowRuntime = new WorkflowRuntime(); 
this.exchangeService = new ExternalDataExchangeService(); 
workflowRuntime.AddService(exchangeService); 
exchangeService.AddService(this); 
workflowRuntime.StartRuntime(); 

MainForm 类中实现 IExpenseReportService 中定义的两个方法。

public void GetLeadApproval(string message) 
{ 
} 
public void GetManagerApproval(string message) 
{ 
} 

构建项目并检查错误。

创建费用报告顺序工作流类库

工作流以 "IfElseActivity" 开始,以确定是必须由经理还是主管批准工作流开始执行时设置的 Amount 属性中指定的值。

确定后,工作流将调用宿主应用程序中定义的方法,请求用户批准或拒绝该金额的输入。当点击“批准”或“拒绝”按钮时,宿主应用程序将引发相应的事件,该事件由工作流正在等待的一个 HandleExternalEventActivity 活动处理。一旦引发一个事件并由工作流处理,工作流将设置 Result 属性并完成其处理。

创建 CallExternalMethod 活动

打开 ExpenseReportWorkflow 项目中的工作流设计器,然后从工具箱中拖动 "IfElseActivity" 控件。

Screenshot - pic2.jpg

从工具箱中拖动 CallExternalMethodActivity 并将其放置在 IfElseActivity 的 if 分支中。从工具箱中拖动另一个 CallExternalMethodActivity 并将其放置在 IfElseActivity 的 else 分支中。

按如下方式为活动命名:

类型

名称

IfElseActivity

ifEvaluateAmount

IfElseBranchActivity

elseLeadApprove

IfElseBranchActivity

elseManagerApprove

CallExternalMethodActivity

invokeLeadApproval

CallExternalMethodActivity

invokeManagerApproval

选择 elseLeadApprove 矩形框,然后按 F4 打开属性窗口。将“Condition”属性设置为“Code Condition”。展开“Condition”属性。

Condition 的值设置为 DetermineApprovalContact

在同一名称下编写一个辅助方法,如下所示:

void DetermineApprovalContact(object sender, ConditionalEventArgs e) 
{ 
    if (this.reportAmount < 1000) 
    { 
        e.Result = true; 
    } 
    else 
    { 
        e.Result = false; 
    } 
} 

现在,也为 CallExternalMethodActivity 框设置属性。

选择 invokeLeadApproval 框,并设置以下属性:

属性

接口

SimpleExpenseReport.IExpenseReportService(为此,点击属性角落的椭圆按钮,在显示的对话框中选择类型)

message

"需要主管批准"

MethodName

GetLeadApproval(单击下拉列表并选择此方法)

选择 invokeManagerApproval 框,并设置以下属性:

属性

接口

SimpleExpenseReport.IExpenseReportService(为此,点击属性角落的椭圆按钮,在显示的对话框中选择类型)

message

"需要经理批准"

MethodName

GetManagerApproval(单击下拉列表并选择此方法)

MainForm 类中的 IExpenseReportService 中定义了两个方法,添加如下代码:

public void GetLeadApproval(string message) 
{ 
    if (this.approvalState.InvokeRequired) 
        this.approvalState.Invoke(new GetApprovalDelegate 
                (this.GetLeadApproval), message); 
    else 
    { 
        this.approvalState.Text = message; 
        this.approveButton.Enabled = true; 
        this.rejectButton.Enabled = true; 
        // expand the panel 
    
        this.Height = this.MinimumSize.Height + this.panel1.Height; 
        this.submitButton.Enabled = false; 
    } 
} 

public void GetManagerApproval(string message) 
{ 
    if (this.approvalState.InvokeRequired) 
        this.approvalState.Invoke(new GetApprovalDelegate 
            (this.GetManagerApproval), message); 
    else 
    { 
        this.approvalState.Text = message; 
        this.approveButton.Enabled = true; 
        this.rejectButton.Enabled = true; 
        // expand the panel 

        this.Height = this.MinimumSize.Height + this.panel1.Height; 
        this.submitButton.Enabled = false; 
    } 
}

构建项目并运行应用程序。您应该会看到窗体显示,但“批准”和“拒绝”按钮此时不起作用。

创建 HandleExternalEvent 活动

在此任务中,您将创建一个 ListenActivity 活动,其中包含两个 HandleExternalEventActivity 活动,以捕获宿主应用程序引发的批准或拒绝事件。当引发批准或拒绝事件时,工作流将继续,设置结果,然后完成。

修改 ExpenseReportWorkflow 类,使其能够侦听宿主应用程序引发的特定事件。

创建一个接受一个名为 sender 的对象和一个名为 eExternalDataEventArgs 作为参数的新方法。

void approveEvent_Invoked(object sender, ExternalDataEventArgs e) 
{ 
    this.reportResult = "Report Approved"; 
} 
private void rejectEvent_Invoked(object sender,ExternalDataEventArgs e) 
{ 
    this.reportResult = "Report Rejected"; 
}

在工作流设计器中,通过从工具箱拖动来放置 ListenActivity 图。

Screenshot - pic3.jpg

从工具箱中拖动 HandleExternalEvent 活动,并将其放置在两个 EventDrivenActivity 中。

按如下方式为活动命名:

类型

名称

ListenActivity

listenApproveOrReject

EventDrivenActivity

eventDriven1

EventDrivenActivity

eventDriven2

HandleExternalEventActivity

ApproveEvent

HandleExternalEventActivity

RejectEvent

选择 ApproveEvent 活动框,然后按 F4 显示属性窗口。

属性

InterfaceType

SimpleExpenseReport.IExpenseReportService(为此,点击属性角落的椭圆按钮,在显示的对话框中选择类型)

EventName

ExpenseReportApproved

Invoked

approveEvent_Invoked(单击下拉列表并选择此方法)

选择 RejectEvent 活动框,然后按 F4 显示属性窗口。

属性

InterfaceType

SimpleExpenseReport.IExpenseReportService(为此,点击属性角落的椭圆按钮,在显示的对话框中选择类型)

EventName

ExpenseReportRejected

Invoked

rejectEvent_Invoked(单击下拉列表并选择此方法)

下一步是从宿主应用程序引发事件。为此,请向 approvebutton_Click 处理程序添加代码。

// Raise the ExpenseReportApproved event back
// to the workflow reportApproved(null, new ExternalDataEventArgs 

(this.workflowInstance.InstanceId)); 
this.Height = this.MinimumSize.Height; 
this.submitButton.Enabled = true; 

同样,向 rejectbutton_Click 处理程序添加代码。

// Raise the ExpenseReportRejected event back to the workflow 

reportRejected(null, new ExternalDataEventArgs (
                         this.workflowInstance.InstanceId)); 
this.Height = this.MinimumSize.Height; 
this.submitButton.Enabled = true; 

构建并运行应用程序。

当您提交费用报告金额时,窗体将展开以显示“**批准**”和“**拒绝**”按钮,以及从工作流收到的消息。点击其中一个按钮会执行以下操作:

  1. 折叠窗体。
  2. 将事件引发回工作流。
  3. 从工作流中检索 Results 属性。
  4. 将其显示在窗体上。

Screenshot - pic4.jpg

这是创建顺序工作流演练的结束。

© . All rights reserved.