WWF包含了一组丰富的通用用户活动,这些能满足绝大多数的场景的需求。但有时,我们还是会碰到需要一些更加自定义的活动的场景。 WWF SDK拥有一些可扩展的特性,能使你轻松的创建自定义的活动,并把他们应用到你的解决方案中。在这一节中,我们会编写一个自定义活动 ,用来根据传入工作流的参数,从网页中下载文本。
创建一个网页解析自定义活动
这个自定义活动会根据传入活动的网页属性集,从网页中下载文本。页面一旦下载完毕,活动就会发出一个网页下载完毕的事件,并把网页数据值发回工作流。
创建WebTearActivity类
WebTear类继承自System.Workflow.ComponentModel.Activity 类 。当你定义了一个自定义活动时,把 ToolboxItemAttributes 属性 应用到类上,并指明为ActivityboxItem 类型
。以下的代码就是一个最小的自定义活动。
using
System; using
System.ComponentModel; using
System.Workflow.ComponentModel; using
System.Workflow.ComponentModel.Design; using
System.Workflow.ComponentModel.Compiler; namespace
Microsoft.Samples.Workflow.Quickstarts.CustomActivity
{ [ToolboxItemAttribute( typeof (ActivityToolboxItem))] public class WebTear : System.Workflow.ComponentModel.Activity { } }
定义活动属性
自定义活动属性的定义和WinForm控件属性的定义类似。当你定义一个自定义活动的属性时,使用DependencyProperty 来存储属性的属性。这能让各种不同类型的属性都能一致地有效地工作。在实际定义属性时,你可能还会用到DependencyProperty 类 中的方法,所以,不要把这个字段定义成private。
活动中的一个属性可以应用许多不同的特性。这些特性通常都是用来支持WWF设计器的工作的。我们的这个属性应用了一下的特性:
1. CategoryAttribute :在设计器的属性窗口中,这个属性位于那个分类下。
2. DescriptionAttribute :设计器属性窗口中显示的属性描述。
3. BrowsableAttribute :这是一个布尔值,指示了是否在属性窗口中显示这个属性。
4. ValidationVisibilityAttribute :指定了如何验证属性的值。验证选项包括:可选Optional、必需Required、隐藏Hidden。
5. DesignerSerializationVisibilityAttribute :指定了是否和如何序列化这个属性。选项包括:可见Visible、隐藏Hidden、内容Content。
以下的代码示例了如何定义WebPage
属性。
public static DependencyProperty WebPageProperty =
DependencyProperty.Register(
" WebPage " , typeof (System.String), typeof
(Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear ) ); [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)] [ValidationVisibilityAttribute(ValidationVisibility.Optional)] [BrowsableAttribute( true
)] [DescriptionAttribute( " Web page to download "
)] [CategoryAttribute( " WebTear Property "
)] public string
WebPage
{ get { return (( string )( base .GetValue(Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear.WebPageProperty))); } set { base .SetValue(Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear.WebPageProperty, value); } }
重载Execute方法
当你创建了一个自定义活动,运行时,工作流引擎会调用活动的 Execute 方法 来执行活动实际的操作。这个方法是在基类中定义的,但你可以重载这个方法,以适应你的活动的功能。
WebTear活动会在Execute 方法 中下载请求的网页,并在下载完成后引发一个事件,将页面数据发回工作流。我们需要定义一个PageFinishedEventArgs类用于事件参数,这个类中有一个Data字段用来存放网页数据。这些数据会在之后被工作流中的事件处理函数访问到。一旦数据下载完毕,Execute方法返回一个Status.Closed 枚举值 ,来通知工作流引擎活动已经完成执行了。
一下的代码演示了如何定义PageFinishedEventArgs
类
public class
PageFinishedEventArgs
{ private string data; public PageFinishedEventArgs( string data ) { this .data = data; } public string Data { get { return data; } } }
以下的代码演示了如何实现Execute
方法。
public delegate void PageFinishedEventHandler( object
sender, PageFinishedEventArgs e ); public event
PageFinishedEventHandler PageFinished; protected override
Status Execute(ActivityExecutionContext context)
{ System.Net.WebClient client = new System.Net.WebClient(); string pageData; try { // Download the web page data pageData = client.DownloadString(WebPage); } catch (Exception e) { pageData = e.Message; } // Raise the PageFinished event back to the host PageFinished( null , new PageFinishedEventArgs(pageData)); // Notifiy the runtime that the activity has finished return Status.Closed; }
为WebTear活动创建顺序工作流。
此时,我们已经创建好了下载网页的自定义活动。现在我们创建一个简单的顺序工作流,来测试一下这个自定义活动的功能。
这个工作流只包含一个活动,就是我们的WebTear活动。要下载的网页地址被传到工作流的Parameters 集合 中,这个的值用来设置活动的WebPage属性。最后,当活动结束下载后,触发事件,工作流会从事件参数中提取网页数据,并把数据通过Parameters 集合 中的out 参数
发回宿主程序。以下代码演示了顺序工作流的实现
using
System; using
System.ComponentModel; using
System.Workflow.ComponentModel; using
System.Workflow.ComponentModel.Design; using
System.Workflow.ComponentModel.Compiler; using
System.Workflow.Activities; namespace
Microsoft.Samples.Workflow.Quickstarts.CustomActivity
{ public sealed partial class WebTearActivityWorkflow : SequentialWorkflow { private string webSite; private WebTear webTear1; public WebTearActivityWorkflow() { InitializeComponent(); } private void InitializeComponent() { System.Workflow.ComponentModel.ActivityBind activitybind1 = new System.Workflow.ComponentModel.ActivityBind(); System.Workflow.ComponentModel.ParameterDeclaration WebPage = new System.Workflow.ComponentModel.ParameterDeclaration(); System.Workflow.ComponentModel.ParameterDeclaration PageData = new System.Workflow.ComponentModel.ParameterDeclaration(); this .webTear1 = new Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear(); // // webTear1 // this .webTear1.ID = " webTear1 " ; activitybind1.ID = " /Workflow " ; activitybind1.Path = " webSite " ; this .webTear1.PageFinished += new Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear.PageFinishedEventHandler( this .webTear1_PageFinished); this .webTear1.SetBinding(Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTear.WebPageProperty, ((System.Workflow.ComponentModel.Bind)(activitybind1))); // // WebTearActivityWorkflow // this .Activities.Add( this .webTear1); this .DynamicUpdateCondition = null ; this .ID = " WebTearActivityWorkflow " ; WebPage.Direction = System.Workflow.ComponentModel.ParameterDirection.In; WebPage.Name = " WebPage " ; WebPage.Type = typeof (System.String); WebPage.Value = null ; PageData.Direction = System.Workflow.ComponentModel.ParameterDirection.Out; PageData.Name = " PageData " ; PageData.Type = typeof (System.String); PageData.Value = null ; this .Parameters.Add(WebPage); this .Parameters.Add(PageData); this .Initialized += new System.EventHandler( this .InitVars); } private void InitVars( object sender, EventArgs e) { webSite = this .Parameters[ " WebPage " ].Value.ToString(); } private void webTear1_PageFinished( object sender, PageFinishedEventArgs e) { this .Parameters[ " PageData " ].Value = e.Data; } } }
创建宿主程序
这里的宿主程序是一个Winform程序,它有一个TextBox来输入要下载的url,当点击Go按钮,工作流开始运行,并执行WebTear活动开始下载网页。工作流运行完后,宿主程序从WorkflowCompleteEventArgs对象中获得网页数据,并把它显示在另一个TextBox中。以下代码演示了如何实现宿主程序(译者注:私自去掉了不重要的代码,要看完整的自个儿去msdn
上找,呵呵)。
…… namespace
Microsoft.Samples.Workflow.Quickstarts.CustomActivity
{ public class MainForm : Form { private System.Windows.Forms.Label addressCaption; private System.Windows.Forms.TextBox address; private System.Windows.Forms.TextBox data; private System.Windows.Forms.Button goButton; private WorkflowRuntime workflowRuntime; public MainForm() { InitializeComponent(); workflowRuntime = new WorkflowRuntime(); workflowRuntime.StartRuntime(); workflowRuntime.WorkflowCompleted += new EventHandler < WorkflowCompletedEventArgs > (workflowRuntime_WorkflowCompleted); } void workflowRuntime_WorkflowCompleted( object sender, WorkflowCompletedEventArgs e) { // Retrieve the downloaded page data if (data.InvokeRequired) data.Invoke( new EventHandler < WorkflowCompletedEventArgs > (workflowRuntime_WorkflowCompleted), sender, e); else data.Text = e.OutputParameters[ " PageData " ].ToString(); } private void goButton_Click( object sender, EventArgs e) { Type type = typeof (Microsoft.Samples.Workflow.Quickstarts.CustomActivity.WebTearActivityWorkflow); // Sending the data to the workflow. // First create the required property set Dictionary < string , object > properties = new Dictionary < string , object > (); properties.Add( " WebPage " , address.Text); properties.Add( " PageData " , "" ); workflowRuntime.StartWorkflow(type, properties); } …… } }