昨夜飘风
昨 夜, 风, 飘 过; 枯 树, 叶, 飞 落。

源代码文件:
Calculator.zip
   结构化编程有顺序、分支和循环三种基本结构,这次我们来看看在
WF中如何构造分支结构。我们以构建一个计算器为例,采用传统WinFormWF相结合的设计方法,同时演示如何编译Workflow的类库。

 

建立calculator工作流

打开VS2008,建立一个新的空解决方案,命名为Calculator。然后在里面添加一个Sequential Workflow Library,命名为CalculatorWorkflow,如图所示:
 

我们的这个计算器要执行四则运算,每执行一次工作流会运算其中一个操作符。每个操作数都是整形的,结果以double型返回。

 

第一步,我们要定义工作流的公共属性,以实现外界参数的传入。下面是Workflow1.cs代码部分的内容:

 

using System;

using System.ComponentModel;

using System.ComponentModel.Design;

using System.Collections;

using System.Drawing;

using System.Workflow.ComponentModel.Compiler;

using System.Workflow.ComponentModel.Serialization;

using System.Workflow.ComponentModel;

using System.Workflow.ComponentModel.Design;

using System.Workflow.Runtime;

using System.Workflow.Activities;

using System.Workflow.Activities.Rules;

 

namespace CalculatorWorkflow

{

     public sealed partial class Workflow1: SequentialWorkflowActivity

     {

        private string _operation = string.Empty;

        private int _operand1;

        private int _operand2;

        private double _result;

 

        public string Operation

        {

            get { return _operation; }

            set { _operation = value; }

        }

 

        public int Operand1

        {

            get { return _operand1; }

            set { _operand1 = value; }

        }

 

        public int Operand2

        {

            get { return _operand2; }

            set { _operand2 = value; }

        }

 

        public double Result

        {

            get { return _result; }

            set { _result = value; }

        }

 

         public Workflow1()

         {

              InitializeComponent();

         }

     }

}

 

添加IfElse Activities

这个工作流需要做出一个简单的判断,就是算术运算的运算符。为了完成这个任务,我们可以采用IfElseActivity

 

打开Workflow1.cs的设计界面,从工具箱中拖放一个标注了IfElseactivity到工作流里,如图所示:

 

这个IfElseActivity实际上就是一个能够容纳其它Activity的容器,里面包含了一组(两个或者更多)IfElseBranchActivity的实例。每一个代表着一个判决树可能的结果。这些分支的顺序很重要,因为判决是从最左边的分支开始的。我们再添加两个分支。右击它,点击Add Branch,如是两次即可。

 

第一个分支我们用它来代表加法运算,它将检查Operation属性是否为“+”。选中最左侧的这个分支,把它的名字改为IfElseBranchActivityIsAdd(非必须,但最好是选择含义清晰的名称)。下面我们有两种方法来定义一个条件,代码条件(Code Condition)和声明规则条件(Declarative Rule Condition)。前者与上篇文章中的CodeActivity很像,它通过代码来实现一个能够评定布尔类型结果的事件句柄,发挥作用;后者也需要评定布尔型结果,但是它与工作流的代码是分离的。采用声明规则条件的好处就是可以在运行时对它进行修改,而无需重新编译。这个例子中,我们也会采用这种形式。这种规则的完整类名是System.Workflow.Activities.Rules.RuleConditionReference

 

在属性窗口中,打开Condition属性下拉列表,并选择Declarative Rule Condition,然后展开它,在ConditionName中填入IsAdd,然后点击Expression后的按钮,填入this.Operation==”+”,如下图所示:
 

此处输入代码的时候仍然支持IntelliSense。以此类推,将另外的三个分支分别设成如下的属性:

 

分支

ConditionName

Expression

IfElseBranchActivityIsMinus

IsMinus

this.Operation=="-"

IfElseBranchActivityIsMultiply

IsMultiply

this.Operation=="x"

IfElseBranchActivityIsDivide

IsDivide

this.Operation=="/"

 

 

添加运算逻辑(Calculation Logic

有了分支的架构,现在需要为每一个分支添加实际的业务操作。从工具栏拖动一个CodeActivity,放置到第一个分支容器内部,将其改名为codeActivityAdd,并且在ExecuteCode属性中填入AddOperation,添加如下的代码:

 

    private void AddOperation(object sender, EventArgs e)

    {

        _result = _operand1 + _operand2;

    }

 

如法炮制将其余三个运算逻辑也完成。注意按照规范来进行命名。把这四个运算模块写完之后,还可以增加一个抛出异常的模块,即如果传进来的运算符不是四则运算符号,就抛出一个异常来。再添加一个分支,命名为IfElseBranchActivityIsUnknown,条件留空,相当于多重if-elseif最后的那个else。再放进去一个CodeActivity,添加如下的代码:

 

    private void UnknownOperation(object sender, EventArgs e)

    {

        throw new ArgumentException(string.Format("Invalid operation : {0} ", Operation));

    }

 

全部搞定之后,这个流程图看起来应该如下所示:
 

创建计算器客户端

现在向这个解决方案中再添加一个Windows Form Application的项目,并为其添加CalculatorWorkflow的项目引用以及下面三个程序集的引用:

 

l  System.Workflow.Activities

l  System.Workflow.ComponentModel

l  System.Workflow.Runtime

 

最后整个解决方案看起来应该是下面这个样子:

 

 

OK,下面又回到了我们熟悉的WinForm编程,我就不多说什么了,搞出下面的界面来先。那些button的命名不重要,把文本框命名为txtNumber,方便后面的讨论。
 

我们没有必要为每一个button去写Click事件。事实上,使用四个事件句柄就可以了。即:

l  NumericButton_Click:赋给所有的数字按钮(0~9);

l  OperationButton_Click:赋给所有的运算符按钮(+-x/);

l  Equals_Click:赋给等号按钮(=);

l  Clear_Click:赋给清除按钮(C)。

 

现在可以向窗体的后台代码文件里添加一些成员变量以及Workflow的初始化代码。完整列表如下。

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using System.Workflow.Runtime;

using System.Threading;

 

namespace SimpleCalculator

{

    public partial class MainUI : Form

    {

        private WorkflowRuntime _workflowRuntime;

        private AutoResetEvent _waitHandle = new AutoResetEvent(false);

        private string _operation = string.Empty;

        private int _operand1;

        private int _operand2;

        private double _result;

 

        public MainUI()

        {

            InitializeComponent();

 

            // 启动工作流运行时。每个应用程序域只能进行一次

            InitializeWorkflowRuntime();

        }

 

        private void InitializeWorkflowRuntime()

        {

            _workflowRuntime = new WorkflowRuntime();

           

            _workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)

            {

                _result = Convert.ToDouble(e.OutputParameters["Result"]);

                _waitHandle.Set();

            };

 

            _workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)

            {

                MessageBox.Show(string.Format("工作流中断: {0}", e.Exception.Message), "工作流错误");

                _waitHandle.Set();

            };

        }

    }

}

 

初始化工作流的那部分代码在前面的Hello Workflow中被我略过,这个地方继续54它,现在只要会套用就可以了。我们在不同应用程序中要做的工作就是将WorkflowCompleted匿名委托里返回的OutputParameters里的有用信息提取出来即可。在前面的介绍中我们知道它是一个Dictionary<string, object>的类型。在每个应用程序域中只能存在一个WorkflowRuntime的实例,因此将它放在窗体的构造函数中是比较理想的。

 

紧接着,我们回到设计界面,将上面提到的四个事件句柄添加给每个按钮,并填充它的实际功能代码。

 

    /// <summary>

    /// 处理数字按钮的事件

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void NumericButton_Click(object sender, EventArgs e)

    {

        txtNumber.AppendText(((Button)sender).Text.Trim());

    }

 

    /// <summary>

    /// 处理运算符按钮的事件

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void OperationButton_Click(object sender, EventArgs e)

    {

        try

        {

            _operand1 = int.Parse(txtNumber.Text);

            _operation = ((Button)sender).Text.Trim();

            txtNumber.Clear();

        }

        catch (Exception exception)

        {

            MessageBox.Show(string.Format("运算符操作错误: {0}", exception.Message));

        }

    }

 

    /// <summary>

    /// 处理等号按钮的事件

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void Equals_Click(object sender, EventArgs e)

    {

        try

        {

            _operand2 = int.Parse(txtNumber.Text);

 

            // 创建一个带有输入参数的字典

            Dictionary<string, object> wfArguments = new Dictionary<string, object>();

            wfArguments.Add("Operand1", _operand1);

            wfArguments.Add("Operand2", _operand2);

            wfArguments.Add("Operation", _operation);

 

            // 申请一个工作流实例并启动它

            WorkflowInstance instance = kflowRuntime.CreateWorkflow(typeof(CalculatorWorkflow.Workflow1), wfArguments);

            instance.Start();

 

            // 等待处理完成

            _waitHandle.WaitOne();

 

            // 显示结果

            Clear();

            txtNumber.Text = _result.ToString();

        }

        catch (Exception ex)

        {

            MessageBox.Show(string.Format("运算错误: {0}", ex.Message));

        }

    }

 

    /// <summary>

    /// 重设当前的状态

    /// </summary>

    private void Clear()

    {

        txtNumber.Clear();

        _operand1 = 0;

        _operand2 = 0;

        _operation = string.Empty;

    }

 

    /// <summary>

    /// 清除按钮的事件

    /// </summary>

    /// <param name="sender"></param>

    /// <param name="e"></param>

    private void Clear_Click(object sender, EventArgs e)

    {

        Clear();

    }

 

Equals_Click()就是工作流创建和启动的地方,入口参数以字典对象的形式传递给工作流,这和我们在Hello Workflow中所看到的是一样的。运算的结果就会以Result属性的方式保存在WorkflowCompleted事件句柄中,返回到UI

posted on 2008-02-25 10:02  昨夜飘风  阅读(2556)  评论(3编辑  收藏  举报