《WF编程》系列之37 – 打开黑盒子:属性升级

组合活动就像一个黑盒子,工作流设计器无法获取这个黑盒子内部的属性和事件,除非我们把这些属性和事件曝露给外部世界.属性升级(Property promotion)允许组合活动的设计者去决定属性对于外部世界的可见性.

5.3.1.1 属性升级 (PROPERTY PROMOTION)

属性升级会在父活动属性和子活动属性之间建立连接.我们可以这样去理解,为了曝露这个属性(或者事件),我们将它从活动树的下面移动到顶端,这个操作就叫做属性升级.下面回到自定义活动的项目中来看看如何升级属性.

首先,我们来确定需要升级的成员.在CallExternalMethod活动的四个”候选属性”(MethodInvoking事件,ReturnValue,id和UserName参数)中,我们选择只曝露userName参数.曝露userName属性之后,工作流就可以设置它的值, CallExternalMethod就能将这个值传递给宿主.其它三个”候选属性”则可以在组合活动的内部来实现.例如,自定义活动可以很容易的得到工作流实例的ID,而ReturnValue并不需要工作流的干预.这也是我们选择只曝露userName的理由,userName参数是该活动唯一一个必须从工作流获取的参数.

想要升级userName,我们需要打开自定义活动,在设计器中选中CallExternalMethod活动,然后在其属性面板中点击userName域旁边的省略号按钮.上述操作会弹出如下图所示的对话框:

属性升级会在我们的自定义活动中增加一个新的属性:UserName.当我们点击了OK,设计器就会自动完成一系列动作.而第一步,设计器会生成一段依赖属性的代码.

当然,我们也可以手工进行属性升级.不妨先看看设计器生成的代码:

public static DependencyProperty UserNameProperty = DependencyProperty.Register("UserName", typeof(System.String), typeof(GetUploadActivity.GetUpload));

[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public String UserName
{
get
{
return ((string)(base.GetValue(GetUploadActivity.GetUpload.UserNameProperty)));
}
set
{
base.SetValue(GetUploadActivity.GetUpload.UserNameProperty, value);
}
}

依赖属性包含一些元数据,它们被标识为特性,比如BrosableAttribute.元数据的BrosableAttribute会告诉工作流设计器让用户可以在属性面板看到这个属性.在以后的章节中,我们还会对依赖属性进行更详细的介绍,下面来接着看看设计器生成依赖属性的代码之后还会做哪些操作.

设计器的第二个工作就是将CallExternalMethod活动userName参数和刚才为父活动添加的依赖属性UserName关联起来.当我们关闭了绑定对话框后,就可以在属性面板中看到如下图所示的userName属性.这就是WF的活动数据绑定语法.活动数据绑定告诉Runtime当要获取userName这个参数的值时就去去找GetUploadActivity的UserName属性.现在,属性升级已经在userName参数和UserName依赖属性之间建立了连接.

注意属性面板的底部,我们看到一个Promote Bindable Properties(升级可绑定属性)命令.这个命令会为活动中的所有参数和事件生成依赖属性并经行活动数据绑定生(在我们的例子中我们只需要曝露userName,所以可以不用执行此命令).

现在工作流可以设置自定义活动的UserName属性了,而且它的值会传递给(绑定到)CallExternalMethod活动的userName参数.除此之外,为了使自定义活动工作,我们需要向外部方法传递一个id参数(即工作流实例的ID).由于这个参数没有升级,所以它对于外部世界来说是不可见的,我们需要自己为它指定正确的值.将下面的代码添加到自定义活动中:

private void requestUpload_MethodInvoking(object sender, EventArgs e)
{
uploadID = this.WorkflowInstanceId;
}
Guid uploadID = default(System.Guid);

这段代码定义了一个私有变量uploadID,用来保存工作流实例的ID.我们在一个叫做requestUpload_MethodInvoking的方法里设置它的值,并打算在CallExternalMethod活动调用宿主方法前执行它.

为了使它工作,我们需要在属性面板建立更多的绑定.在下一节中,我们会将CallExternalMethod活动的MethodInvoking属性绑定到刚刚编写的requestUpload_MethodInvoking方法.这个方法会在这个活动调用外部方法之前执行.同时我们还会绑定活动的id参数到私有变量uplaodID.

接着,我们的自定义活动还要升级HandleExternalEvent活动的属性.譬如,如果工作流需要传入参数中的文件名,我们就需要一个依赖属性来曝露这个文件名.为了达到这个目的,我们可以升级整个事件参数(e),或者创建一个依赖属性并只曝露事件参数中的文件名部分.

5.3.2 组合活动小结

我们在工作流中创建了一个可以重用的部分-自定义活动.我们可以拖拽这个活动到任何工作流中,摆脱了拖拽并配置两个活动的麻烦.而且,升级后的属性可以供工作流设计人员使用.他们针对不同的任务传递不同的参数.下图演示了我们的自定义活动在新的工作流中使用的情景.

在我们介绍另外一种创建自定义活动的方法之前,我们还需要了解依赖属性和活动数据绑定的相关知识,因为在自定义活动和工作流的基本架构中,它们都很重要并且相互关联.

5 Comments

  1. 不知道能不能提供一些,WF状态机用法的相关资料,我在公司的最近的一个项目上已经使用了WF,不过是顺序流,对于状态机我个人感觉资料很少

  2. marcher

    楼主,能不能快点把这个系列写完啊,我打算用它给我的团队做培训,马上要启动项目了,要用WF,什么时候能全部翻译完?

  3. @marcher
    这么看重这个系列的文章哦,真是感动,呵呵.
    这个系列目前才翻译到一半了,照现在蜗牛般的速度,估计还得很久.

发表评论

电子邮件地址不会被公开。 必填项已用*标注