[置顶]坚持学习WF文章索引
一:先来介绍两个活动 EventDrivenActivity和 ListenActivity。
EventDrivenActivity是一个等侍事件触发的容器,EventDrivenActivity第一个子结点必需是一个继承 IEventActivity接口的Activity,后面所有的结点可以是任意Activity。该活动必须具有父级活动,它的父级活动可以是ListenActivity,StateActivity或StateMachineWorkflowActivity。
如果EventDrivenActvity在状态机工作流中使用,还有如下使用限制:
1. EventDrivenActivity 可以并且只能包含一个实现了IEventActivity接口的Activity.如HandleExternalEvent或Delay。
2. 如果有 HandleExternalEventActivity 必须是第一个结点.这是因为,Windows Workflow Foundation 实现的状态机工作流模型一次只处理一个EventDrivenActivity活动。例如,如果EventDrivenActivity 活动中包含多个能够运行的 IEventActivity 活动,则会出现以下情况:某个 EventDrivenActivity 活动在等待可能永远不会执行的IEventActivity 时被阻止。这样,状态机将无法处理任何其他消息。
3. 一个StateActivity状态容器可以有多个EventDrivenActivity。
ListenActivity属于单线触发容器,当某条分支中的结点执行完成后,该ListenActivity结点就结束,继续向下执行,其他分支内的结点就不执行了。EventDrivenActivity是它唯一可以添加的子活动,并且至少必须有两个子活动。该活动无法用于状态机工作流中。
二:工作流能够使用方法和事件通过消息与宿主程序交互。 事件用于将数据发送到工作流,而工作流使用方法将数据发送到主机应用程序。在坚持学习WF(8):本地服务之调用外部方法这篇中我们说明了工作流如何使用本地服务来调用外部方法,接下来我们用一个猜数字游戏的小例子来主要说明在本地服务中如何使用事件。调用外部方法和事件处理基本的流程差不多。
1.实现事件参数类,GuessReceivedEventArgs类需要继承自ExternalDataEventArgs类,ExternalDataEventArgs类中的instanceId可以保证正确的工作流实例来接收事件。
GuessReceivedEventArgs .cs
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
using System;
using System.Workflow.Activities;
![None.gif](/Images/OutliningIndicators/None.gif)
namespace GuessNumber
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
{
[Serializable]
public class GuessReceivedEventArgs : ExternalDataEventArgs
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
private Int32 _nextGuess;
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public GuessReceivedEventArgs(Guid instanceId, Int32 nextGuess)
: base(instanceId)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_nextGuess = nextGuess;
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public Int32 NextGuess
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _nextGuess; }
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _nextGuess = value; }
}
}
}
2.定义服务接口,代码如下
IGuessingGame.cs
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
using System;
using System.Workflow.Activities;
![None.gif](/Images/OutliningIndicators/None.gif)
namespace GuessNumber
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
{
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
[ExternalDataExchange]
public interface IGuessingGame
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
void SendMessage(String message);
event EventHandler<GuessReceivedEventArgs> GuessReceived;
}
}
3.实现本地服务,先定义一个事件参数。这个也继承自ExternalDataEventArgs,虽然它不是用在工作流中,但是我们为了使用InstanceId,所以也让它继承这个类。
MessageReceivedEventArgs .cs
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
using System;
using System.Workflow.Activities;
![None.gif](/Images/OutliningIndicators/None.gif)
namespace GuessNumber
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
{
[Serializable]
public class MessageReceivedEventArgs : ExternalDataEventArgs
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
private String _message;
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public MessageReceivedEventArgs(Guid instanceId, String message)
: base(instanceId)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_message = message;
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public String Message
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _message; }
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _message = value; }
}
}
}
本地服务类,这类中共有四个成员:
SendMessage:workflow使用,由工作流实例来调用,发送一个信息到宿主程序,并且激发MessageReceive事件。
GuessReceived:workflow使用,接收下一个猜的数字,当OnGuessReceived方法调用的时候激发。
MessageReceived:宿主使用,从工作流实例接收一个信息,通过SendMessage方法激发。
OnGussReceived:宿主使用,发送一个新的猜的数字到工作流,会引发GuessReceived事件。
注意:SendMessage方法使用静态的WorkflowEnvironment.WorkflowInstanceId方法取回工作流实例的ID,WorkflowEnvironment表示正在当前线程中运行的工作流实例的事务环境。
GuessingGameService.cs
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
using System;
using System.Workflow.Runtime;
![None.gif](/Images/OutliningIndicators/None.gif)
namespace GuessNumber
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
{
public class GuessingGameService : IGuessingGame
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
public void SendMessage(string message)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (MessageReceived != null)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
MessageReceivedEventArgs args
= new MessageReceivedEventArgs(
WorkflowEnvironment.WorkflowInstanceId,
message);
MessageReceived(this, args);
}
}
public event EventHandler<GuessReceivedEventArgs> GuessReceived;
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public void OnGuessReceived(GuessReceivedEventArgs args)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (GuessReceived != null)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
//must pass null as the sender otherwise
//the correct workflow won't receive the event.
GuessReceived(null, args);
}
}
}
}
4.设计工作流,在工作流的Initialized事件中初始化这个要猜的随机数。
private
void
OnInitialized(
object
sender, EventArgs e)
![None.gif](/Images/OutliningIndicators/None.gif)
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
Random random = new Random();
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
_theNumber = random.Next(1, 10);
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
Message = "Please guess a number between 1 and 10.";
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
}
Message属性给宿主的程序的暗示信息,指示猜的数字是大了还是小了。IsComplete属性是WhileActivity使用的,决定工作流是否完成了。
通过活动CallExternalMethodActivity设置InterfaceType和MethodName,还有message属性。
GuessReceived事件:在GuessReceived被激发的时候执行handleExternalEventActivity1_Invoked。该事件中决定用户的猜测是大了还是小了,为了完成工作流你必须处理该事件永远不发生的情况,我们要拖一个CodeActivity在右边的Event-Driven中,设置事件为一分钟,然后在拖一个TeminalActivity,如果一分钟内没猜的话就结束了。
完整的工作流如下图:
完整代码如下
GuessingGameWorkflow.cs
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
using System;
using System.Workflow.Activities;
![None.gif](/Images/OutliningIndicators/None.gif)
namespace GuessNumber
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
{
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// The guessing game workflow
/// </summary>
public sealed partial class GuessingGameWorkflow
: SequentialWorkflowActivity
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![ContractedSubBlock.gif](/Images/OutliningIndicators/ContractedSubBlock.gif)
Variables and Properties#region Variables and Properties
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
private Int32 _theNumber;
private Boolean _isComplete = false;
private String _message = String.Empty;
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public String Message
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _message; }
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _message = value; }
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public Boolean IsComplete
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _isComplete; }
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{ _isComplete = value; }
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
#endregion
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
public GuessingGameWorkflow()
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
InitializeComponent();
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
private void OnInitialized(object sender, EventArgs e)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Random random = new Random();
_theNumber = random.Next(1, 10);
Message = "请输入1到10之间的某一个数字.";
}
![InBlock.gif](/Images/OutliningIndicators/InBlock.gif)
private void handleExternalEventActivity1_Invoked(
object sender, ExternalDataEventArgs e)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
GuessReceivedEventArgs eventArgs
= e as GuessReceivedEventArgs;
if (eventArgs != null)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (eventArgs.NextGuess < _theNumber)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Message = "请输入一个更大的数字.";
}
else if (eventArgs.NextGuess > _theNumber)
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Message = "请输入一个更小的数字.";
}
else
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Message = String.Format(
"恭喜您,猜对了{0}.", _theNumber);
IsComplete = true;
}
}
}
}
}
实现宿主程序,代码如下
Form1.cs
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>-->
using System;
using System.Windows.Forms;
![None.gif](/Images/OutliningIndicators/None.gif)
using System.Workflow.Activities;
using System.Workflow.Runtime;
![None.gif](/Images/OutliningIndicators/None.gif)
![None.gif](/Images/OutliningIndicators/None.gif)
namespace GuessNumber
![ExpandedBlockStart.gif](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![ContractedBlock.gif](/Images/OutliningIndicators/ContractedBlock.gif)
{
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// The WinForm for the number guessing game
/// </summary>
public partial class Form1 : Form
![ExpandedSubBlockStart.gif](/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{