再看逻辑流活动的Replicator活动。
它并不与for循环完全相同。它也有一个循环初始化事件,循环完成事件和一个循环继续事件。它提供了这些事件来指明重复性的子活动的创建工作,以便你能个性地进行数据绑定,子活动完成了它就触发一个事件,以便你能为每个子活动实例执行一些清理和管理的任务。
Replicator活动只接受一个唯一的活动,它能作为其他活动的容器。它触发一个开始执行的初始化事件。在初始化事件执行期间,你可把一个基于IList的集合绑定到Replciator活动的InitalChildData属性中。
该Replicator活动然后会重复你所提供的子活动。次数和基于IList集合中的项的数目相等。这此子活动能够按顺序的方式或并行的方式进行(通过ExecuteType进行)。UtileCondition事件在每一个子活动执行前触发,处理它时可设置ConditionalEventArgs的Result属性为false来通知Replicator活动继续执行。如下表列出了Replicator活动执行时的一些属性和事件。
属性 | 功能 |
ExecuteType | 设置或获取Replicator活动的ExecuteType。该枚举值包含并行和顺序活动。 |
InitalChildData | 设置或获取子活动数据的一个IList集合。分配给该属性的必须是一个IList对象。 |
事件 | 功能 |
ChildCompleteEvent | 子活动完成后触发。每一次循环触发一次。 |
ChildInitializedEvent | 子活动实例初始化后触发。每一次循环触发一次。 |
CompleteEvent | Replicator活动完成后触发。 |
InitializedEvent | Replicator活动开始前触发。 |
UntilCondition | 在每一个子活动实例执行前触发,它的ConditionalEventArgs事件控制循环是继续执行。 |
使用Replicator活动创建QuestionFlow工作流
同样只关注QuestionFlow工作流程序。
从工具箱拖一个Replicator活动到视图设计器界面上。把它的Initialized属性设为InitializeLoop,Completed属性设为LoopCompleted,ChildInitialized属性输入PrepareQuestion,ChildCompleted属性设置为QuestionAsked;选中UntilCondition属性,选择代码条件,并把它的Condition属性设置为TestCondition。
拖一个Code活动到replicatorActivity中,将它的ExecuteCode属性设置为AskQuestion。把生成的SendResponseDataToHost活动放到Replicator活动下面。同样再放置一个Code活动到上述两个活动之间。
下面为Workflow1添加相应的代码。
首先添加与上例相同的属性用来存储问题和回答及是否相关。我们还需要创建一个新的整形数组,用来表示问题数组的偏移量。生成的子活动将根据所要提问的问题的编号进行访问,以问题和回答数组的联系起来,定义如下:
private Int32 _qNums = null;
它的初始化应在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
}
}
为了对Replicator活动的所有事件都会被使用到进行证明,需要添加如下语句:
private Int32 _currentQuestion = -1;
privatebool _currentQuestionResponse = false;
InitalizeLoop事件处理中添加:
replicatorActivity1.InitialChildData = _qNums;
LoopCompleted事件处理添加:
replicatorActivity1.InitialChildData = _qNums;
问题的子活动将会执行多次,在每一个问题提问前,Replicator活动都将触发ChildInitialized事件,我们将处理该事件并从事件参数中获取我们要提问的问题的编号。当Code活动执行时会对和该问题编号对应的问题进行提问,因此在PrepareQuestion事件处理中添加:
_currentQuestion = (Int32)e.InstanceData;
QuestionAsked事件处理中添加:
_response[_currentQuestion] =_currentQuestionResponse;
还要对Replicator活动的UntilCondition进行编辑。找到TestContinue方法,添加如下代码:
if (_currentQuestion >= 0)
{
// Check dependency.
if (!_response[_currentQuestion] && Dependent)
{
// Negate remaining questions.
for (Int32 i = _currentQuestion + 1; i < Questions.Length; i++)
{
// Negate thisquestion.
_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;
}
}
}
AskQuestion方法中添加如下:
DialogResult result =MessageBox.Show(Questions[_currentQuestion],
"Questioner:", MessageBoxButtons.YesNo,MessageBoxIcon.Question);
_currentQuestionResponse = (result == DialogResult.Yes);
编译运行,功能与前两个例子完全相同。
事件活动
在前面的例子中,工作流使用CallExternalMethod活动来和宿主应用程序进行通信。当工作流调用一个外部方法时,使用一个你提供的本地通信服务,该宿主应用程序会收到一个事件,然后宿主对数据进行处理并进行一些相应的动作。相反的调用过程是宿主应用程序触发的事件被工作流捕获进行处理。
HandleExternalEvent活动
HandleExternalEvent活动的作用是响应一个基于IEventActivity接口的事件,有三个主要的成员:QueueName属性(表示正在等待该事件的工作流队列),Subscribe属性和Unubscribe方法(把你的事件处理程序将要接收或不接收的特定事件实例告知工作流运行时)。它可和CallExternalMehod活动一起使用。
HandleExternalMethod常用的属性和方法如下:
属性 | 功能 |
CorrelationToken | 获取或设置一个到关联标记(Correlation Token)的绑定。 |
EventName | 活动将要处理的事件。如果没有对其进行设置,该活动将不会对事件进行监听并且和宿主通信也就不可能进行。 |
InterfaceType | 获取或设置进行通信要使用的接口类型。该接口必须使用ExternalDataExchange特性进行标记。 |
OnInvoked | 用来把本事件参数中的值和你的工作流中的字段或依赖属性进行绑定。 |
尽管可直接使用HandleExternalEvent活动,但更普遍的情形是使用wca.exe工具为你正在使用的通信接口创建一个派生自HandleExtenalEvent的自定义类。
在你的工作流中放入该活动,指定接口和事件名。还可为Invoked事件提供一个Evnet Handler。使用wca.exe后,可为你提供一个派生自HandleExternalEvent的活动,可直接拖放进工作流中,在属性窗口中添加绑定,把事件参数中的数据和一个局部字段或者依赖属性绑定在一起。
工作流中有了HanldeExternalEvent活动后,在等待事件发生时所有通过该顺序流的处理过程都会停止,只有该事件触发时才允许工作流处理进行下去
Delay活动
Delay活动实现了IEventActivity接口,同样归类于WF的基于事件的活动。
传给Delay的是一个TimeSpan对象,它在延时时间过期后,触发一个事件。它也提供了一个event handler(InitializTimeoutDuration),当Delay活动被初始化并获取所需的时间间隔信息时将调用该事件处理程序。
EventDriven活动
EventDriven是一个组合活动,对于它容纳的活动的唯一限制是在执行路径上的第一个活动必须是对IEventActivity进行了处理的活动。它在有事件触发并被经一个活动处理前是不允许所容纳的活动执行的。
EventDriven的父活动必须是Listen、State或StateMachineWorkflow之中的一个。
Listen活动
Listen活动是一个并行活动,可作为两个或更多的EventDriven活动的容器,一旦其中一个对某个事件进行了处理,其它并行的EventDriven活动的执行路径都会被忽略。
Listen活动必须至少包含两个及以上的EventDriven活动对象,且只有EventDriven类型的活动可直接放到Listen活动中。为防止在状态机工作流中出现循环,它禁止使用Listen活动。
EventHandlingScope活动
EventHandlingScope活动是一个组合活动,它的作用是去容纳一组EventHandler活动,以及唯一一个其它的非基于事件的如Sequence或Parallel之类的组合活动。非基于事件的组合活动在EventHandler活动中所容纳的全部事件都已处理完毕前会一直执行。在所有这些事件都已触发并被处理完后,在该工作流的EventHandling活动外面的下一个活动才会执行。
下节将通过一个例子综合说明以上各种事件活动。