上面学习了如何使用CodeActivity来定义工作流,现在我们来看看另外一种方式—自定义活动。自定义活动的好处是可以复用,一旦一个自定义活动定义好之后,就可以通过拖动被多个工作流调用。
每个自定义活动都应该被设计成独立的组件,他们有自己的输入和输出参数,一个活动并不知道工作流中其他活动的信息,他们之间只有通过属性值来通讯。为了支持绑定,这时要采用从属属性(Dependency Properties)来代替普通的.NET属性。
第一步:
创建一个名为APJ.Workflow.Demo1的顺序工作流控制台应用程序。再在Project菜单中新建一个Activity,名称为:LoginActivity一般来说,命名应以Activity结尾。
此LoginActivity用于接收用户输入的用户名和密码,然后通过该用户名和密码验证用户是否合法有效。因此,该LoginActivity至少需要这几个参数,即用户名、密码和返回结果三个。
我们依次添加这三个从属属性,再添加的时候有个技巧,可以通过CodeSnippet来快速添加,具体用法是,在代码的空白区点击右键,然后在弹出菜单上点击插入代码段,再依次按照下图A方式选择。最后将在光标位置自动插入一段如下图B所示的代码。

图A

图B
将其中的MyProperty改为UserId,同时修改后面的属性类型为实际需要的,这里是string不用修改。同样的,我们添加两外两个属性 UserPwd,IsUserVerified。最终添加完成的这段代码应该是这样的:
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace APJ.Workflow.Demo1
{
public partial class LoginActivity: SequenceActivity
{
public static DependencyProperty UserIdProperty = DependencyProperty.Register("UserId", typeof(string), typeof(LoginActivity));
[DescriptionAttribute("UserId")]
[CategoryAttribute("UserId Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public string UserId
{
get
{
return ((string)(base.GetValue(LoginActivity.UserIdProperty)));
}
set
{
base.SetValue(LoginActivity.UserIdProperty, value);
}
}
public static DependencyProperty UserPwdProperty = DependencyProperty.Register("UserPwd", typeof(string), typeof(LoginActivity));
[DescriptionAttribute("UserPwd")]
[CategoryAttribute("UserPwd Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public string UserPwd
{
get
{
return ((string)(base.GetValue(LoginActivity.UserPwdProperty)));
}
set
{
base.SetValue(LoginActivity.UserPwdProperty, value);
}
}
public static DependencyProperty IsUserVerifiedProperty = DependencyProperty.Register("IsUserVerified", typeof(bool), typeof(LoginActivity));
[DescriptionAttribute("IsUserVerified")]
[CategoryAttribute("IsUserVerified Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public bool IsUserVerified
{
get
{
return ((bool)(base.GetValue(LoginActivity.IsUserVerifiedProperty)));
}
set
{
base.SetValue(LoginActivity.IsUserVerifiedProperty, value);
}
}
public LoginActivity()
{
InitializeComponent();
}
}
}
第二步:
添加了属性之后,我们需要实现该活动所要实现的业务逻辑。此操作是在Execute方法中执行的,这个方法是由基类提供的虚方法,我们需要重写该方法。在活动执行时,该方法就会被工作流同步调用。
我们这个活动是要实现验证用户帐户是否有效,因此,我们在Execute中模拟用户帐户验证的过程,实际操作中,这里大多是读取数据库,查询用户名和密码进而比对以确定是否有效,我们这里仅作实例,因此只是简单的HARDCODE一些用户信息在里面。重写Execute后的代码如下:
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
switch (UserId) {
case "ryan":
IsUserVerified = (UserPwd == "123" ? true : false);
break;
case "guest":
IsUserVerified = true;
break;
default:
IsUserVerified = false;
break;
}
return base.Execute(executionContext);
}
这时按F5编译一下,然后回到Workflow1的设计界面。这时可以看到左侧的工具箱中多了一个我们刚定义的那个活动。LoginActivity。如下图所示。

图
C
第三步:
将LoginActivity拖动到Workflow1中,为了接收用户输入,我们需要为本工作流添加两个参数,用户名和用户密码,这里用普通属性即可。添加完参数之后,在转到设计界面。点击刚拖动到Workflow1中的LoginActivity活动。查看属性,可以看到开始定义的UserId和UserPwd两个属性都列在此处,点击右边的按钮,在弹出框中,将该属性绑定到Workflow1的相应属性上。

图
D
第四步:
再在LoginActivity的下面拖入一个IfElse活动。然后分别在两个活动的下面再拖入两个CodeActivity活动。分别为两个活动修改名字,操作完成之后的界面应该是这样的。

图
E
第五步:
再点击IfElse活动的左侧分支,查看属性,在Condition中选择“申明性规则条件”,点击ConditionName,在弹出框中加入这样一条条件。
this.loginActivity1.IsUserVerified == True
同样的,在IfElse活动的右侧分支,在Condition中加入相应的条件。
this.loginActivity1.IsUserVerified == False
最后再在两个CodeActivity中加入一些输出代码。
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("UserId:{0}, 登录成功!",UserId);
}
private void codeActivity2_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("UserId:{0}, 用户名验证失败!",UserId);
}
第六步:
修改program.cs中的调用代码,为WF传入参数:
class Program
{
static void Main(string[] args)
{
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();};
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
//准备该工作流需要传入的参数,参数放置在一个Dictionary对象中
Dictionary<string, object> wfArgs = new Dictionary<string, object>();
wfArgs.Add("UserId", "Ryan");
wfArgs.Add("UserPwd", "123");
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(APJ.Workflow.Demo1.Workflow1),wfArgs);
instance.Start();
//阻止当前线程,以等待异步调用的工作流完成
waitHandle.WaitOne();
//等待用户响应退出
Console.WriteLine("Press Enter to exit");
Console.ReadLine();
}
}
}
现在就可以按F5执行了。在后面可以下载该示例的全部代码。
APJ.Workflow.Demo1.rar (28.82 kb)