WWF将工作流分为两大类:

  • 面向Human:在工作流运行时通过用户对外部应用程序的操作来影响工作流的业务流转。
  • 面向System:应用程序控制流程。

  工作流与应用程序都是可以单独存在的,因此它们之间的数据交互需要通过接口来实现。工作流、应用程序、接口程序之间的数据交互模型如下图:

  

  工作流的实例存在于一个工作流运行时的Runtime容器中,但是用于数据交互的接口项目和应用程序都不存在于该工作流的Runtime容器中。WWF提供了"System.Workflow.Activities.ExternalDataExchangeService"专门用于它们的数据交互。用户可以把外部应用程序加载到"ExternalDataExchangeService"服务中,然后再将该服务加载到工作流运行时的Runtime容器中,那么这时工作流的实例和外部应用程序都处于同一个容器里,这样就可以进行数据交互了。

  

一、HandleExternalEvent(外部程序调用工作流)

  HandleExternalEvent是一个事件类型的活动。CallExternalEvent和HandleExternalEvent活动与应用程序之间的相互调用关系如下:

  

  HandleExternalEvent是通过一个"ExternalDataExchangeService"服务将应用程序注册到工作流运行时容器Runtime中,并通过一个继承了"ExternalDataExchange"的接口项目来实现外部应用程序对工作流的调用。但是HandleExternalEvent活动在接口中定义的是Event事件而不是普通的函数或方法。当工作流执行到HandleExternalEvent活动后会进入到"idle"状态。

  以下示例主要演示外部调用工作流引擎。

  示例:创建一个控制台程序,一个顺序工作流,一个Winform程序。

  控制台程序仅仅一个接口:

namespace ClassLibrary1
{
[ExternalDataExchange] //using System.Workflow.Activities;
public interface IEvent
{
event EventHandler<ExternalDataEventArgs> MyEvent1;
}
}

  

  

  InterfaceType属性将活动HandleExternalEvent与接口进行绑定。

  Invoked添加一个事件,弹出对话框提示工作流是否已被调用。

  工作流代码:

    public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
} private void Call(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("工作流已被调用!");
}
}

  窗口程序代码:

   public partial class Form1 : Form,ClassLibrary1.IEvent
{
private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime;
private WorkflowInstance wfInstance = null;
private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities;
public event EventHandler<ExternalDataEventArgs> MyEvent1;
public Form1()
{
InitializeComponent(); wfRuntime = new WorkflowRuntime();
externalService = new ExternalDataExchangeService(); wfRuntime.AddService(externalService);
externalService.AddService(this);
wfRuntime.StartRuntime();
} private void button1_Click(object sender, EventArgs e)
{
wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
wfInstance.Start();
} private void button2_Click(object sender, EventArgs e)
{
ExternalDataEventArgs guid = new ExternalDataEventArgs(wfInstance.InstanceId);
MyEvent1(null, guid);
}
}

  显示:

  

二、CallExternalMethod(工作流调用外部程序)

  在实际项目中除了应用程序调用工作流外,工作流也可能需要调用应用程序,以便把工作流运行结果反馈给操作用户。WWF提供了CallExternalMethod活动来实现这个功能。

  注意:HandleExternalEvent范例中接口定义的内容不同,外部应用程序是通过接口定义的“事件”来调用工作流项目,而工作流项目则是通过接口中定义的“方法”来调用应用程序的。

  首先添加一个接口代码如下:

    [ExternalDataExchange]  //using System.Workflow.Activities;
public interface ICallExtennalMethod
{
void MyCallExternalMethod();
}

  工作流如下:

  

  设置属性如下:

  

  窗体代码如下:

    public partial class Form1 : Form, ICallExtennalMethod
{
private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime;
private WorkflowInstance wfInstance = null;
private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public Form1()
{
InitializeComponent(); wfRuntime = new WorkflowRuntime();
externalService = new ExternalDataExchangeService(); wfRuntime.AddService(externalService);
externalService.AddService(this);
wfRuntime.StartRuntime();
} private void button1_Click(object sender, EventArgs e)
{
//创建一个CallExternalMethod_Workflow工作流实例
wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
//启动工作流实例
wfInstance.Start();
} private void button2_Click(object sender, EventArgs e)
{
ExternalDataEventArgs guid = new ExternalDataEventArgs(wfInstance.InstanceId);
} void ICallExtennalMethod.MyCallExternalMethod()
{
MessageBox.Show("工作流通过接口调用应用程序中的方法成功!");
} private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
wfRuntime.StopRuntime();
}
}

  运行如下:

  

三、Listen(排他事件分支)

  • Listen是一个容器类型活动;
  • 专门用于存放事件类型活动;
  • Listen可以由多条分支,每条分支都是由EventDriven容器类型活动构成,但它不能通过逻辑判断条件来确定哪条分支将被执行;
  • Listen活动在工作流运行时处于事件的监听状态,哪条分支上的事件被触发,哪条分支就将被执行,而其他分支将不再被执行,即只有一条分支可以被执行,分支与分支之间是具有“排他”性的;
  • Listen活动每条分支上的第一个子活动一定为事件类型的活动;

  Listen活动默认情况下有两条分支,在很多实际的项目中Listen活动的其中一条分支往往使用Delay活动作为默认分支,在工作流运行时如果事件始终未触发,那么用户可以通过Delay活动设置一个超时的时间,当Listen活动超时后工作流可以继续执行其他后续工作。

  Listen活动的每条分支都是由EventDriven活动构成的,因此在EventDriven容器中的第一个活动必须是事件类型活动,否则系统会给出错误提示。

  示例:

  首先定义一个接口如下:

    [ExternalDataExchange]  //using System.Workflow.Activities;
public interface IListen
{
event EventHandler<ExternalDataEventArgs> ListenEvent1;
event EventHandler<ExternalDataEventArgs> ListenEvent2;
}

  工作流如下:

  

  设置属性如下:

  

  代码如下:

   public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
} private void EventInvoked1(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("执行HandleEvent1!");
} private void EventInvoked2(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("执行HandleEvent2!");
} private void Code1(object sender, EventArgs e)
{
MessageBox.Show("工作流即将结束!");
}
}

  设计窗口程序如下:

  

  代码如下:

    public partial class Form1 : Form, ClassLibrary1.IListen
{
private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime;
private WorkflowInstance wfInstance = null;
private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public event EventHandler<ExternalDataEventArgs> ListenEvent1;
public event EventHandler<ExternalDataEventArgs> ListenEvent2; public Form1()
{
InitializeComponent(); //创建工作流的运行时容器
wfRuntime = new WorkflowRuntime();
externalService = new ExternalDataExchangeService(); //加载外部数据交互服务
wfRuntime.AddService(externalService);
externalService.AddService(this);
wfRuntime.StartRuntime();
} private void button1_Click(object sender, EventArgs e)
{
//创建一个CallExternalMethod_Workflow工作流实例
wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
//启动工作流实例
wfInstance.Start(); btnEvent1.Enabled = true;
btnEvent2.Enabled = true;
} private void btnEvent1_Click(object sender, EventArgs e)
{
ExternalDataEventArgs args = new ExternalDataEventArgs(wfInstance.InstanceId);
ListenEvent1(null,args); btnEvent1.Enabled = false;
btnEvent2.Enabled = false;
} private void btnEvent2_Click(object sender, EventArgs e)
{
ExternalDataEventArgs args = new ExternalDataEventArgs(wfInstance.InstanceId);
ListenEvent2(null, args); btnEvent1.Enabled = false;
btnEvent2.Enabled = false;
}
}

  点击按钮显示如下:

   

四、Delay(定时超时)

  Delay活动是一个事件类型的活动,它与HandleExternalEvent活动有所不同。Delay是由自身触发事件的,HandleExternalEvent活动是依赖外部来触发事件的。Delay活动常作为HandleExternalEvent等时间类型活动的补充,当一段时间内工作流没有被外部事件触发时,就认为工作流出现超时,那么继续执行后续的活动。该活动可以触发工作流的"WorkflowIdled"事件,使工作流进入"idle"状态。

  示例:

  定义一个接口如下:

    [ExternalDataExchange]  //using System.Workflow.Activities;
public interface IListen
{
event EventHandler<ExternalDataEventArgs> ListenEvent1;
}

  设计工作流如下:

  

  

  代码如下:

    public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
} private void EventInvoked1(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("执行HandleEvent1!");
} private void Code1(object sender, EventArgs e)
{
MessageBox.Show("工作流即将结束!");
}
}

  启动Winform程序,当5秒内不点击,则自动弹出工作流即将结束对话框。

  

五、EventHandlingScope(无排他分支)

  EventHandlingScope活动是一个容器类型的活动,它与Listen类似,每个分支都是由EventDriven容器类型的活动构成。 它与Listen活动的最大区别是EventHandlingScope活动有且只有一个主活动,而Listen活动却没有主活动的概念。 Listen活动每条分支之间都是平行关系,只要有一个分支监听到事件,其他分支就都不会被执行。而EventHandlingScope活动是在其活动运行结束前,它的所有事件的分支都可以被执行,不会出现“排他”的现象,该活动所监听的事件都存放在"Event Handlers"视图里。下面通过一个具体范例来对EventHandlingScope活动进行讲解。

  下面以一个20秒内的投票系统作为示例:

  首先定义一个接口如下:

    [ExternalDataExchange]  //using System.Workflow.Activities;
public interface IEvent
{
event EventHandler<ExternalDataEventArgs> Approved;
event EventHandler<ExternalDataEventArgs> Rejected;
}

  设计工作流如下:

  

  顺序工作流:=》Code活动=》EventHandlingScope(里面放一个Delay,时间设为20秒),然后又是一个Code活动。

  然后,在EventHandlingScope切换到事件视图:

  

  然后分别放入两个EventDriven,再在EventDriven里面分别放入两个HandleExternalEvent。

  最里面的HandleExternalEvent属性设置如下:

  

  工作流代码如下:

    public sealed partial class Workflow1 : SequentialWorkflowActivity
{
private string approvednum = "";
private string rejectednum = ""; public Workflow1()
{
InitializeComponent();
} private void ApproveEvent(object sender, ExternalDataEventArgs e)
{
ClassLibrary1.Voter vo = (ClassLibrary1.Voter)e;
approvednum = vo.VoterNum;
} private void RejectedEvent(object sender, ExternalDataEventArgs e)
{
ClassLibrary1.Voter vo = (ClassLibrary1.Voter)e;
rejectednum = vo.VoterNum;
} private void StartExecute(object sender, EventArgs e)
{
MessageBox.Show("工作流已启动!");
} private void TotalExecute(object sender, EventArgs e)
{
if (approvednum != "" && rejectednum != "")
{
MessageBox.Show("20秒内投赞成票人数:" + approvednum + ",投反对票人数:" + rejectednum);
}
else
{
MessageBox.Show("20秒内没有人投票!");
}
}
}

  窗体程序设计如下:

  

  窗体程序代码如下:

    public partial class Form1 : Form, ClassLibrary1.IEvent
{
private WorkflowRuntime wfRuntime = null; //using System.Workflow.Runtime;
private WorkflowInstance wfInstance = null;
private ExternalDataExchangeService externalService = null; //using System.Workflow.Activities; public event EventHandler<ExternalDataEventArgs> Approved;
public event EventHandler<ExternalDataEventArgs> Rejected; public Form1()
{
InitializeComponent(); //创建工作流的运行时容器
wfRuntime = new WorkflowRuntime();
externalService = new ExternalDataExchangeService(); //加载外部数据交互服务
wfRuntime.AddService(externalService);
externalService.AddService(this);
wfRuntime.StartRuntime();
} private void button1_Click(object sender, EventArgs e)
{
//创建一个CallExternalMethod_Workflow工作流实例
wfInstance = wfRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));
//启动工作流实例
wfInstance.Start();
} private void btnApprove_Click(object sender, EventArgs e)
{
Approved(null, new ClassLibrary1.Voter(wfInstance.InstanceId, this.numericUpDown1.Value.ToString()));
} private void btnRejected_Click(object sender, EventArgs e)
{
Rejected(null, new ClassLibrary1.Voter(wfInstance.InstanceId, this.numericUpDown1.Value.ToString()));
}
}

  输出如下:

  当20秒内投了票。

  

  当20秒内没人投票时:

  

六、Parallel(平行顺序)

  Parallel活动是一个容器类型的活动,该活动的每条分支都是由顺序活动构成的。在Parallel活动运行时,只有每条分支全部执行完毕后该活动才结束,工作流也才能执行Parallel活动的其他后续活动,否则工作流就将一直等待直到Parallel活动结束。

  平行前面已经在例子中说明过。Parallel活动被广泛运用于工作流中的“会签”流程。

  首先定义一个接口如下:

    [ExternalDataExchange]  //using System.Workflow.Activities;
public interface IEvent
{
event EventHandler<ExternalDataEventArgs> CEOEvent;
event EventHandler<ExternalDataEventArgs> CTOEvent;
event EventHandler<ExternalDataEventArgs> CFOEvent;
}

  添加一个工作流如下:

  

  注意,默认Parallel为两个分支,可以右键"添加分支"增加一个。

  属性设置:

  

  工作流代码如下:

    public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
} private void Code1(object sender, EventArgs e)
{
MessageBox.Show("会签结束!");
} private void CEOInvoked(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("CEO签字!");
} private void CTOInvoked(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("CTO签字!");
} private void CFOInvoked(object sender, ExternalDataEventArgs e)
{
MessageBox.Show("CFO签字!");
}
}

  增加一个控制台程序如下:

  

  以上代码实现的效果是,当三个按钮都点击,即3个XXO都签字之后,就会弹出会签结束,XXO之间的签字顺序无影响,否则一直不会弹出结束。

  

WWF3事件类型活动<第三篇>的更多相关文章

  1. 深入理解DOM事件类型系列第三篇——变动事件

    × 目录 [1]删除节点 [2]插入节点 [3]特性节点[4]文本节点 前面的话 变动(mutation)事件能在DOM中的某一部分发生变化时给出提示,这类事件非常有用,但都只能使用DOM2级事件处理 ...

  2. 深入理解DOM事件机制系列第三篇——事件对象

    × 目录 [1]获取 [2]事件类型 [3]事件目标[4]事件代理[5]事件冒泡[6]事件流[7]默认行为 前面的话 在触发DOM上的某个事件时,会产生一个事件对象event,这个对象中包含着所有与事 ...

  3. WWF3事务和异常处理类型活动<第四篇>

    一.FaultHandler 添加一个工作流图如下: 首先添加一个Seruence,在里面添加3个Code,外面添加一个Code,打开Seruence错误处理,在容器里添加一个faultHandler ...

  4. 深入理解DOM事件类型系列第四篇——剪贴板事件

    × 目录 [1]定义 [2]对象方法 [3]应用 前面的话 剪贴板操作可能看起来不起眼,但是却十分有用,可以增强用户体验,方便用户操作.本文将详细介绍剪贴板事件 定义 剪贴板操作包括剪切(cut).复 ...

  5. 深入理解DOM事件类型系列第六篇——加载事件

    前面的话 提到加载事件,可能想到了window.onload,但实际上,加载事件是一大类事件,本文将详细介绍加载事件 load load事件是最常用的一个事件,当页面完全加载后(包括所有图像.java ...

  6. 深入理解DOM事件类型系列第五篇——文本事件

    × 目录 [1]change [2]textInput [3]input[4]propertychange[5]兼容 前面的话 如果DOM结构发生变化,触发的是变动事件:如果文本框中的文本发生变化,触 ...

  7. 第三篇 :微信公众平台开发实战Java版之请求消息,响应消息以及事件消息类的封装

    微信服务器和第三方服务器之间究竟是通过什么方式进行对话的? 下面,我们先看下图: 其实我们可以简单的理解: (1)首先,用户向微信服务器发送消息: (2)微信服务器接收到用户的消息处理之后,通过开发者 ...

  8. WWF3自定义活动<第八篇>

    WWF提供了对原有活动进行扩展以及自定义新活动的功能,用户可以通过"Workflow Activity Library"创建和开发自定义活动. 一.自定义活动类型 默认情况下,创建 ...

  9. 深入理解DOM事件类型系列第二篇——键盘事件

    × 目录 [1]类型 [2]顺序 [3]按键信息[4]应用 前面的话 鼠标和键盘是电脑端主要的输入设备,上篇介绍了鼠标事件,本文将详细介绍键盘事件 类型 键盘事件用来描述键盘行为,主要有keydown ...

随机推荐

  1. Report_报表中Ref Cursor数据源的概念和用法(案例)

    2014-06-22 Created By BaoXinjian

  2. PDF内容不允许复制的解决方法!

    PDF文档的内容不允许复制解决方法! PDF的加密有两个级别: 一个是Owner级别,就是打开文档时需要输入密码PDF Password Remover可以用来破解Owner级别的口令,但是不能用于破 ...

  3. Java注解教程及自定义注解

    Java注解提供了关于代码的一些信息,但并不直接作用于它所注解的代码内容.在这个教程当中,我们将学习Java的注解,如何定制注解,注解的使用以及如何通过反射解析注解. Java1.5引入了注解,当前许 ...

  4. Java多线程之阻塞I/O如何中断

    阻塞的I/O线程在关闭线程时并不会被打断,需要关闭资源才能打断. 1.执行socketInput.close();阻塞可中断.2.执行System.in.close();阻塞没有中断. package ...

  5. How to push your code in git

    1. display all the branches git branch -a 2. delete branches git br -d <branch> # 删除某个分支 git b ...

  6. Move Zeroes

    https://leetcode.com/problems/move-zeroes/ Given an array nums, write a function to move all 0's to ...

  7. android 定时执行一个任务

    1. timer = new Timer(true) TimerTask task =  new TimerTask(){ public void run(){ test(); } } timer.s ...

  8. [ActionScript 3.0] AS3 GUID(全局唯一标识符)

    package com.controls { import flash.display.Sprite; import flash.system.Capabilities; public class G ...

  9. SQL日期格式

    ) :: ),'-',''),' ',''),':','') ) , ) ) , ) ) , ) ) , ) 其它不常用的日期格式转换方法: ) , ) ) , ) ) , ) ) , ) ) , ) ...

  10. easyUi中的一段漂亮代码之将list转换成tree.

    function convert(rows){ function exists(rows, parentId){ for(var i=0; i<rows.length; i++){ if (ro ...