Windows Workflow Foundation 中的 XAML 简介






4.82/5 (14投票s)
2007年4月13日
9分钟阅读

147221

823
Windows Workflow Foundation 教程中的第三篇文章。
引言
这是关于“Windows Workflow Foundation”介绍性系列文章的第三篇。在第一篇文章中,我们快速浏览了 Windows Workflow Foundation,并开发了一个基于“顺序工作流”的初步航班预订系统。随后,在第二篇文章中,我们探索了“状态机工作流”,并开发了一个采购订单系统。现在,我们将重点关注XAML,并学习如何在 Windows Workflow Foundation 中使用它来设计工作流。
你好,XAML!
可扩展应用程序标记语言,简称 XAML(发音为 zammel),是 Windows Framework 和 Windows Presentation Foundation 中使用的一种新的声明式语言。XAML 随 .NET Framework 3.0 推出,标志着声明式(与命令式编码相对)编程模型的到来。在 Workflow Foundation 中,可以通过代码和 XAML 来创建工作流。甚至可以实现完全基于 XAML 的解决方案,无需任何代码!这不仅为应用程序提供了无需编译的灵活性,还为非技术人员(如业务分析师)设计工作流开启了可能。 (尽管 Workflow Foundation 主要面向程序员,但全球范围内正在进行相关实验,以使其对业务分析师来说也能够可行地在此建模流程)。
XAML 与 Workflow Foundation
使用 Workflow Foundation,可以通过三种方式设计工作流:纯代码、代码/XAML 混合以及纯 XAML。部分代码/XAML 工作流是代码和 XAML 的组合。例如,典型场景是:对应用程序进行总体概览,并使用 XAML 定义工作流,然后由开发人员用代码为每个工作流活动添加技术细节。这些解决方案需要编译。纯 XAML 解决方案中的工作流以及所有条件/规则都在 XAML 中指定,并且可以在不编译的情况下托管,尽管需要“XAML 激活”才能使其正常工作。
本文将涵盖的内容
我们将使用 XAML 开发世界上最简单的顺序工作流,以“声明式”(即在 XAML 中)方式添加一些规则,然后插入一些控制台输出代码。我们将学习声明式编程模型,玩转 XAML,并了解 XAML 和代码如何集成!
让我们开始吧
向所有坚持到现在的“Alpha Blue Geeks”致敬,感谢你们的耐心,并邀请你们投入到一些真正令人兴奋的实践工作中。请在开始菜单中点击“Microsoft Visual Studio 2005”,选择“新建项目”,然后选择“Sequential Workflow Console Application”。
默认情况下,Workflow 项目会包含一个基于代码的工作流文件,现在由于我们更想玩转 XAML,我们将删除该文件。在“解决方案资源管理器”中右键单击项目,选择“添加新项”,然后选择“Sequential Workflow with Code Separation”文件。“代码分离”表明我们正在进行一些“令人兴奋的”(即 XAML)与基础代码不同的操作。
完成此操作后,点击“添加”,您将看到标准的空工作流设计器窗口。在这里稍作停留,让我们来探索一下我们这个“宠物项目”包含哪些文件。在“解决方案资源管理器”窗口中,有一个 MyWorkflow.xoml 文件,这个文件包含了我们的工作流 XAML。由于工作流目前是空的,如果您打开该文件,它只会包含一个头部节点。[您可以通过右键单击文件并选择“在浏览器中查看”来查看 XAML,或者,进入项目文件夹,选择 MyWorkflow.xoml 文件并在您喜欢的 XML 编辑器中打开]。MyWorkflow.xoml 目前应该看起来像这样。
<SequentialWorkflowActivity x:Class="FirstXAMLWFApplication.MyWorkflow 1"
Name="MyWorkflow 1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</SequentialWorkflowActivity>
让我们用放大镜仔细看看。根元素是“Sequential Workflow Activity”;正如我们正在开发一个顺序工作流应用程序一样,这应该是合理的。
另一个选项是 StateMachineWorkflowActivity。该元素有两个重要属性;Class 和 Name,其中“Class”指定了我们工作流类的完全限定名称,“Name”就是我们工作流的“name”属性值。
为了满足我们“做些令人兴奋的事情”的渴望,让我们在工作流中添加一个 While
活动。While
活动是 Microsoft 提供的“Base Activity Library”的一部分,该库包含许多活动,这些活动可用于各种工作流,无论它们属于哪个业务领域。库中的一些活动包括:Code
、IfElse
、Delay
、WebServiceInput
、Replicator
等。
While
活动是最简单且最常用的活动之一。几乎每个工作流都有需要迭代运行的任务;因此 While
活动,它会在某个特定条件满足之前,持续运行一个特定的活动。
我们总是可以通过在工作流设计器窗口中进行拖放来添加 While
活动,但让我们先玩玩酷炫且基础的 XAML。
打开 MyWorkFlow.xoml 并添加以下代码(粗体显示)。
<SequentialWorkflowActivity
x:Class="FirstXAMLWFApplication. MyWorkflow" Name="MyWorkflow "
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<WhileActivity x:Name="whileActivity1">
</WhileActivity>
</SequentialWorkflowActivity>
保存文件,然后切换回 VS.NET IDE 并打开工作流。它看起来会像这样
很酷,对吧!?但是你能看到那个红色的感叹号吗?它表示我们还没有为循环指定终止条件。让我们进入代码并在工作流类中添加一个受保护的变量。代码片段
namespace FirstXAMLWFApplication
{
public partial class MyWorkflow : SequentialWorkflowActivity
{
public Int32 counter;
}
}
回到设计器窗口,在 While
活动的“Condition”属性中选择“Declarative Rule Condition”,然后单击“Condition Name”的“Ellipse”。应该会显示以下对话框。
单击“Add”进入“Rule Condition Editor”并添加以下条件。
我们在这里添加了一个简单的条件,它将使 While
循环一直运行,直到 counter
达到 10,届时它将终止。现在可能是时候回到我们的 XAML 文件,看看这个声明式条件是如何添加到其中的了。
<SequentialWorkflowActivity x:Class="FirstXAMLWFApplication.MyWorkflow"
x:Name="MyWorkflow"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
<WhileActivity x:Name="whileActivity1">
<WhileActivity.Condition>
<RuleConditionReference ConditionName="Condition1" />
</WhileActivity.Condition>
</WhileActivity>
</SequentialWorkflowActivity>
“嗯,我看到 Condition 属性被设置为 Condition1
,这是我们最近添加的条件的名称,但实际的条件语句在哪里?” 好问题!项目中有一个单独的文件名为 MyWorkflow.rules,所有规则都保存在该文件中。为了简洁起见,我没有添加该文件的摘录(尽管您始终可以从文章顶部的附件代码中下载它以详细了解)。目前,就够了,我们的声明式规则语句存在于 .rules 文件中,而条件名称则包含在 XAML 中。这再次向我们展示了使用 XAML 设计工作流的强大功能,因为我可以将一百万个规则添加到我的项目中,并通过在 XAML 中添加一行来将其与特定活动关联!并且甚至可以通过简单地更改条件的名称来稍后编辑/替换它,就是这样!
这足以让 while 活动执行了,现在让我们在其中添加一个活动。您可以将其视为循环体。这里我们将添加一个 IfElse
活动,然后在 While
活动中的每个 IfElse
分支中添加一个 Code
活动。这次我们将使用 VS IDE 的强大功能,只需将活动拖放到工作流设计器中即可。添加这些新活动后,工作流的最终形状将如下所示
正如我们所见,IfElse
活动有两个分支。当 If
条件为真时执行一个分支,当 else
条件为真时执行另一个分支。我们将为每个分支分配适当的条件,但现在,让我们看看我们的 XAML 文件,看看工作流中的这个更改是如何翻译到其中的。
<SequentialWorkflowActivity x:Class="FirstXAMLWFApplication.MyWorkflow"
x:Name="MyWorkflow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
<WhileActivity x:Name="whileActivity1">
<WhileActivity.Condition>
<RuleConditionReference ConditionName="Condition1"/>
</WhileActivity.Condition>
<IfElseActivity x:Name="ifElseActivity1">
<IfElseBranchActivity x:Name="ifElseBranchActivity1">
<CodeActivity x:Name="codeActivity1"
ExecuteCode="codeActivity1_ExecuteCode" />
</IfElseBranchActivity>
<IfElseBranchActivity x:Name="ifElseBranchActivity2">
<CodeActivity x:Name="codeActivity2"
ExecuteCode="codeActivity2_ExecuteCode" />
</IfElseBranchActivity>
</IfElseActivity>
</WhileActivity>
</SequentialWorkflowActivity>
简单且结构化?你打赌!粗体语句显示了我们新的 IfElse
和 Code
活动。现在,我们在 IfElse
活动的每个分支中添加简单的条件。为了简单起见,我们将简单地检查计数器是偶数还是奇数,并根据此条件检查,将每个控制发送到每个分支。我们将添加这两个规则,并使用与 While
活动相同的机制(从属性窗口 -> 选择条件 -> 规则条件编辑器)将它们分配给每个分支。我们每个分支的条件将是
this.counter
% 2 == 0 // for even
this.counter
% 2 != 0 // for odd
最后,我们的“选择条件”看起来像这样
嘿!我说过我们**会**写一些代码吗?是的,我说了,现在是时候这样做了。选择“CodeActivity1”,在“Properties”窗口中单击“Generate Handlers”,然后深入其处理程序代码。在其中添加以下代码。我们只是在控制台显示消息,宣布这是一个偶数,并递增了我们的计数器变量。
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Hello! " + Convert.ToString(counter) +
" is an even number!");
counter++;
}
同样,为“CodeActivity2”添加以下代码。private void codeActivity2_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Hello! " + Convert.ToString(counter) +
" is an odd number!");
counter++;
}
耶!就这样。我们完成了工作流。让我们回顾一下我们所做的。
- 我们使用 XAML 添加了一个 while 活动。
- 我们以“声明式”方式为 while 活动添加了一个条件。该条件仅检查
counter
字段是否小于 10 - 然后我们将在
While
活动中拖放IfElse
和CodeActivity
。 - 在
IfElse
活动的每个分支中添加了适当的条件来检查它是偶数/奇数 - 在
Code
活动处理程序中添加了代码,用于在控制台显示和递增counter
字段。
Workflow runtime 需要一个宿主来运行工作流。我们的宿主将是一个简单的控制台应用程序。我们的项目包含 Progam.cs,在这里我们的工作流运行时将实际启动。将创建、执行工作流实例,然后关闭运行时。这没有什么复杂的;只是 VS IDE 为我们生成的相当简单的代码
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();
};
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
typeof(FirstXAMLWFApplication.MyWorkflow));
instance.Start();
waitHandle.WaitOne();
Console.ReadKey();
}
}
创建了一个新的运行时对象。为工作流的“Completed”和“Terminated”状态提供了两个事件处理程序。然后调用“Create Workflow”来获取我们工作流的一个实例,并调用“Start”来启动它。运行时引擎将异步执行我们的工作流,因此,我们需要在 AutoResetEvent
对象上阻塞我们的线程,并等待工作流完成(否则,在我们看到控制台窗口中的内容之前,它就会关闭,程序也会退出!)。现在,使用 AutoResetEvent
意味着线程将被阻塞,直到在完成事件处理程序中“设置”它。
就这样,伙计们。点击“Run”观看表演。好吧,我们的输出可能不会有多少美学上的吸引力,但它应该能让我们对 XAML 有一个很好的认识。以及为什么以及如何将其与 Workflow Foundation 一起使用。
我们的输出窗口看起来像这样
在 Workflow Foundation 中,我们还能用 XAML 做些什么?
很多!我们甚至可以设计完全基于 XAML 的工作流,而且我们甚至不需要编译它们。也就是说,一个人可以使用 XAML 设计一个工作流(除了 Visual Studio 之外的任何工具也可以使用,毕竟这一切都是关于创建/修改一个类似 XML 的文档),然后部署它来运行。“XAML 激活”被用于替代编译来启动工作流。这超出了本文的范围,我们选择了一种折衷的方法,即同时使用 XAML 和代码(声明式和命令式编程模型),因为工作流在很大程度上就是这样设计的和实现的。
在哪里可以找到更多信息
我最喜欢的书籍/链接与我之前文章中指定的相同。
- Programming Workflow Foundation – K. Scott Allen 使用 XAML 和 C# 的实践 WF 技术和示例
- 工作流基础的官方网站(Web 空间)
- K. Scott Allen 的博客
- DharmaShukla、BobSchmidt 著的 Essential Windows Workflow Framework。
- Google!– 搜索每天在技术世界中涌现的海量文章/博文。