工作流编程循序渐进(8:状态机工作流)

作者  朱先忠

一、状态机工作流简介

      状态机工作流由一组状态组成。一个状态被指示为
初始状态。每个状态都可以接收一组特定事件。视事件而定,可以转换到另一个状态。状态机工作流
可以有最终状态。当转换到最终状态时,工作流将完成。

[一]状态机设计器视图

      状态机设计器是一种自由形式的设计器,这意味着可以在设计图面上自由移动活动。
状态机设计器有两个视图:
(1)“
状态”视图
(2)“
事件驱动”视图
      状态视图显示状态活动和可包含在状态活动内的事件驱动的活动。在此视图中,从一个状态到另一个状态的转换是由直线表示的,这些直线从一个状态中的事件驱动活动延伸到另一个状态。
也可以通过自己绘制直线来创建一个状态活动到另一个状态活动的转换。若要绘制转换,请你选择事件驱动的活动,然后选择活动上的某个手柄并拖动该手柄。此操作将绘制直线。此直线随后将连接到目标状态,指示状态之间的转换。如下图所示:
File?id=d25b9pk_260fb62sngq_b
拖动上图状态活动内部的事件驱动活动的手柄,拖动到下面的状态活动上即可,得到如下图所示的结果:
File?id=d25b9pk_261636kgh3n_b
      若要访问事件驱动的视图,请
双击事 件驱动的活动。出现的设计器与顺序工作流设计器很像。在设计器的顶部,导航栏显示直到所显示事件驱动活动为止的活动层次结构。可以通过单击显示的层次结构 中的任意元素导航回状态视图。如果已在状态视图中绘制了从一个状态到另一个状态的转换,并且正在显示该活动的事件驱动视图,则会为您将一个已设置状态活动 添加到事件驱动的活动。如果更改已设置状态活动的属性,它将反映到状态视图中。

[二]状态机工作流活动

      下表描述了状态机工作流设计器中使用的关键活动。
工具箱名称 活动 说明
State
表示状态机中的一个状态;可能包含其他 
StateActivity 活动。有关更多信息,请参见(可能为英文网页)。
SetState
指定到新状态的转换。有关更多信息,请参见(可能为英文网页)。
StateInitialization
在进入某个状态时执行;可能包含其他活动。有关更多信息,请参见(可能为英文网页)。
StateFinalization
在离开  活动时执行包含的活动。有关更多信息,请参见(可能为英文网页)。
EventDriven
用于依赖于外部事件开始执行的状态。
EventDrivenActivity 活动必须具有实现  接口作为第一个子活动的活动。有关更多信息,请参见(可能为英文网页)。
    
      状态机工作流中的主要组成部分是  活动。在状态机工作流中的不同位置捕获了事件时,将会进入不同的状态,以处理与这些事件关联的任务。在工作流的生存期内,工作流可能会离开和进入若干不同的状态。这些状态通过使用  活动互相连接。
      将新的 
StateActivity 拖到工作流设计图面上时,您可以添加 、、 或其他 
StateActivity 活动作为子活动。
Bb552443.Caution%28zh-cn,VS.90%29.gif警告:
使用状态机工作流设计器来创建工作流时,推荐使用“文档大纲”视图窗口来监视所设计工作流的结构。“文档大纲”视图窗口中状态机工作流结构的视图反映了工作流标记文件中活动的逻辑布局。工作流活动显示在设计图面上的物理布局可能不会反映工作流标记文件中活动的逻辑布局。
若要打开
“文档大纲”窗口,方法有二:(1)在
“视图”菜单上指向
“其他窗口”,然后选择
“文档大纲”;(2)也可以右单击某个状态活动, 然后选择
“文档大纲”。
例如下面的图形,左边显示的是
“文档大纲”,右边是相应的状态工作流。
File?id=d25b9pk_262cx2b2tcb_b

二、创建控制台状态工作流示例程序框架

请遵循如下步骤创建一个实现简单的请假流程的控制台状态工作流示例程序:
1. 启动VS2008,单击菜单”文件“|”新建“|”项目“,创建一个名字为 MyStateWF 的控制台状态工作流示例程序。
File?id=d25b9pk_263f9bwnw84_b
2.之后,系统自动打开工作流设计器界面 ,如图所示 。
File?id=d25b9pk_264hrh3bwdj_b

三、设计状态机工作流

3. 从工具箱中依次拖动四个State活动到上面的工作流设计器中。
File?id=d25b9pk_269d7fqwtf6_b
4. 依次拖动三个 eventDriven 活动到上图中上面的三个State活动中(最下面的一个不必拖入)。
File?id=d25b9pk_268gx2srngm_b
5.确定各个状态的转换关系。文章开始时提供了两种方法,显然使用拖动手柄法非常简单,结果如下:
File?id=d25b9pk_267gg2n7xgj_b
6.状态1为审批准备阶段。现在,我们来看看具体的准备工作。为此,双击上图状态1中的eventDrivenActivity1,进入到状态1的活动图中。
File?id=d25b9pk_270f5g3t4d5_b
7. 注意到,这里自动添加了SetState活动,这是前面手动拖动手柄的结果。根据开始时的描述, eventDriven活动中的第一个子活动必须为延迟活动。为此,我们作如下修改:
File?id=d25b9pk_271drvmt6cn_b
因为状态2执行结果存在两种情况,一种是得到批准;另一种是不批准。为此,创建如下的状态2活动:
File?id=d25b9pk_272dqvbcxfp_b
状态3中的活动非常简单,如下图所示:
File?id=d25b9pk_273hfcj6dhq_b

四、设计状态机中各活动的参数及有关函数

本实例中,绝大部分的代码活动仅是通过控制台输入简单的提示文字。对于上面的 左边的 IfElseBranch活动,在对应的属性对话框中指定Condition为“代码条件”,输入条件方法名为LeaveCondition ,按下回车,创建如下代码:
private void LeaveCondition(object sender, ConditionalEventArgs e)
{
    System.Threading.Thread.Sleep(5000);
    string result = Console.ReadLine();
    if (result == "0")
    {
        e.Result = false;
    }
    if (result == "1")
    {
        e.Result = true;
    }
}
下面是分散在几个代码活动中的活动对应的 ExecuteCode 属性函数的内容:
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine("这是初始状态,请假流程开始!\n");
    System.Threading.Thread.Sleep(5000);
    Console.WriteLine("开始提交请假申请\n");
}
private void codeActivity2_ExecuteCode(object sender, EventArgs e)
{
    System.Threading.Thread.Sleep(5000);
    Console.WriteLine("收到请假申请,请批示:!\n");
    Console.WriteLine("输入0:不同意请假。\n");
    Console.WriteLine("输入1:同意请假。\n");
}
private void codeActivity5_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine("请假申请结束!\n");
}
private void codeActivity3_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine("允许请假!");
}
private void codeActivity4_ExecuteCode(object sender, EventArgs e)
{
    Console.WriteLine("不允许请假!");
}
在本例中,我们使用了 System.Threading.Thread.Sleep函数,你也可以使用上面流程中的延迟函数进行设置。

四、运行实例

按F5运行控制台程序,一般顺利的话,将得到如下图所示运行时快照。
File?id=d25b9pk_274sjgn8bdx_b
从图中看出,当输入0时,整个请假流程循环从头操作。当输入1时,请假得到允许,整个流程结束。

补充

因为本例中各个状态的名称都是使用汉字表示的,所以,需要把整个状态机工作流的InitialStateName属性更改为“准备”。
File?id=d25b9pk_275cssfp2w2_b