《WF编程》系列之8 - 工作流编写方式:纯XAML
《WF编程》系列之8 - 工作流编写方式:纯XAML
2.3 纯XAML
下面是一段纯XAML的工作流定义:
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可以被编译.
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.下面的代码是一个可以向控制台输出消息的自定义活动.
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特性来设置这个属性的值,而无需使用内嵌代码.
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名称空间之间的映射.
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指令.
所以,下一篇,工作流编译!