代码改变世界

《WF编程》系列之8 - 工作流编写方式:纯XAML

2007-04-06 09:25  Windie Chai  阅读(...)  评论(...编辑  收藏

2.3 纯XAML

下面是一段纯XAML的工作流定义:

<SequentialWorkflowActivity 

  
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" 

  xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"  

  x:Class
="MyWorkflow">

  
<WhileActivity>

    
<CodeActivity />

  
</WhileActivity>

</SequentialWorkflowActivity>

这段XAML的根元素是SequentialWorkflowActivity.WF的根活动可以是SequentialWorkflowActivity或StateMachineWorkflowActivity.这两个类以不同方式管理其子活动的执行过程.SequentialWorkflowActivity顺序地执行它的子活动直到最后一个活动完成.所以在顺序工作流(Seqential Workflow)中活动的先后顺序很重要,顺序决定了活动什么时候被执行.关于StateMachineWorkflowActivity将在以后的章节详述.  

定义工作流的XAML文件的扩展名是.xoml.

我们的工作流中只有一个包含CodeActivity的WhileActivity.定义这个工作流的XAML其实就是将工作流中的对象以”树”的形式表示出来.XAML在循环内部创建了一个CodeActivity,我们没有定义任何代码和While的条件,所以这个工作流暂时还不能通过验证.

我们像讨论类一样讨论XML元素(如<WhileActivity>),其实这些元素就是类.因为XAML是通过将XML映射到.NET类型来工作的,其中XAML的元素映射到类,元素的特性(attribute)映射到类的属性(property).每个XML名称空间则对应于一个或更多的.NET名称空间.

XML中的名称空间和.NET中的很类似.二者都用来避免同名实体之间的名称冲突.我们的XAML文件中有两个名称空间,workflow是第一个名称空间,因为它没有前缀,所以它还是默认的名称空间.第二个名称空间是XAML,它使用x:前缀.

XAML名称空间比较特别,因为它会将指令传给工作流编译器.比如x:Class特性会告诉编译器工作流定义中待创建的新类名称.我们的XAML会被编译为包含一个类的程序集,这个类的名称就是MyWorkflow.

我们先删除WhileActivity并让XAML可以被编译.

<SequentialWorkflowActivity 

  
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" 

  xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"

  x:Class
="MyWorkflow"

  
>

  
<CodeActivity ExecuteCode="SayHello" />

  
<x:Code>

    
<![CDATA[  

    private void SayHello(object sender, EventArgs e)

    {

      Console.WriteLine("Hello, workflow!");

    }

    
]]>

  
</x:Code>

</SequentialWorkflowActivity>

现在这个工作流通过了验证,因为CodeActivity已经有了用来定义ExecuteCode事件的event handler.一般来说,CodeActivity并不这样使用,CodeActivity应该使用一些计算和逻辑来检查并改变工作流的状态,而我们现在只是向控制台输出一条消息.这个例子演示了如何在XAML中使用内嵌代码.内嵌代码通常也是不被采用的,许多开发人员喜欢使用精准的类并且认为内嵌代码是七宗罪之一.

如果我们真的需要从工作流内部向控制台输出消息,我们可以将这个行为封装到一个自定义活动中.封装了行为的自定义活动可以作为控件来重复使用.

2.3.1 在XAML中使用自定义活动

自定义活动继承自System.Workflow.ComponentModel.Activity,它允许我们在工作流中使用针对特定领域的控件(活动).为了实现自定义活动,我们需要覆盖(Override)一个虚方法-Execute.下面的代码是一个可以向控制台输出消息的自定义活动.

using System.Workflow.ComponentModel;
namespace OdeToCode.WinWF.Activities
{
   
public class WriteLineActivity : Activity
   {
      
protected override ActivityExecutionStatus Execute
               (ActivityExecutionContext executionContext)
      {
         Console.WriteLine(_message);
         
return ActivityExecutionStatus.Closed;
      }
      
private string _message;
      
public string Message
      {
         
get { return _message; }
         
set { _message = value; }
      }
   }
}

我们为自定义活动创建了一个公有属性-Message.我们可以通过XAML里的Message特性来设置这个属性的值,而无需使用内嵌代码.

<SequentialWorkflowActivity 
  
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" 
  xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:otc
="http://schemas.OdeToCode.com/WinWF/Activities"
  x:Class
="MyWorkflow">
  
<otc:WriteLineActivity Message="Hello, workflow!"/>
</SequentialWorkflowActivity>

请注意,我们定义了一个新的XML名称空间(http://schemas.OdeToCode.com/WinWF/Activities)来表示自定义活动.工作流编译器将如何使用这个名称空间来查找WriteLineActivity控件呢?答案是通过程序集级别的元数据(metadata),因为这个元数据负责提供XML名称空间和.NET名称空间之间的映射.

[assembly: XmlnsDefinition("http://schemas.OdeToCode.com/WinWF/Activities""OdeToCode.WinWF.Activities")]

XAML编译器会从被引用的程序集中查找XmlnsDefinition特性.当它找到了上面的定义,编译器就会知道应该将http://schemas.OdeToCode.com/WinWF/Activities映射到该程序集的CLR名称空间OdeToCode.WinWF.Activities. 

另一个映射名称空间的方式是在XAML中直接嵌入CLR名称空间和程序集名称.假设我们的自定义活动所在程序集中叫做Foo.dll,则在XAML中这样写:
xmlns:otc="clr-namespace:OdeToCode.WinWF.Activities;assembly=Foo"
当我们需要在工作流内部使用一个非原创的类型(我们无法向其中添加元数据)时,这种方式很有用.
我们的工作流几乎已经准备好了,但是在首次执行之前还需要将XAML转换为CLR指令.
所以,下一篇,工作流编译!