一起学Windows Workflow Foundation

[转帖]和我一起学Windows Workflow Foundation

(1)-----创建和调试一个WF实例

今天开始,我打算开始学习WWF,从网上搜索到了部分相关资料,也找到了一些文档和实验。但是,资料以英文的占多数,所以,在学习起来似乎比较吃力,不过相信我能坚持下来,顺便提高点英语阅读能力,不过本人英文水平实在是差,解释的不到位或错误的地方请大家谅解(千万别笑话我,哈哈)。毕竟我也是从空白开始学习WWF,让我们一起进步。

首先,我们需要安装WinFX下载)和Visual Studio 2005 extensions for .NET Framework 3.0 (Windows Workflow Foundation)。这是我们必备的开发组件。

这些例子和教程是从微软的labs上下载到的,分为10个部分。先来学最基础的第一部分先 :)

第一部分的目的

这个试验的目的是介绍Windows Workflow Foundation的工作流概念的关键点

完成了这个试验以后,我们可以学习到:

· 使用Visual Studio 2005 为WWF设计顺序工作流

· 配置和使用Visual Studio 2005调试器调试你的工作流

· 让工作流接受参数

· 通过接收事件触发一个新的工作流实例

· 定义条件

· 为If/Else, Delay, Listen, and Code配置基本的活动(activities不知道怎么翻译好)

· 给工作流为接收到的事件定制活动

工作流是在一个活动图中进行定义的,它可能是一个人为操作或一个系统进程。一个活动在工作流中是一个步骤,其体现为一个可执行单元,并且是可重用的,可整合的。工作流在设计好后被编译成.net程序集,它被工作流运行时和通用语言运行时(CLR)所执行。

 

一.创建一个Hello World工作流

在这次练习里,我们将通过VS2005工作流设计器设计一个非常简单的“Hello World”工作流。这个Hello World工作流将通过很简单的代码实现一个顺序工作流,它将在我们的命令行中输出“Hello, World!”。我们将了解工作流的定义和其代码。最后,我们将学习怎样通过VS2005在我们的机器上执行、调试我们的工作流。

打开VS 2005,新建项目,选择“顺序工作流控制台应用程序”,修改项目名称为:HelloWorldWorkflow.

 

1. 点击确定我们将生成一个新的工作流项目。

2. 这个项目会为我们自动生成一个工作流模版Workflow1。在这里我们是不需要这个模版的,我们只是做一个非常简单的例子,所以我们删除这个Workflow1.cs文件。

3. 右键单击项目选择添加新项

4. 选择“顺序工作流(具有单独的代码)”,点确定添加。这里将添加两个文件Workflow1.xoml和Workflow1.xoml.cs,Workflow1.xoml包含了描述工作流类型的XML语言,而其CS后缀的的文件则包含这个工作流内的动作执行代码。

5. 下面我们要做的就是添加代码了。双击Workflow1.xoml文件来打开VS 工作流设计器视图。

6. 我们从工具箱里可以砍刀我们可以添加的各种活动。

7. 我们从工具箱中拖拽“Code”这个活动到我们的工作流当中。

8. 注意,这个时候在我们拖拽进来的“Code”这个活动的右上角有一个红色的叹号,点击下拉的小三角我们可以看到显示这个叹号的呀,是因为属性ExecuteCode还没有被设置。

9. 那么下一步很明白了,我们需要设置ExecuteCode,点击这个提示,我们会发下在属性窗口的ExecuteCode会显示高亮,在其内输入codeActivity1_CodeHandler并双击,会在后台代码Workflow1.xoml.cs中生成codeActivity1_CodeHandler方法

10. 可以看到,这个类继承自 SequentialWorkflowActivity 基类. 我们要在方法中输入:

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

全部代码如下:

 

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 HelloWorldWorkflow
{
public
partial
class Workflow1 : SequentialWorkflowActivity
{
private
void codeActivity1_CodeHandler(object sender, EventArgs e)
{
Console.WriteLine("Hello, World!");
}
}
}

11. 按Ctrl+F5运行程序,如果出现:
则说明成功。

12. 是不是太简单了?至少,我们第一次的工作流编写完成了。

13. 等等,别急,还有个好玩的,那就是WWF的调试功能。我们在以前调试的时候,是要在代码中设置断点的,现在,你可以试一下调试WWF。

14. 打开工作流的设计视图Workflow1.xoml。

15. 选中我们刚才拖拽过来的codeActivity1,点右键,选断点—插入断点(或者选中后直接点F9),看到了什么?

6. 现在我们再按F5,在执行到这里的时候就会停止,可以进行调试了。

7. 点F11单步执行将进入我们的codeActivity1_CodeHandler事件,是不是很方便?

 

今天先说这么多,明天继续学,呵呵

 

 

(2)-----让WF通过参数接收数据

 

在这一节,我们一起学习通过其他程序调用工作流,并且在工作流启动时给工作流传递一些数据,使其在工作流中使用。

在workflow开始执行时,我们有两种方式给工作流传递数据------参数和事件。今天,我们先说说怎样通过参数来给工作流传递数据。

需要传递给workflow的参数,我们需要先在工作流中通过属性来进行设置,而后在其他程序调用这个工作流创建一个工作流的实例时,通过 Dictionary<string, object>键值集合进行传递,如下:

继续使用我们上一节用到的代码,给Workflow1 类添加两个属性FirstName和LastName

 

         private   string  firstName ;

        
public   string  FirstName
        {
            
get
            {
                
return  firstName;
            }
            
set
            {
                firstName 
=  value;
            }
        }

        
private   string  lastName;

        
public   string  LastName
        {
            
get
            {
                
return  lastName;
            }
            
set
            {
                lastName 
=  value;
            }
        }

 

这里我们将通过其他应用程序传递fristName和lastName并让这两个值在工作流执行过程中以对话框的形式显示出来。

因为我们上一节中创建的是个控制台应用程序,所以让起弹出对话框需要应用System.Windows.Forms程序集。

然后修改我们的Workflow1类中的codeActivity1_CodeHandler

 

         private   void  codeActivity1_CodeHandler( object  sender, EventArgs e)
        {
            System.Windows.Forms.MessageBox.Show(
                    
" Hello world:  "   +  firstName  +   "   "   +  lastName);
        }


现在我们已经修改好我们要做的工作流代码了。

 

 

下面我们要创建一个winForm应用程序。

添加一个新的项目WinFormTestHost

接下来我们要确认我们新创建的项目WinFormTestHost能够调用工作流Workflow1。

给WinFormTestHost添加引用,引用项目HelloWorldWorkflow.

同时,我们还要让我们的这个winfrom应用程序引用wwf程序集。继续添加引用,选择.net选项卡,添加如下三项:

System.Workflow.Activities

System.Workflow.ComponentModel

System.Workflow.Runtime

 

修改我们的Form1窗体。

添加两个Label分别为Label1和Label2,Text属性分别为First name和 LastName。

添加两个TextBox分别为txtFirstName和txtLastName。

添加一个按钮btnStartWorkflow,用来启动我们的工作流,Name属性为btnStartWorkflow,Text属性为Start Workflow。

这个简单的窗体就做完了,我们来添加代码。

首先在我们的类Form1.cs中添加工作流运行时声明:

        private  WorkflowRuntime wr;

 

双击btnStartWorkflow添加事件处理程序。

 

         private   void  btnStartWorkflow_Click( object  sender, EventArgs e)
        {
            
if  (wr  ==   null )
            {
                wr 
=   new  WorkflowRuntime();
                wr.StartRuntime();
            }

            
// 定义键和值的集合(Dictionary)用来传递参数
            Dictionary < string object >  parameters  =   new  Dictionary < string object > ();
            parameters.Add(
" FirstName " , txtFirstName.Text);
            parameters.Add(
" LastName " , txtLastName.Text);

            
// 创建一个工作流实例
            WorkflowInstance instance  =  wr.CreateWorkflow( typeof (HelloWorldWorkflow.Workflow1), parameters);
            
// 启动工作流
            instance.Start();
            
        }

最后给FormClosed事件添加一段代码,使其在关闭窗体时关闭工作流

 

         private   void  Form1_FormClosed( object  sender, FormClosedEventArgs e)
        {
            
if  (wr  !=   null )
            {
                
if  (wr.IsStarted)
                {
                    wr.StopRuntime();
                }
            }
        }

 

现在,我们的程序就开发完成了,点击F5,将打开窗体:

我们输入名称后点Start Workflow,将弹出窗体:

这个窗体是在Workflow1中执行的,同时,我们也看到,在winfrom窗体中输入的值成功的传递到了工作流中。

 

 

(3)-----使用If/Else活动,定制活动处理工作流,使用事件传递数据

 

上一节我们学习了怎样通过参数传递数据,这节我们就说说通过事件传递数据的方法,由于这节除了我们自己写的一部分代码外,还需要用到另外需要触发事件的ExpenseApplication外部应用程序和所用事件示例代码,所以请下载示例程序源代码,根据需要添加项目的引用。

另外,我们将学习到怎样使用if/else 根据条件处理工作流 ,还将学习根据定制活动有条件的处理工作流。我们将在制作一个简单的处理费用支出报表的工作流中学习到这些概念。当工作流在报表数据提交的时候将接收到一个事件,这个事件的参数将把包含实际的费用支出报表数据提交到进程。如果报表的支出费用少于1000,工作流将自动提交,否则,将自动拒绝。

首先,创建一个名称为ExpenseWorkflows的空工作流项目。

添加一个含有单独代码的顺序工作流。默认名称就叫Workflow1.xoml

我们将使用HandleExternalEventCallExternalMethod 这两个活动来和支出报表建立联系,在这之前,先给我们的解决方案添加一个现有项目ExpenseLocalServices(从下载的源代码中可以找到这个项目),然后给我们的项目ExpenseWorkflows添加一个引用,引用这个现有项目。

添加提交支出报表的活动

1. 在Workflow1这个类中我们需要添加两个成员变量供将来使用,一个变量名称为reportArgs,用于接收报表提交这个事件的参数对象。另外一个变量将保存报表的支出费用。

(Snippet: Lab01_Ex03_Task03_MemberVariables)

public partial class Workflow1 : SequentialWorkflowActivity

{

public ExpenseLocalServices.ExpenseReportSubmittedEventArgs reportArgs =

default(ExpenseLocalServices.ExpenseReportSubmittedEventArgs);

public int amount = default(System.Int32);

}

2. 打开workflow1.xoml的设计视图。

3. 从工具箱中找到HandleExternalEvent 这个活动拖拽到我们的工作流设计视图中,将其Name属性设置为expenseReportSubmitted1如图:

Ex3Task3Sequence

4. 点击红色的叹号选择Property ‘InterfaceType’ is not set.来设置InterfaceType属性。

Ex3Task3WarningSelection

5. 点击[…]按钮将出现 .NET Type Browser 对话框。从引用程序集目录树中选择 ExpenseLocalServices.IExpenseService 。如下图所示:

6.现在我们的工作流设计视图expenseReportSubmitted1上还有一个叹号,是因为我们的EventName还没有设置。同样点选它,设置EventName属性。点击下拉列表框,选择ExpenseReportSubmitted。

7.继续设置expenseReportSubmitted1的属性,找到e这个属性,点[…]按钮,选择Bind to an existing member选项卡下的Workflow1---reportArgs.也就是把这个提交事件的e参数和我们定义的成员变量reportArgs绑定。

8.双击设计视图中的expenseReportSubmitted1创建Invoked事件。

(Snippet: Lab01_Ex03_Task03_ReportSubmittedInvoked)

public void ReportSubmitted_Invoked(object sender, EventArgs e)

{

Console.WriteLine("ReportSubmitted_Invoked");

//给我们的成员变量amount设置事件触发得到的值

this.amount = this.reportArgs.Report.Amount;

}

添加自动审批活动到我们的工作流

1. 我们将添加一个定制活动(我们还没学到怎么定制活动,所以先直接引用做好的定制活动吧,在下载的源代码里的ExpenseActivities,可以看一下,其实很容易理解,就象我们做定制控件。:))来判断所做的提交是否进行自动审批。

2. 单击工具|选择工具箱项

3. 选择 .NET Framework组件 选项卡并单击浏览

4. 导航到我们代码源文件的ExpenseWorkflows\ExpenseActivities\bin\Debug\ExpenseActivities.dll.将其添加。

5. 现在我们的工具箱会多一个新的选项卡叫做ExpenseActivities,里面有两个新的活动:AutoApproveGetManager

6. 现在把我们把一个新的定制活动AutoApprove拖拽到我们的工作流设计视图。

7. 现在我们的工作流设计视图有两个活动,如图:

8. 在设计视图中选择活动autoApprove1 ,设置其Amount属性,绑定到Workflow1.amount

给工作流添加条件

1. 从工具箱中找到IfElse活动拖拽到设计视图中autoApprove1的下面。

2. 将其(Name)属性设置为EvaluateExpenseReport

3. 选择IfElse左边活动分支设置:(Name属性设置为IfAutoApproveReport;Condition属性设置为Declaritive Rule Condition

4. 展开Condition的“+”,设置其中的ConditionName属性为AutoApproveCondition

5. 对Expression属性编辑表达式:this.autoApprove1.Approved。如图:

Ex3AutoApprovedCondition

6. 选择EvaluateExpenseReport右边的活动分支将其(Name)属性改为ElseRejected.

给支出报表是否通过添加逻辑判断

1. 从工具箱中选择CallExternalMethod活动添加到标签为IfAutoApproveReport的条件分支里面,设置这个活动(Name)属性为approveExpenseReport1

2. 象上面我们设置HandleExternalEvent活动时一样,我们把InterfaceType属性按照相同的方法设置成ExpenseLocalService.IExpenseService

3. MethodName属性选择下拉列表框中的ApproveExpenseReport方法。

4. 同样的,我们还要设置properties属性,选择report,设置如下所示:

5. 现在,左边的IfAutoApproveReport条件分支我们配置完毕了。接下来配置右边的条件分支ElseRejected

6. 添加另一个CallExternalMethod活动到ElseRejected,设置Name属性为rejectExpenseReport1

7. 选择InterfaceType属性设置为:ExpenseLocalService.IExpenseService

8. 选择MethodName属性设置为:RejectExpenseReport

9. 选择Report属性绑定到:reportArgs.Report.

10. 现在我们的工作流设计视图如图所示:

调试工作流

到现在,我们的工作流就做完了。接下来我们要调试它,看他的执行过程。

不过,这里我们还需要借助两个项目,来触发我们所做的工作流事件。

1.进入ExpenseWorkflows\ExpenseHost\bin\Debug目录,复制ExpenseHost.exeExpenseHost.exe.config到我们的项目文件目录:ExpenseWorkflows\ExpenseWorkflows\bin\Debug。

还有ExpenseWorkflows\ExpenseApplication\这个项目(一会调试用)。

2. 现在需要配置我们的ExpenseWorkflows项目,将其设置为启动项目,然后右击项目选择属性,选择调试,在启动操作选择“启动外部程序”,浏览到ExpenseWorkflows\ExpenseWorkflows\bin\Debug\ExpenseHost.exe

3. 现在可以按F5启动调试了,当然,为了看整个项目的执行过程,我们可以给各个工作流和其他感兴趣的位置设置断点来详细学习工作流的执行过程。

Task 7 – Test the Expense Reporting Workflow

测试支出报表工作流

1. Compile and run the ExpenseWorkflows project under the Visual Studio debugger by pressing F5 or selecting the Debug | Start Debugging menu command.

2. 点F5启动调试,将显示如下界面,执行ExpenseHost命令行程序。

4. 运行 ExpenseApplication.exe (在如下目录:ExpenseWorkflows\ExpenseApplication\bin\Debug)。

我们提交一条报表数据,金额填写了1500,

5. 在VS2005给活动 expenseReportSubmitted1设置断点

6. 然后单步执行可以看到 ReportSubmitted_Invoked 事件处理的代码.

7. 继续单步执行到活动 rejectExpenseReport1

8. 看到我们的支出报表请求被拒绝了,这里会调用方法我们设置的RejectExpenseReport来更新刚才我们的那条report记录,将起状态设置为拒绝。

9. 我们可以看到应用程序状态

10. 点击Refresh Reports 

 变成了拒绝(Rejected)。

11. 如果输入小于1000的数,会看到

12. 状态为通过。

 

大家可以仔细阅读一下这些项目的代码,然后调试一遍,应该就能了解其中工作的过程和工作流中HandleExternalEventActivity和CallExternalMethodActivity的使用方法了。

 

 

(4)-----使用Listen,Delay,和其他envnt-based定制活动

 

这一节我们将扩充上一节制作的支出报表的例子(所以继续使用第三个试验使用的例子),给他添加一个功能使管理者可以去管理大于1000的金额是通过审核还是拒绝。我们将使用一些新的活动包括 Listen, Delay等,另外还有一些定制的活动。

代码点击这里下载

添加一个请求管理者通过的活动

1. 首先我们需要在类Workflow1声明ReportEmployeeId, ManagerEmployeeIdreviewArgs3个变量,所以给workflow1.xoml.cs添加如下代码:

public string ReportEmployeeId = default(System.String);

public string ManagerEmployeeId = default(System.String);

public ExpenseLocalServices.ExpenseReportReviewedEventArgs reviewArgs =

default(ExpenseLocalServices.ExpenseReportReviewedEventArgs);

2. 打开Workflow1.xoml的设计视图,选择EvaluateExpenseReport这个IfElse活动右边的活动分支(ElseRejected),修改其Name属性为ElseManagerApproval。删除里面我们在上一节中创建的活动rejectExpenseReport1

3. 添加一个自定义活动GetManager(在我们上一节添加的自定义活动ExpenseActivities.dll中)到IfElse活动分支的ElseManagerApproval节点内,如图:

4. 设置getManager1的属性:
ManagerEmployeeId –点击[…]绑定到ManagerEmployeeId

ReportEmployeeId –点击[…]绑定到ReportEmployeeId

5. 修改Workflow1类中的ReportSubmitted_Invoked的代码,添加高亮的那行,将参数提供者提供的EmployeeId赋值给刚才我们声明的变量ReportEmployeeId。

6. Change the code in the ReportSubmitted_Invoked method to set the ReportEmployeeId to the value provided in the Expense Report’s EmployeeId property. Add the highlighted line of code below.

private void expenseReportSubmitted1_Invoked(object sender, ExternalDataEventArgs e)

{

Console.WriteLine("ReportSubmitted_Invoked");

this.amount = this.reportArgs.Report.Amount;

this.ReportEmployeeId = this.reportArgs.Report.EmployeeId;

}

7. 在工作流设计视图中从工具箱拖拽一个活动CallExternalMethodgetManager1活动的下面。

8. 设置其(Name) 属性为 requestManagerApproval1

9. InterfaceType属性为ExpenseLocalServices.IExpenseService

10. MethodName 属性从下拉列表框中选择 RequestManagerApproval

11. 还要设置如下属性:

12. ManagerEmployeeId – 设置为 ManagerEmployeeId.

13. Report – 设置为 reportArgs.Report.

14. 现在EvaluateExpenseReport 节点如下图所示:

添加一个Listen活动来等待支出报表的审核

1. 在requestManagerApproval1下添加一个Listen 活动。

2. 设置其(Name)属性为ListenForManagerApproval,修改其eventDrivenActivity1eventDrivenActivity1的(Name)属性分别为:ManagerReviewedReviewTimeout

3. 如图所示:

4. 在ManagerReviewed部分添加一个HandleExternalEvent活动,将这个新添加的活动(Name)属性设置为expenseReportReviewed1

5. 继续给我们新创建的这个活动设置属性,将

a. InterfaceType 属性设置为ExpenseLocalServices.IExpenseService.

b. EventName 属性设置为ExpenseReportReviewed

c. 绑定属性ereviewArgs

6. 从工具箱中拖拽一个Delay活动到设计视图中Listen活动下的ReviewTimeout分支。

7. 设置这个新添加的活动delayActivity1的属性TimeoutDuration值为:00:00:30

8. 现在的工作流如图所示:

批准或拒绝支出报表

1. 在expenseReportReviewed1活动下添加一个IfElse活动,修改(Name)属性为EvaluateReview

2. 选择左边的分支,设置起属性:

a. (Name) – 设置为 IfApproved

b. Condition – 选择 CodeCondition,说明我们将通过代码控制条件。

c. 展开Condition属性,在下一级Condition中输入IfReportApproved_Condition,并双击,将在Workflow1类中自动添加此事件处理程序。

d. 我们输入入下代码,表示如果通过则将条件参数值设置为通过:

public void IfReportApproved_Condition(object sender, ConditionalEventArgs e)

{

e.Result = this.reviewArgs.Review.Approved;

}

3. 选择EvaluateReview活动右边的分支设置其(Name) 属性为ElseDeclined

4. 添加一个CallExternalMethod活动到EvaluateReview活动的IfApproved分支。

a. (Name) 属性设置为 approveExpenseReport2

b. InterfaceType属性设置为ExpenseLocalServices.IExpenseService

c. MethodName属性设置为ApproveExpenseReport

d. report 属性设置为reportArgs.Report

5. 同样的,添加一个CallExternalMethod活动到EvaluateReview活动的ElseDeclined分支。

a. (Name) 属性设置为 rejectExpenseReport1

b. InterfaceType属性设置为ExpenseLocalServices.IExpenseService

c. MethodName属性设置为RejectExpenseReport

d. report 属性设置为reportArgs.Report

6. 现在如图所示:

Ex4Task3Step21

7. 在rejectExpenseReport1活动上右键点击复制,粘贴到delayActivity1活动下面并修改这个新创建活动的(Name) 属性为 rejectExpenseReport2.

8. 如图:

9. Ex4Task3Step25

10. 现在我们的工作流制作完成了,他的全图如下所示

测试报表支出工作流

1. Next you will verify that the startup application is correct.

2. 察看ExpenseWorkflows项目的属性,点击调试选项卡,象上一节一样,确认在启动操作中选择启动外部程序,并导航到ExpenseWorkflows\ExpenseWorkflows\bin\Debug\ExpenseHost.exe。

3. 下面点击F5启动调试。

4. 运行ExpenseWorkflows\ExpenseApplication\bin\Debug目录下的ExpenseApplication.exe

5. 运行ExpenseWorkflows\ManagerApplication\bin\Debug 目录下的ManagerApplication.exe

6. 如图

7. 现在,我们再在Expense Reporting 中提交一条数据:

8. 在Manager Expense Report Review中通过点击刷新获得此条数据:

9. 并且,通过点击通过还是拒绝来审核此条纪录是否通过。如果超时30秒,那么就会自动拒绝。

10. 最终提交的数据如下:

11.

12.

这里我们可以学到Listen和Delay的用法,更充实了第三个试验的内容。

 

 

(5)-----使用activity设计器创建一个整合的定制activity

Lab01已经完了,lab02中说的是创建自定义的Activity

Activities是工作流模型的一个可执行单元,它可以被重用,也可以把多个Activities整合成一个Activities。一个工作流可以由多个Activities组成,一个Activities也可以由其他的Activities构成,最终,每个Activities都作为一个可执行单元的形式表现出来。这次,我们来学习创建一个简单的Activity。

Activities是一个其实类,我们可以为其编写属性,方法和事件----这些属性方法和事件引用自workflow。我们还可以创建Activity并部署到.net程序集中来重用。

先看第一个:

使用activity设计器创建一个合成的定制activity

在这个练习里,我们将创建一个有两个分开的代码段组成的activity,然后我们将在一个工作流项目中使用这个activity,运行这个工作流察看运行结果。

创建一个新的WF项目

1. 创建一个名称为CompositeActivitySample顺序工作流控制台应用程序。

2.

New Picture (8)

3. 打开Program.cs查看其代码。

4. Program.csMain()方法包含了启动,加载和执行和等待工作流完成的示例代码。

5. 将项目中的Workflow1.cs重命名为HelloWorldWorkflow.cs并在弹出的提示对话框中选择时,修改所有代码引用。

创建一个Activity项目

现在我们已经创建了一个工作流项目,我们接下来要创建一个Activity项目来添加到解决方案。

在这个项目中我们去定制一个新的activity,这个新的activity中我们去添加两个基本的activity --- code的来整合成我们定制的新的activity 。

1. 新建一个项目,选择“工作流Activity库”,名称为HelloWorldActivityLibrary

2. 重命名Activity1.cs为HelloWorldActivity.cs。同样在弹出的是否对起所有引用的项目执行重名名提示时选择是。

3. 打开HelloWorldActivity的设计视图。

4. 从工具箱中拖拽一个Code activity到我们的设计视图中。

5. 如图:

CropperCapture[2]

CropperCapture[3]

6. 修改起(Name)属性为writeHello

CropperCapture[4]

7. 双击设计视图中的writeHello activity创建一个writeHello_ExecuteCode方法。

8. 在writeHello_ExecuteCode 方法中输入如下代码:

Console.Write("Hello, ");

9. 现在我们回到设计视图,再拖拽第二个Code activity到我们的设计视图中writeHello的下面,并修改(Name)属性为writeWorld

10. 双击设计视图中的writeWorld activity创建一个writeWorld _ExecuteCode方法。

11. 在 writeWorld _ExecuteCode 方法中输入如下代码:

Console.WriteLine("World");

 

生成工作流解决方案

现在我们将刚刚创建的这个activity添加到工作流中,并运行察看效果。

1. 我们点“生成---生成解决方案”或者直接点F6来生成解决方案。

2. 选择CompositeActivitySample项目中的HelloWorldWorkflow.cs来打开设计视图。

3. 这时,我们可以看到工具箱中会出现新的组件栏:

4.

5. 拖拽HelloWorldActivity到我们HelloWorldWorkflow的工作流设计视图中,并且把这个activity的(Name)属性修改为helloWorld

New Picture (10)

6. 现在我们把CompositeActivitySample作为启动项目按ctrl+f5查看运行结果吧。

7. .

8. 这里我们创建的这个activity按照顺序工作流的形式进行了执行。打印出“Hello,World”

9. 现在我们应该已经知道怎样创建一个简单的合成的activity了吧 :)

 

 

(6)-----制作一个基本的定制活动  

上一节,我们的自定义活动其实是“拼”出来的,这次,我们来做一个基本的定制活动(不再是将已有的活动进行拼凑),我们将通过自定义属性来设置一封email的信息。我们将给创建给自定义活动创建一个执行组件,并把这个自定义活动添加到工作流中。这个执行组件将使用.NET API来把包含信息的 email发送出去。

创建一个工作流项目

1. 创建一个顺序工作流控制台应用程序,名称为CustomPropertySample

2. 重命名Workflow1.cs的名称为SendMailWorkflow.cs

创建一个工作流Activity项目

1. 添加新项目 工作流Activity库,名称为SendMailActivityLibrary记得勾上添加到解决方案的勾哦)。

2. 修改Activity1.cs为SendMailActivity.cs

3. 单击活动设计视图中的标题(或者双击SendMailActivity.cs),可以看到如下界面:

CropperCapture[5]

4. 在属性窗口,选择Base Class 属性,点击“…”打开浏览对话框来为这个活动选择一个基类。

CropperCapture[48]

5. 选择System.Workflow.ComponentModel命名空间下的Activity后点击OK。这样我们自定义的工作流活动是继承自基本的活动,而不是像以前一样继承自类似System.Workflow.Activities.SequenceActivity来拼凑工作流了。

6. 右键点击SendMailActivity.cs 选择查看代码。

7. 在如图所示处点击右键,选择插入代码段。

Snap1.jpg

8. 选择Workflow。

Snap2.jpg

9. 选择 DependencyProperty - Property.

New Picture (3)

10. 此时将插入一些模板代码在我们选择的位置。

下面是一些我们的属性(property)代码段中可以被添加的属性(attribute)描述:

 

 

Name

 

 

Type

 

 

Description

 

 

Browseable

 

 

Boolean

 

 

Indicates whether this property appears in the Properties window.

 

 

指出这个属性在我们自定义活动右侧的属性栏中是否可见

 

 

Category

 

 

String

 

 

A user-defined category for the property.

 

 

用户为这个属性自定义的分类

 

 

Description

 

 

String

 

 

A description for the property.

 

 

此属性的描述

 

 

DesignerSerializationVisibility

 

 

Visible, Hidden, or Content

 

 

Determines how and if properties will be serialized.

 

 

Visible (the default) – the property will be serialized normally.

 

 

Hidden – prevents property serialization

 

 

Content – used for collection properties. The collection object itself is not serialized, however the contents of the collection are.

 

 

If a developer chooses a collection type, this property will be set to Content. If a developer chooses a non-serializable type, this property will be set to Hidden.

 

 

ValidationVisibility

 

 

Optional, Required, or Hidden

 

 

Specifies how the property’s value is validated.

 

 

Optional (the default) – the property can accept null values.

 

 

Required – The property must be set to a non-null value and is checked to ensure that this is the case.

 

 

Hidden – There is no automatic validation of the property’s value.

 

 

If ValidationVisibility is set to ‘Required,’ when the component is reused, it will require the property to be configured via smart tags, obviating the need for a check in the activity’s Validator class.

 

 

 

 

如图:

CropperCapture[49]

12. 类似的,添加如下属性:

Name

 

 

Type

 

 

Description

 

 

Category

 

 

From

 

 

string

 

 

From Email Address

 

 

EmailActivity

 

 

Subject

 

 

string

 

 

Subject of Email

 

 

EmailActivity

 

 

Body

 

 

string

 

 

Body of Email

 

 

EmailActivity

 

 

 

到这里我们的这个活动应该有4个属性了。.

构建工作流解决方案

1. 生成解决方案.
2. 然后会发现SendMailActivity会被添加到工具栏(在解决方案资源管理器中双击SendMailWorkflow.cs,就可以看到工具栏出现了SendMailActivity)。

3. 从工具栏中将 SendMailActivity 活动拖拽到 Visual Studio 工作流设计器中,并修改 (Name) 属性 为 sendMail.

4. 注意,现在我们可以看到我们在自定义活动那个项目中创建的属性出现在了属性窗口:

5. 现在可以为这个活动的自定义属性设置初始值值。

添加一个可执行组件

1. 右键点击 SendMailActivity.cs 选择查看代码

2. 在代码文件底部重写活动的 Execute方法,添加如下代码:

 

protected  override  ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) 



Console.WriteLine(To); 

return ActivityExecutionStatus.Closed; 

}
 

 

构建项目

1. 选择重新生成解决方案。

2. 在解决方案资源管理器中双击 SendMailWorkflow.cs 查看工作流设计视图.

3. 选择调试-开始执行(不调试)来运行项目(或点ctrl+F5),可以看到命令行的输出信息。

4. 命令行中将显示我们的邮件将寄出的地址:

更新这个活动,使他能够发送邮件

现在我们已经吧上面自定义的活动制作成功了,能供通过输出我们自定义的属性到命令行中。现在,我们修改这个代码,使他真的可以发送邮件。

1. 首先,我们本机的添加删除windows组件中确认已经安装了SMTP服务

2. 回到 SendMailActivity.cs 文件的代码视图。

3. 添加如下引用(用以发送邮件):

using System.Net;

using System.Net.Mail;

4. 用如下代码替代我们Execute方法中的测试代码---用来发送邮件:

 

protected  override  ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) 



MailAddress toAddress 
= new MailAddress(To); 

MailAddress fromAddress 
= new MailAddress(From); 

MailAddressCollection addresses 
= new MailAddressCollection(); 

addresses.Add(toAddress); 

MailMessage msg 
= new MailMessage(fromAddress, toAddress); 

msg.Subject 
= Subject; 

msg.Body 
= Body; 

SmtpClient mail 
= new SmtpClient("smtp.163.com"); 

mail.Credentials 
= new NetworkCredential("shinji329""123456"); 

mail.Send(msg); 

return ActivityExecutionStatus.Closed; 

}
 

如果出现异常,可以看一下是不是杀毒软件监控防止蠕虫病毒的设置拒绝了我们的发送。

在运行这个工作流项目偶,我们可以收到邮件:

现在,我们的自定义基本活动也做出来了,嘿嘿,比较简单的说。

 

(7)-----给定制活动添加有效性验证

 

给定制活动属性添加有效性验证

这个实验也没什么可详细说的了,无非就是给我们之前发送E-Mail的活动的属性里加个验证,这里我们验证了E-mail的To和From属性是否符合邮件的格式。如果不是正确的邮件格式,我们则在编译的时候不允许进行编译。

下面大概过一下:

1. 打开我们上次的那个定制活动项目,在 SendMailActivityLibrary这个项目上添加一个新的代码文件,名称叫做ParametersValidator.cs

2. 在代码文件中输入如下代码,代码继承自ActivityValidator,如果是合成的活动(就是上上次我们拼出来的那个自定义活动),那么代码应该继承自CompositeActivityValidatorActivityValidator在设计和运行时状态会进行验证,这些验证依赖于活动,在我们进行编译和运行时活动时,会自动运行这些代码查看我们的属性是否符合规则。

using System;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Text.RegularExpressions;
using System.Net.Mail;
namespace SendMailActivityLibrary
{
public
class ParametersValidator : ActivityValidator
{
}
}

3. 重写ValidateProperties方法来完成我们的验证。活动的ValidateProperties方法是在进行编译时执行的验证方法。这些验证语句将验证我们输入的这些属性是否是正确的E-mail格式

public
override ValidationErrorCollection ValidateProperties(ValidationManager manager, object obj)
{
ValidationErrorCollection validationErrors = new ValidationErrorCollection(base.ValidateProperties (manager, obj));
SendMailActivity sendMailActivityToBeValidated = obj as SendMailActivity;
if (sendMailActivityToBeValidated == null)
{
throw
new InvalidOperationException("Parameter obj is not of type SendMailActivity");
}
if (!IsValidEmailAddress(sendMailActivityToBeValidated.To))
{
ValidationError CustomActivityValidationError =
new ValidationError(String.Format("\'{0}\' is an Invalid destination e-mail address", sendMailActivityToBeValidated.To), 1);
validationErrors.Add(CustomActivityValidationError);
}
if (!IsValidEmailAddress(sendMailActivityToBeValidated.From))
{
ValidationError CustomActivityValidationError =
new ValidationError(String.Format("\'{0}\' is an Invalid source e-mail address", sendMailActivityToBeValidated.From), 1);
validationErrors.Add(CustomActivityValidationError);
}
return validationErrors;
}
public Boolean IsValidEmailAddress(String address)
{
// must only proceed with validation if we have data    
// to validate
if (address == null || address.Length == 0)
return
true;
Regex rx = new Regex(@"[^A-Za-z0-9@\-_.]", RegexOptions.Compiled);
MatchCollection matches = rx.Matches(address);
if (matches.Count > 0)
return
false;
// Must have an '@' character
int i = address.IndexOf('@');
// Must be at least three chars after the @
if (i <= 0 || i >= address.Length - 3)
return
false;
// Must only be one '@' character
if (address.IndexOf('@', i + 1) >= 0)
return
false;
// Find the last . in the address
int j = address.LastIndexOf('.');
// The dot can't be before or immediately after the @ char
if (j >= 0 && j <= i + 1)
return
false;
return
true;
}

4. 修改 SendMailActivity.cs 的代码

5. 添加 ActivityValidator 属性到 SendMailActivity, 如下面所示:

namespace SendMailActivityLibrary

{

[ActivityValidator(typeof(ParametersValidator))]

public partial class SendMailActivity: System.Workflow.ComponentModel.Activity

{

6. 重新进行编译.

7. 下面我们进行测试,修改SendMailWorkflow.cs 中的SendMailActivity活动,修改他的From属性,让他不是一个正确的E-mail格式,如:shinji3292163.com。

8. 重新进行编译。

9. 可以看到如下界面:

10. 修改成正常的,就可以编译成功了。

 
 
——cedar.zhao

转载于:https://www.cnblogs.com/Silverlight_Team/archive/2008/12/28/1364149.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值