WF 从入门到精通(第九章):逻辑流活动

学习完本章,你将掌握:

1.学会怎样使用IfElse 活动来执行条件表达式

2.学会怎样使用While 活动来执行循环

3.理解Replicator 活动是怎样来模拟for 循环的,以及它的使用方法。

我们已经看到过怎样执行工作流内部和外部的代码,已经知道怎样处理异常,暂停进程,

在事情脱离控制时终止我们的工作流。但无疑对于任何一个 计算机系统的主要组成部分来

说,都应具有根据运行时的条件做出判断以执行不同的任务的能力。在本章,我们将演示要

求我们应付if/else 场景及基本的循 环的一些工作流活动。

条件及条件处理

现在,你可能不会感到奇怪,你发现WF 提供了基于运行时的条件进行逻辑处理控制流的

活动。毕竟,假如WF 提供了活动去抛出并捕获异常,那它为什么就没有相应的活动来根据工

作流的执行情况进行检测并根据它们获取的结果作出决策呢?

我 们将在本章中进行测试的活动包括IfElse 活动、While 活动和Replicator 活动。

IfElse 活动的作用是测试一个条件并根据测试结果执行 不同的工作流路径。While 活动用来

执行一个While 循环。而对于for 循环,则是使用Replicator 活动来完成。现在通过本章的

示例应用程序 开始我们的学习。

备注:在本章你将依靠CodeCondition 来进行条件的处理,它 (CodeCondition)的意

思是你将写下C#代码来处理条件表达式。在12 章(“策略和规则”)中,你将使用

RuleCondition 来对条件 表达式的值进行处理,RuleCondition 使用了WF 的基于规则的处理

方式。两种方式都同样有效。

Qustioner应用程序

本章的示例应用程序是一个Windows Form 应用程序,它会请你回答三个问题,问题内容

你能够进行修改。(问题的内容保存在应用程序的settings property 中。)你也可指定这

些问题是各自独立还是相互关联的。

当 工作流开始执行时你要把这些问题和相关的情况传入该工作流。相互关联的问题只有

在前面的问题回答正确时才会被进一步提出。例如,假如有人问你:“谈到的文 档你看过

吗?”,假如你没有,则没多大意义问接下来这一问题:“这个文档你批准吗?”假如问题

是相关的,则第一个问题回答是否定的话,就将返回否定的回 答,余下的问题不予考虑也都

将返回否定的回答。

各自独立的问题要求你必须回答,而不管前面的问题中你回答的是什么。例如这个问题,

“你喜 欢冰淇淋吗?”就和问题“现在外面在下雨吗?”是不相关的。无论你喜不喜欢冰淇

淋,你的答案都和外面的天气这个问题是各自独立的。对于相互独立的问题来 说,不管你在

前面的问题中是肯定还是否定的回答,都会进一步被问到。

用户界面如图9-1。假如你修改三个问题中的任何一个的内容,新问题 的都将自动地保

存到你的应用程序的settings property 中(问题的类型也一样)。这些问题会产生“是/

否”的回答,使工作流能够把这些回答作为一个Boolean 类型的数组传回到宿主应用程序

中。

image.png

图9-1 Questioner 主应用程序界面

当你点击Execute 按钮时,这些问题通过带“是”和“否”按钮的信息框依次呈现。一

旦工作流处理完所有的这些问题,它就返回一个Boolean 数组给宿主应用程序。宿主应用程

序将检查该数组以显示不同的用户界面。

当 工作流执行时,回答结果将以蓝色圆球的形式显示(如图9-1)。当工作流任务完成

后,通过的回答将以绿色圆球的形式出现,未通过的回答将以红色圆球的形式 出现。假如所

有的回答都通过了,则“最终回答结果”图片将以绿色圆球的形式呈现。但是,假如三个问

题中的任何一个没有通过,则“最终回答结果”图片将以带 “8”字的圆球的形式呈现。如

图9-2。

image.png

图9-2 Questioner 应用程序执行期间的用户界面

对你来说,使用这个应用程序的目的是测试本章中的三个活动。第一次迭代,Questioner

将使用IfElse 活动来判断要执行什么动作过程。第二次迭代时这些问题仍然会被问到,我们

将使用While 活动来提问。最后一次迭代我们将使用Replicator 活动来模拟for 循环进行提

问。对于该应用程序的每一次迭代,我们都将使用前一章中演示的技术来把回答的结果传回

给宿主应用程序。

使用IfElse活动

IfElse 活动的作用是对if-then-else 条件表达式进行模拟,其实你在前几章使用过这

个活动。

IfElse 活动要求你提供一个条件表达式,它其实是作为一个event handler 执行。你创建的

event handler 有一个类型为ConditionalEventArgs 的参数,它有一个Boolean 类型的(名

称为)Result 属性。你可对其进行set,以指明该条件表达式的结果。

IfElse 活动根据Result 的值来指挥工作流到底该执行两个分支中的哪一个。在

Microsoft Visual Studio 的工作流视图设计器中,true 执行的是显示在左边的路径,而false

执行的是右边的路径。两个分支都可作为其它活动的容器,允许你插入任何一个你需要的工

作流活动。

备注:通过本节的学习,你可能会认为,IfElse 活动可能不是构建下面的工作流的最合

适的活动。你在本章的后面部分将找到更加适合下面特定的工作流的活动。

使用IfElse活动创建QuestionFlow工作流

1.下载本章源代码,打开IfElse Questioner 文件夹中的解决方案。

2. 看看Visual Studio 解决方案资源管理器,你会看到解决方案的层次结构和前一章中

的相似。主应用程序的文件位置在Questioner 项目中,而宿主通信服务文件 的位置则在

QuestionService 项目中。为使你把注意力放到工作流上,我已经创建了服务接口(具体过

程参见前一章):IQuestionService,并且使用wca.exe 工具(使用方法参见前一章)生成

了一个必需的通信活动:SendReponseDataToHost。现在,找到QuestionFlow 项目的

Workflow1.cs 文件并在视图设计器中打开它。

3.从工具箱中拖拽一个IfElse 活动到设计器界面上。

image.png

4.你会看到一个内含感叹号(!)标记的红色圆圈,这是提醒你还需要更多的信息才能

编译你的工作流。其实,缺少的就是条件表达式!选中ifElseActivity1 的左边分支以便在

Visual Studio 的属性面板上呈现该活动的属性。选中它的Condition 属性以激活它的下拉

列表框,然后从列表中选择代码条件。

image.png

备注:你通常有两种方式来对条件表达式进行选择:code(代码)和rules-based(基于规

则)。我们这里将使用基于代码的条件表达式,基于规则的技术将保留到第12 章(策略活动)

进行学习。

5.展开显示的Condition 属性,输入AskQuestion1,然后按下回车键,Visual Studio

这就为你插入了AskQuestion1 的事件处理程序并会切换到代码视图下。现在,重新回到工作

流视图设计器上,你还要把更多的活动添加到你的工作流中。

image.png

6.拖拽一个Code 活动到设计器界面上,并把它放到ifElseActivity1 的右边分支上。

image.png

7.指定它的ExecuteCode 属性值为NegateQ1。当Visual Studio 插入了NegateQ1 事件

出现程序后,重新回工作流视图设计器界面上。

image.png

8.重复步骤6 和步骤7,在ifElseActivity1 的左边分支上也添加一个Code 活动。

image.png

指定它的ExecuteCode 属性值为AffirmQ1,但是,当Visual Studio 插入了AffirmQ1

事件出现程序后,不要切换回工作流视图设计器界面上。相反,我们要添加一些代码。

image.png

9.我们现在需要为该工作流类添加一些属性,当我们起动工作流进程时可把它们作为参

数。在Workflow1 的构造器的下面,添加下面的代码,它们包含有工作流将问到的三个问题:

private string[] _questions = null;
public string[] Questions
{
    get { return _questions; }
    set { _questions = value; }
}

10.我们也需要添加一个Dependent 属性,它用来告知这些问题彼此是否是相关的。在上

面所添加的代码下,添加如下代码:

private bool _dependent = true;
public bool Dependent
{
    get { return _dependent; }
    set { _dependent = value; }
}

11.问题回答的结果是一些Boolean 值,在传回给宿主应用程序前需要保存到某些地方。

因此,在上面所插入的代码下添加该字段:

private bool[] _response = null;

12.该_response 字段没有被初始化,因此找到Workflow1 的构造器,在里面的

InitializeComponent 方法下面添加如下的代码:

// Initialize return vector.
_response = new bool[3];
_response[0] = false;
_response[1] = false;
_response[2] = false;

13.现在找到Visual Studio 为你添加的AskQuestion1 事件处理程序(event handler)。

在该事件处理程序中添加下面的代码:

// Ask the question!
DialogResult result = MessageBox.Show(Questions[0], "Questioner:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
e.Result = (result == DialogResult.Yes);

14.对于NegateQ1 事件处理程序,则添加下面的代码:

// Negate answer.
_response[0] = false;
if (Dependent)
{
    // Negate remaining answers.
    _response[1] = false;
    _response[2] = false;
}

15.接下来,在AffirmQ1 事件处理程序中添加下面的代码:

// Affirm answer.
_response[0] = true;

16.你现在就已为第一个问题的提问添加了对应的工作流组成部分,但是这还有两个问

题。对于第二个问题,重复步骤3 至步骤8 来新添加一个IfElse 活动添加到工作流中,把和

问题1 相关的地方用问题2 替换掉,例如插入的事件处理程序就应该是AskQuestion2、

NegateQ2 和AffirmQ2。工作流视图设计器的界面现在如下所示:

image.png

17.现在找到AskQuestion2 事件处理程序并添加下面的代码:

if (_response[0] == false && Dependent)
{
    // No need to ask!
    e.Result = false;
}
else
{
    // Ask the question!
    DialogResult result = MessageBox.Show(Questions[1], "Questioner:",
    MessageBoxButtons.YesNo, MessageBoxIcon.Question);
    e.Result = (result == DialogResult.Yes);
}

18.对于NegateQ2 事件出现程序,添加下面的代码:

// Negate answer
_response[1] = false;
if (Dependent)
{
    // Negate remaining answer
    _response[2] = false;
}

19.对于AffirmQ2 事件处理程序,添加下面的代码:

// Affirm answer.
_response[1] = true;

20.在一次重复步骤3 至步骤8,添加第三个问题,把和问题1 相关的内容替换掉(方法

和添加第二个问题时一样)。此时,工作流视图设计器的界面如下所示:

image.png

21.找到AskQuestion3 事件处理程序,插入下面的代码:

if (_response[1] == false && Dependent)
{
// No need to ask!
e.Result = false;
}
else
{
    // Ask the question!
    DialogResult result = MessageBox.Show(Questions[2], "Questioner:",
    MessageBoxButtons.YesNo, MessageBoxIcon.Question);
    e.Result = (result == DialogResult.Yes);
}

22.对于NegateQ3 事件处理程序,添加下面的代码:

// Negate answer.
_response[2] = false;

23.对于AffirmQ3 事件处理程序则添加下面的代码:

// Affirm answer
_response[2] = true;

24.现在回到工作流视图设计器。你会在工具箱中找到一个名称为

SendResponseDataToHost 的自定义活动。(注意:假如工具箱中没有这个

SendResponseDataToHost 活动,则编译该项目再重新看看。)

image.png

25.拖拽一个SendResponseDataToHost 活动到你的工作流视图设计器界面上,把它放到

第三个IfElse 活动(ifElseActivity3)的下边。

image.png

26.因为返回的数据是一个简单的Boolean 类型的数组,因此这里的处理方式和前一章有

点点区别。和你去添加一个容纳该Boolean 类型的数组的依赖属性不同,

SendResponseDataToHost 活动使用一个字段来容纳该数据,创建该字段的用户界面也就和你

在第七章中看到的不同。在Visual Studio 属性面板上选中responses 属性,然后点击浏览

(...)按钮。

image.png

这打开了如下面的Boolean 集合编器对话框。

image.png

27.点击添加按钮,共重复三次,保留这些默认的False 值,然后点击确定按钮。Visual

Studio 就为你把包含三个Boolean 元素的数组添加进了你的Workflow1.designer.cs 文件的

代码中。

提示: 在下面的第28 步,你将添加一个CodeActivity,用它来把你在第11 步添加的

_response 字段分配到我们刚刚为 SendResponseDataToHost 创建的Boolean 数组中。但是,

你也可直接使用SendResponseDataToHost 的一个我 们已经创建的response 属性(来对它进

行访问)。我选择这样做仅仅是因为(从阐述的角度来说)这样更有意义,这可展示出在涉

及到宿主通信活动前该 Ifelse 活动是怎样添加和工作的。

28.现在我们需要把保存了问题回答结果的数组的值和将要使用这些值的

SendResponseDataToHost 活动联系起来。因此,我们现在就拖拽一个Code 活动到工作流视

图设计器的界面上,把它放到第三个IfElse 活动(ifElseActivity3)和

SendResponseDataToHost 活动(sendResponseDataToHost1)之间。

image.png

29.设置该Code 活动的ExecuteCode 属性为CopyResponse,然后按下回车键。

30.在Visual Studio 插入的CopyResponse 事件处理程序中添加下面的代码:

// Assign outgoing data.
sendResponseDataToHost1.responses = _response;

31.编译并运行该应用程序。改变问题的Dependency 属性,看看在回答这些问题时,作

出否定的回答其结果一样吗?

使用While活动

假如你回头看看前一节,你会至少注意到两件事。首先,毫无疑问你体验了IfElse 活动;

第二,它用了31 个独立的步骤 来创建了该工作流。有些程序结构使用if-then-else 来进行

处理很合适,但这个特殊的应用程序使用循环结构来进行问题的提问会更合适些。这些将在

接下来演示。你将使用另一个使用了while 循环的工作流来替换你刚刚创建好了的工作流。

WF 的While 活动处理条件表达式时的过程和IfElse 活动相似。它触发了一个对循环是

否继续进行验证的事件,它使用ConditionalEventArgs 来返回你的判断结果(也要使用

Result 属性)。

但是,和IfElse 活动不同的是,你在使用While 活动的时候,假如设置Result 为true 将导

致继续进行循环,设置Result 为false 则终止循环。我们就来看看怎样使用while 循环来替

换if-then-else 进行条件处理,以简化我们的工作流。

使用While活动创建QuesionFlow工作流

1.从下载的本章源代码中使用Visual Studio 打开While Questioner 文件夹内的解决方

案。

2.和前一节一样,该应用程序本质上是完整的,它包含了已创建好了的

SendResponseDataToHost 活动。剩下要去完成的工作是完善工作流的处理过程。在解决方案

管理器面板上找到QuesionFlow 项目中Workflow1.cs 文件,然后在工作流的视图设计器中打

开它。

3.从工具箱中拖拽一个While 活动到视图设计器界面上。

image.png

4.和IfElse 活动相似,选中whileActivity1 活动的Condition 属性以激活它的下拉列

表框。从这个下拉列表框中选择代码条件。

image.png

5.展开该Condition 属性,输入TestComplete,然后按下回车键。Visual Studio 这就

为你添加了TestComplete 事件程序程序,然后回到工作流的视图设计器界面上。

image.png

6.拖拽一个Code 活动到工作流视图设计器界面上,把它放到whileActivity1 的里面。

指定它的ExecuteCode 的属性值为AskQuestion。同样,在生成了AskQuestion 事件处理程

序后重新回到工作流视图设计器界面上来。

image.png

7.为了使我们能把保存有问题回答结果的Boolean 数组返回给宿主应用程序,我们需要

重复前面一节的第24、25 步,以把一个SendResponseDataToHost 活动插入到我们的工作流

中。(在这之前,需要编译该应用程序,否则SendResponseDataToHost 活动不会在工具箱中

显示。)我们把该SendResponseDataToHost 活动放到whileActivity1 的下面,以便它在while

循环后被执行。

image.png

8.我们同样需要重复前一节的第9 步至第12 步,以便插入Questions 和Dependent 属性,

并且对_response 数组进行创建和初始化。

9.在_response 数组的声明语句下面,添加下面的代码:

private Int32 _qNum = 0;

10.找到TestComplete 事件处理程序,添加下面的代码:

// Check for completion.
if (_qNum >= Questions.Length)
{
// Assign outgoing data.
sendResponseDataToHost1.responses = _response;
// Done, so exit loop.
e.Result = false;
}
else
{
// Not done, so continue loop.
e.Result = true;
}

11.我们需要完成的最后一点代码实际上就是问题的回答。在Workflow1.cs 文件中,你

会找到AskQuestion 事件处理程序,为该事件处理程序添加下面的代码。假如问题的回答是

否定的并且Dependent 属性是true(表示各个问题是相关的),则所有余下的问题的回答就

都是否定的。

// Ask the question!
DialogResult result = MessageBox.Show(Questions[_qNum], "Questioner:",
MessageBoxButtons.YesNo, MessageBoxIcon.Question);
_response[_qNum] = (result == DialogResult.Yes);
// Check response versus dependency
if (!_response[_qNum] && Dependent)
{
// Negate remaining questions
while (_qNum < Questions.Length)
{
// Negate this question
_response[_qNum] = false;
// Next question
++_qNum;
}
} // if
else
{
// Set up for next iteration
++_qNum;
}

12.重复前一节的步骤28 至步骤30,我们将使用Code 活动来把保存了问题回答结果的

数组传给SendResponseDataToHost 活动。(该Code 活动放到While1 活动和

SendResponseDataToHost1 活动中间。)

13.编译并执行该应用程序。

假如你花一些时间来比较一下本节和上一节的最终的工作流视图设计器界面,你很容易

发现使用While 活动大大简化了工作流的处理。整个工作流视图设计器界面简单多了!

既然有和while 循环等价的工作流活动,是否也有和for 循环等价的工作流活动呢?答

案是肯定的,它就是Replicator 活动。

使用Replicator活动

说Replicator 活动和C#术语中的for 循环是等价的可能并不正确。C#语言规范1.2 中

告诉我们,C#中的for 循环看起来和下面的类似:

for(for-initializer;for-condition;for-iterator) embedded-statement

embedded- statement(内嵌语句)在for-condition(条件判断)的值为true 时执行

(如果被省略,则假定为true),循环由for- initializer 开始,每一次循环都要执行

for-iterator。这当中没有涉及到任何的C#声明组件。对于replication 来说,我们 可以

设想它是一个能重复地对源代码进行准确复制的软件工厂。C#的for 循环则不是以这种方式

进行操作的。

事实上,“重复”的概念在我们看到WF 中和for 循环等价的活动这一说明后就不会感到

可怕了。假如你熟悉ASP.NET 的话,你或许使用过Repeater 控件。这个ASP.NET 的Repeater

控件有一个项模板(同时也有一个项替换模板),它能根据所绑定的数据项的数目进行重复

处理。

Replicator 活动和ASP.NET 的绑定到基于IList 数据源的Repeater 控件相似,它重复

它所包含的子活动,基于IList 的数据源中的每个元素就相当于一个子活动。但是Replicator

活 动和C#中的for 语句在有些方面是相似的,因为它也有一个循环初始化事件(这就像是

for-initializer)、一个循环完成事件(这和用 for-iterator 和for-condition 做比较类

似)以及一个循环继续事件(和for-condition 类似)。它提供了这些事件来指明 重复性的

(和嵌入语句相似)子活动的创建工作,以便你能个性化地进行数据绑定,子活动完成了它

就触发一个事件,以便你能为每个子活动实例执行一些清理和管 理的任务。

Replicator 活动必须接受也只接受一个唯一的活动,它能作为其它活动的容器(和

Sequence 活动类似),它触发一个开始执行的初始化事件。在初始化事件的执行期间,你可

把一个基于IList 的集合绑定到Replicator 活动的InitialChildData 属性中。

该Replicator 活动然后会重复你所提供的子活动,它们的次数和基于IList 集合中的项

的数目相等。这些子活动实例能够以依次按顺序的方式或以并行的方式执行(这可通过

ExecutionType 属性进行设置)。UntilCondition 事件在每一个子活动执行前触发,在处理

UntilCondition 事件时,你可通过设置ConditionalEventArgs 的Result 属性为false 来通

知Replicator 活动继续执行(为true 则终止循环)。表9-1 简要地列出了我们需要关注的

Replicator 活动的一些属性,而表9-2 列出了在我们的工作流中使用Replicator 活动时需

要进行处理的一些事件。表9-1 Replicator 活动的属性

属性 功能

ExecutionType

获取或设置Replicator 活动的ExecutionType(一个枚举值)。该

ExecutionType 的枚举值包含Parallel 和Sequence。

InitialChildData

获取或设置子活动数据的一个IList 集合。该属性和其它.NET 技术中的

data-binging(数据绑定)属性相 似,事实上要分配给该属性的对象必

须是一个IList 对象。Replicator 活动会为分配给该属性的基于IList 集

合中的每一项创建一个子活动实 例。

表9-2 Replicator 活动的事件

事件 功能

ChildCompleteEvent

在Replicator 活动中的子活动实例已完成后触发。对于每一个循环

触发一次。

ChildInitializedEvent

在Replicator 活动中的子活动实例初始化后触发。对于每一个循环

触发一次。

CompleteEvent

在Replicator 活动已完成后触发(也就是说,在所有循环中的子活

动实例都已执行完成)。

InitializedEvent

在Replicator 活动开始执行时触发。该事件只触发一次,在所有的

子活动执行前触发。

UntilCondition

UntilCondition 在许多的WF 文档中都是以属性的方式列出的,它其

实表示的是一个事件处理程序,这和 Code 活动的ExecuteCode 属性

的作用一样(通过ExecuteCode 属性就把一个去执行相应代码的事

件处理程序和相应的Code 活动联系了 起来)。这个事件在每一个

子活动实例执行前触发。它的ConditionalEventArgs 事件参数控制

了循环是否继续进行执行。指定Result 的 值为false 则允许子活

动继续执行。而指定Result 的值为true 则导致Replicator 活动停

止所有子活动的执行。

图9-3 为你提供了一个基本的流程图,它显示了在什么地方触发什么事件

image.png

图9-3 Replicator 活动的事件触发顺序流程图

你在图9-3 中看到的基于IList 的集合是通过InitialChildData 属性指定的,你也可在

Initialized 事件处理前或处理期间进行指定。该工作流也没有说明所复制(生成)的子活

动能以顺序或者以并行的方式执行,这取决于ExecutionType 属性的设置。

在现实情形中怎样使用Replicator 活动呢?从目前的描述来看,它比真实的使用情形要

复杂得多。事实上,它的机制和其它活动相比并没有多大的区别。拖拽该活动到工作流视图

设计器界面上,为各中事件处理程序指定值,再拖拽一个唯一的子活动到Replicator 活动当

中。该唯一的子活动和Replicator 活动本身一样,也能作为一个(其它活动的)容器(就像

Sequence 活动一样),因此事实上多于一个的活动也能执行。在我们的头脑里有了这些表和

这些图,我们就可使用Replicator 活动来重新编写Questioner 应用程序了。

使用Replicator活动创建QuestionFlow工作流

1.下载本章源代码,打开Replicator Questioner 文件夹内的解决方案。

2.和前两节一样,宿主应用程序本质上已经完成,这可方便你把焦点放到工作流方面。

选中Workflow1.cs 文件并在Visual Studio 的工作流视图设计器中打开它。

3.从工具箱中拖拽一个Replicator 活动到视图设计器界面上,界面如下所示:

image.png

4.在Visual Studio 属性面板上,选中Initialized 属性并输入InitializeLoop,然后

按下回车键。这样Visual Studio 就在你的代码中插入了相应的事件处理程序并把界面切换

到代码编辑视图界面中。我们回到工作流视图设计器界面上来,你还要继续设置属性。

image.png

5.对于Completed 属性,输入LoopCompleted 并按下回车键,在添加了LoopCompleted

事件处理程序后,和前一步骤一样,我们重新回到工作流视图设计器界面上。

image.png

6.在ChildInitialized 属性中,输入PrepareQuestion。在插入了PrepareQuestion 事

件处理程序后,我们再次回到工作流视图设计器界面上。

image.png

7.接着,在ChildCompleted 属性中输入QuestionAsked,在创建了ChildCompleted 事

件处理程序后,我们同样回到工作流视图设计器界面上。

image.png

8.为了在问完所有的问题后(或者在问题是相关的,同时用户的回答是否定的情形下)

终止循环,我们需要添加一个事件处理程序。因此,我们需要选中UntilCondition 属性,从

它的下拉列表框中选择“代码条件”选项。

image.png

9.对于该UntilCondition 的Condition 属性,我们输入TestContinue,按下回车键,

在插入相应的事件处理程序后再次回到工作流的视图设计器界面上来。

image.png

10.对于Replicator 活动的实例replicatorActivity1 来说,需要一个唯一的子活动。

因此,从工具箱中拖拽一个Code 活动到replicatorActivity1 中。指定它的ExecuteCode 属

性为AskQuestion。


image.png

11.在工作流视图设计器上需要完成的最后工作是把一个SendResponseDataToHost 活动

拖拽到设计器界面上,把它放到replicatorActivity1 活动的下面。然后重复“使用IfElse

活动创建QuestionFlow 工作流”这一节中的步骤24 至步骤30。(在这之前,你或许需要编

译该应用程序以让SendReponseDataToHost 活动显示在工具箱中。)

image.png

12.现在我们就来为Workflow1 添加相应的代码,因此我们进入它的代码视图界面。

13.因为我们修改的replicatorActivity1 活 动的各个属性,Visual Studio 都为我们

添加了对应的事件处理程序,现在我们就来完成这些事件处理程序并为工作流添加其它一些

所需要的代码。我们重复“使用IfElse 活动 创建QuestionFlow 工作流”这一节中的步骤9

至步骤12,这些过程为工作流添加了为进行问题处理所必须的一些属性。

14.Replicator 活动需要一个基于IList 的集合以便复制(生成)出它的子活动。我们

有一个容纳问题的数组可以使用,因为基本的数组类型就是基于IList 的。但是,我们怎样

返回结果呢?在问题描述和问题编号之间并没有直接联系在一起。除了这些,我们还不能在

所返回的数组的值中指定Boolean 返回值。因此,我们将作出一些轻微的修改,我们需要创

建一个新的数组——一个整形数组。它用来表示在问题描述数组中的元素的偏移量。对于生

成的子活动,它将对所要提问的问题编号进行访问,给定它的一个索引,就可把问题描述数

组和回答的Boolean 类型数组联系起来。因此,我们需要在_respone 数组的声明代码下面添

加这样一个数组。

private Int32[] _qNums = null;

15._qNums 数组还没有初始化,因此必须初始化才能使用。初始化最好的位置是在

Question 属性中,因此对它的set 访问器进行修改,修改后的代码如下:

public string[] Questions
{
get { return _questions; }
set
{
// Save question values
_questions = value;
// Create new question number array
_qNums = new Int32[_questions.Length];
for (Int32 i = 0; i < _questions.Length; i++)
{
// Assign this question number to the array
_qNums[i] = i;
} // for
}
}

16.为了对Replicator 活动的所有事件都会被使用到进行证明,我们需要在_qNums 数组

的声明语句下添加下面的代码:

private Int32 _currentQuestion = -1;
private bool _currentQuestionResponse = false;

17.在InitializeLoop 事件处理程序中添加下面的代码来对InitialChildData 进行初始

化:

replicatorActivity1.InitialChildData = _qNums;

备注:假如Workflow1 有可绑定的属性的话,你可通过工作流视图设计器来直接对

InitialChildData 的值进行指定。但是,因为该Replicator 活动正使用一个内部生成的数

组(_qNums),因此和上面所展示的一样,你必须在InitializeLoop 事件处理程序中对

InitialChildData 的值进行指定。

18.对于LoopCompleted 事件处理程序,添加下面的代码来返回问题的回答结果:

replicatorActivity1.InitialChildData = _qNums;

19.现在我们的子活动将执行许多次来进行问题的提问。在每一个问题提问之前,

Replicator 活动都将触发ChildInitialized 事件。我们将处理该事件并从事件参数中获取

我们将要提问的问题编号。稍后,当Code 活动执行时,将会对和该问题编号对应的问题进行

提问。因此,把下面的代码添加到PrepareQuestion 方法中(该方法是ChildInitialized 事

件的处理程序):

_currentQuestion = (Int32)e.InstanceData;

20.当对Code 活动的回答结果进行保存时,我们需要做的过程都是相似的。定位到

QuestionAsked 事件处理程序(它用来处理Replicator 活动的ChildCompleted 事件),添

加下面的代码:

_response[_currentQuestion] = _currentQuestionResponse;

21.紧接着是要对Replicator 活动的UntilCondition 进行编辑。找到TestContinue 方

法,添加下面的代码。这些在TestContinue 方法中的代码将对Dependent 属性进行检查。假

如不再有问题,循环将被终止,同时,假如这些问题被指明是相关的,并且最近的一次回答

是否定的,则所有余下未答问题的回答也被标明为否定的并终止循环。

if (_currentQuestion >= 0)
{
// Check dependency.
if (!_response[_currentQuestion] && Dependent)
{
// Negate remaining questions.
for (Int32 i = _currentQuestion + 1; i < Questions.Length; i++)
{
// Negate this question.
_response[i] = false;
}
// Stop processing.
e.Result = true;
}
else
{
// Check for complete loop.
if (_currentQuestion == _qNums[Questions.Length - 1])
{
// Done.
e.Result = true;
}
else
{
// Continue processing.
e.Result = false;
}
}
}

22.找到Visual Studio 已经为你添加了的AskQuestion 方法,添加下面的代码。该方法

使你有机会去进行问题的回答。

// Ask the question!
DialogResult result = MessageBox.Show(Questions[_currentQuestion],
"Questioner:", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
_currentQuestionResponse = (result == DialogResult.Yes);

23.编译并执行该应用程序。把它执行的结果和前面的两个示例做比较,你会发现它的功

能和前面的示例完全一样。


源码下载 http://files.cnblogs.com/gyche/WF%20Step%20by%20Step/Chapter9.rar



--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864