随笔 - 6  文章 - 0  评论 - 10 
  2008年11月14日

       

     [上一节]

     尽管WF提供了很多的活动,但是还是有些情况需要开发人员自己开发工作流活动。例如:我们要执行一条SQL语句。

     接下来我们开发一个简单的活动。

     它有一下特征:

                         1.自定义工具箱图标

                         2.自定义工具箱中活动的名称

                         3.自定义设置器

                         4.自定义主题

                         5.自定义验证

                         6.自定义执行的代码

       

     首先,新建一个工作流活动库,如图:                

   

     接下来,新建一个活动,如图:    

          

   

     在新添加的活动中找到"Base"这个属性。设置这个属性,让其继承自System.Workflow.ComponentModel.Activity,如图:

          

     

     接下来我们来编写活动的代码,代码如下:   

Code
 1 public GreetActivity()
 2 {
 3     InitializeComponent();
 4 }
 5 
 6 public static DependencyProperty 
用户名Property = DependencyProperty.Register("用户名"typeof(string), typeof(GreetActivity));
 7 public static DependencyProperty 问候语Property = DependencyProperty.Register("问候语"typeof(string), typeof(GreetActivity));
 8 
 9 
10 public string 
用户名
11 {
12     get { return (string)base.GetValue(
用户名Property); }
13     set { base.SetValue(用户名Property, value); }
14 }
15 
16 public string 
问候语
17 {
18     get { return (string)base.GetValue(
问候语Property); }
19     set { base.SetValue(问候语Property, value); }
20 }
21 
22 protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
23 {
24     Console.WriteLine(
用户名 + ":" + 问候语);
25     return base.Execute(executionContext);
26 }

   

     这段代码声明了两个属性,最后重写了基类的Excute方法向控制台输出了这两个属性的信息。

     这个简单活动我们就做完了。

     执行代码结果如图:           

   

     接下来我们对这个活动进行下扩展,让它变的更美观。

     首先新增一个MyActivityToolboxItem类,代码如下:     

Code
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Drawing;
 6 using System.Workflow.ComponentModel.Design;
 7 using System.Runtime.Serialization;
 8 namespace MyActivityLibrary
 9 {
10     [Serializable]
11     public class MyActivityToolboxItem : ActivityToolboxItem
12     {
13         public MyActivityToolboxItem()
14         {
15         }
16 
17         public MyActivityToolboxItem(Type type)
18             : base(type)
19         {
20             base.DisplayName = type.Name.Substring(0, type.Name.Length - 8);
21         }
22 
23         protected MyActivityToolboxItem(SerializationInfo info, StreamingContext context)
24         {
25             Deserialize(info, context);
26         }
27     }
28 }

   

      这段代码改变了工具箱中的活动的默认名称,将活动的名称中的Activity去掉。

     在增加一个GreetActivityDesigner类,代码如下:     

Code
 1 [ActivityDesignerTheme(typeof(GreetDesignerTheme))]
 2 public class GreetActivityDesigner : ActivityDesigner
 3 {
 4     private const int TEXT_WIDTH = 75;
 5     private const int PADDING = 4;
 6 
 7     protected override Rectangle ImageRectangle
 8     {
 9         get
10         {
11             Rectangle rectangle = new Rectangle();
12             rectangle.X = this.Bounds.Left + PADDING;
13             rectangle.Y = this.Bounds.Top + PADDING;
14             rectangle.Size = Properties.Resources.
问候.Size;
15             return rectangle;
16         }
17     }
18 
19     protected override Rectangle TextRectangle
20     {
21         get
22         {
23             Rectangle imgRect = this.ImageRectangle;
24 
25             Rectangle rectangle = new Rectangle(
26                 imgRect.Right + PADDING,
27                 imgRect.Top,
28                 TEXT_WIDTH,
29                 imgRect.Height);
30             return rectangle;
31         }
32     }
33 
34     protected override void Initialize(System.Workflow.ComponentModel.Activity activity)
35     {
36         base.Initialize(activity);
37 
38         Bitmap bitmap = Properties.Resources.
问候;
39         this.Image = bitmap;
40         
41     }
42 
43     protected override Size OnLayoutSize(ActivityDesignerLayoutEventArgs e)
44     {
45         base.OnLayoutSize(e);
46 
47         Size imgSize = Properties.Resources.
问候.Size;
48         return new Size(imgSize.Width + TEXT_WIDTH + (PADDING * 3),
49             imgSize.Height + (PADDING * 2));
50     }
51 }

   

     这个类定义了活动中的图片和文字是如何显示的。

     新增GreetDesignerTheme类,代码如下:

       

Code
 1 public class GreetDesignerTheme : ActivityDesignerTheme
 2 {
 3     public GreetDesignerTheme(WorkflowTheme theme)
 4         : base(theme)
 5     {
 6     }
 7 
 8     public override void Initialize()
 9     {
10         this.ForeColor = Color.Black;
11         this.BorderColor = Color.Black;
12         this.BorderStyle = DashStyle.Solid;
13         this.BackgroundStyle = LinearGradientMode.ForwardDiagonal;
14         this.BackColorStart = Color.Black;
15         this.BackColorEnd = Color.Black;
16         this.ForeColor = Color.White;
17         base.Initialize();
18     }
19 }

   

     这个类改变了活动的颜色以及文字的颜色等。

     增加GreetActivityValidator.cs类,代码如下:     

Code
 1 public class GreetActivityValidator:Validator
 2 {
 3     public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
 4     {
 5         GreetActivity greetActivity = obj as GreetActivity;
 6         ValidationErrorCollection errorCollection = base.Validate(manager, obj);
 7         if (greetActivity.Parent!=null)
 8         {
 9             if (string.IsNullOrEmpty(greetActivity.
问候语))
10             {
11                 errorCollection.Add(ValidationError.GetNotSetValidationError("
问候语"));
12             }
13             if (string.IsNullOrEmpty(greetActivity.
用户名))
14             {
15                 errorCollection.Add(ValidationError.GetNotSetValidationError("
用户名"));
16             }
17         }
18         return errorCollection;
19     }
20 }

   

     这个类用来检查活动中的属性是否被设置,如果没有设置就会出现红色感叹号提示开发人员。

     最后,我们配置一下刚才开发的那个活动,添加如下代码:     

Code
1 [ActivityValidator(typeof(GreetActivityValidator))]
2 [ToolboxBitmap(typeof(GreetActivity), "Resources.
问候.ico")]
3 [ToolboxItem(typeof(MyActivityToolboxItem))]
4 [Designer(typeof(GreetActivityDesigner), typeof(IDesigner))]

   

     让我们来看下美化前和美化后的活动。

      

     

     怎么样是不是好看多了。

 

     [源代码]

 

posted @ 2008-11-14 21:06 o﹎箜絔┌↘ 阅读(1172) | 评论 (1)编辑
  2008年11月9日

     [上一节]

     本节我们做一个工作流通信的简单例子,具体的概念请参考上一节的内容。

     一.     通信服务:

     新建一个“顺序工作流控制台应用程序”,然后右键解决方案→添加→新建项目→类库。如图:

删掉默认的“Class”类,新增MessageEventArgs类,代码如下:

 

Code

 

 注意:一定要加上Serializable属性

新增“IMessageService”接口,代码如下: 

 

Code

 

注意:一定要加上ExternalDataExchangeAttribute属性

 

     接下来新增“MessageService”类,继承“IMessageService”接口,代码如下:

 

Code

 

     二.     工作流:

     接下来我们回到工作设计器,首先从工具箱中找到“CallExternalMethodActivity”活动,拖拽到设计器中。

     设置它的InterfaceType属性,Method属性和message属性,如图:

     

     然后在从工具箱中找到“ListenActivity”活动,拖拽到设计器中。

     向它的三个分支添加两个“HandleExternalEventActivity”活动和一个“DelayActivity”,最后添加一个“CodeActivity”活动,如图所示:

     然后我们分别设置这些活动的属性,如图:

   接下来我们来看下工作流的代码:

     

Code

 

     三.     工作流宿主

     最后编写工作流宿主的代码:

     

Code

 

 

     运行结果如图:

     

 

     [源代码]

 

     [下一节]

 

    

posted @ 2008-11-09 11:31 o﹎箜絔┌↘ 阅读(1547) | 评论 (0)编辑
  2008年11月6日

 

     [上一节]

     今天我们来介绍下工作流的通信。假设有这样一个场景,有一个应用程序,当用户输入想保存一个信息的时候,系统要向用户发送一条信息确认是否保存此信息,根据用户的选择执行保存或其他操作。

    我们可以利用WF内置的一些服务和活动实现此场景。

     首先介绍几个概念:

     1.    参数:传递参数最简单的方法就是在工作流实力创建期间传递。代码如下:     

Code
 
 
注意:工作流中必须有包含set访问修饰符的属性与添加到集合中的关键字相对应。     

因为集合是在工作流启动之前传递的,所以此方法不能用来和已经运行的工作流通信,只适合初始化工作流信息。

同样也可以将参数从工作流传递给它的宿主。代码如下:

 

Code

 

注意:工作流中必须有包含get访问修饰符的属性与添加到集合中的关键字相对应。   

     2.     本地通信服务

     预定义一个接口,当工作流想告诉宿主某些消息的时候,它会调用接口中的某个方法,当宿主有消息要发给工作流的时候,它会触发接口中的事件,工作流会处理这个事件。

     ExternalDataExchangeService:这个类用来管理所有的通信服务类。必须将这个类的实例添加到工作流的运行时中才能使用本地通信服务。代码如下:

Code

 

     ExternalDataExchangeAttribute: 将接口标记工作流本地服务的接口。代码如下:

Code

     

     ExternalDataEventArgs:当触发工作流活动事件的时候,随之发送的数据。代码如下:

 

 

Code


     注意:1.属性必须加上Serializable属性,

             2.构造函数中的的instanceID是必须的,用来唯一标识一个工作流实例。

 

      下面介绍几个WF内置的通信活动

     1.     CallExternalMethodActivity:可以用来在工作流运行时向外界发送工作流内部的属性。要想使用这个活动,只需要设置两个属性即可。

           首先,设置InterfaceType属性。如图:

     

 

     从这里可以选择程序中预定义的接口。

     然后设置MethodName属性,指定调用接口的哪个方法。如图:

     

     

     选择的方法不同,属性也随之发生改变,绑定好属性这个活动就可以使用了。

     2.     HandleExternalEventActivity: 用来处理工作流宿主触发的事件。和CallExternalMethodActivity相同,它也有InterfaceType和MethodName属性,按照上面的方法设置好属性就可以正常使用了。

 

     本节我们介绍了有关工作流通信的几个概念和WF内置的两个活动。下一节中,我们将做个通信的简单例子。

 

     [下一节]

 

posted @ 2008-11-06 22:53 o﹎箜絔┌↘ 阅读(1163) | 评论 (0)编辑
  2008年11月4日

                        

                         [上一节]

                         工作流需要一个宿主应用程序才能运行。宿主负责启动并维持工作流,工作流本身不需要了解运行时它所处的宿主是怎样的,然而这个宿主对工作流的生命周期来说却是十分重要的。

                         任何类型的.NET应用程序都可以作为工作流的宿主,一个工作流实例可以由Windows窗体应用程序的一个按钮开始,然后和一个Asp.net Web程序交互。在Windows窗体和Asp.net Web程序中使用相同的工作流实例,这些都被成为宿主。接下来我们了解宿主的几个概念:

    1.工作流运行时

     

    工作流运行时是宿主应用程序和工作流实例之间的通道,对工作流生命周期管理起到了重要作用。

     

    WorkflowRuntime类表示工作流运行时,为管理运行时环境提供了许多功能。有了这个类就可以完全控制工作流实例的执行和运行时本身。

     

    创建WorkflowRuntime实例,如下代码所示  

     

    Code

     

     

    WorkflowRuntime有两个公共方法:StartRuntime和StopRuntime.

     

    当调用StartRuntime的时候,会检查工作流运行时中是否包含(工作流事务服务和工作流调度服务)两个服务之一是否已经被手动添加到运行时中。如果没有,运行时会为每个服务类型创建默认实例。事务服务默认类是DefaultWorkflowCommitWorkBatchService,调度服务默认类是DefaultWorkflowSchedulerService    。当成功实例化这两个服务并将他们添加到运行时后,调用服务的Start方法启动服务。最后,设置工作流运行时的IsStarted属性为true并触发Started事件。

     

    StopRuntime方法将导致工作流运行时引擎卸载其每个工作流实例,停止所有服务,将 IsStarted设置为 false 并引发 Stopped事件。

     

    添加持久化服务,代码如下所示:  

     

    Code

     

    工作流运行时执行的最重要的任务之一就是创建工作流实例。调用WorkflowRuntime的CreateWorkflow方法就能创建一个工作流实例,代码如下所示:

     

    Code

 

               如果WorkflowRuntime类的StartRuntime方法没有被调用,它会在一个工作流实例启动的时候被调用。

    2.WorkflowInstance类

     

    WorkflowInstance类以实例的形式表示所有的工作流。可以将它看作一个工作流实例的外包装。

    得到一个新的工作流实例的方法是调用WorkflowRuntime的CreateWorkflow方法,这点在上文已经提到过,调用CreateWorkflow不会启动这个工作流,只有当WorkflowInstance的Start方法被调用以后,才会启动工作流。

    下面列出了WorkflowInstance类的方法:

   

名称

说明

 

Abort

中止工作流实例。

 

ApplyWorkflowChanges

将更改应用到 WorkflowChanges 对象所指定的工作流实例。

 

EnqueueItem

将消息同步发送到指定工作流队列。

 

EnqueueItemOnIdle

当工作流处于空闲状态时,将消息发送到指定的工作流队列。在确认工作流计划程序处于空闲状态(即没有执行任何有效操作)之后,EnqueueItemOnIdle将一直等待,直至工作流达到空闲点并编排队列。

 

Equals

返回一个值,该值指示指定对象是否等于 WorkflowInstance。 (重写 Object..::.Equals(Object)。)

 

Finalize

允许 Object 在"垃圾回收"回收 Object 之前尝试释放资源并执行其他清理操作。 (继承自 Object。)

 

GetHashCode

返回此工作流实例的哈希代码。 (重写 Object..::.GetHashCode()()()。)

 

GetType

获取当前实例的 Type。 (继承自 Object。)

 

GetWorkflowDefinition

检索此工作流实例的根活动。

 

GetWorkflowNextTimerExpiration

返回下一个时间点,已计划在此时间点向此 WorkflowInstance传递计时器消息。

 

GetWorkflowQueueData

获取 WorkflowQueueInfo对象的集合,该集合包含与此工作流实例关联的工作流队列的挂起项和订阅活动。

 

Load

加载先前卸载的工作流实例。

 

MemberwiseClone

创建当前 Object 的浅表副本。 (继承自 Object。)

 

ReloadTrackingProfiles

重新加载此工作流实例的跟踪配置文件。

 

Resume

继续执行先前挂起的工作流实例。

 

Start

开始执行工作流实例。

 

Suspend

挂起工作流实例。

 

Terminate

以同步方式终止工作流实例。

 

ToString

返回表示当前 Object 的 String。 (继承自 Object。)

 

TryUnload

当实例处于挂起或空闲状态时,将工作流实例从内存卸载到持久性存储区。

 

Unload

将工作流实例从内存卸载到持久性存储区。此调用将进行阻止,直至当前计划的工作完成或事务范围结束。

 

 

    3.WorkflowEnvironment 类
     

    WorkflowEnvironment能够访问正在当前线程上执行的工作流实例的事务上下文。这个类有两个属性: WorkflowInstanceId和WorkBatch。

    WorkflowInstanceId:获取与当前线程关联的工作流实例的GUID。

    WorkBatch:获取当前工作批次。(允许工作流具有事务的功能)

     

    [下一节]

     

posted @ 2008-11-04 18:27 o﹎箜絔┌↘ 阅读(1221) | 评论 (5)编辑
  2008年11月1日

     [上一节]

 

     上一节我们通过一个简单的Hello程序,了解了Windows Workflow Foundation的开发环境环境以及如何使用。这次我们扩展上次的例子,做一个复杂一点的练习

     在上节的例子中,调用的程序向工作流传入一个姓名,返回问候消息。这次我们让工作流判断用户的性别,返回不同的问候消息

     新建一个“顺序工作流控制台应用程序”。

     为了让工作流判断用户的性别,我们需要从工具箱找到IfElse控件,将它拖拽到工作流设计界面中,如下图:

和上次的Code Activity的例子一样,IfElse Activity也出现了红色惊叹号。因为我们没有设置IfElse第一个分支的Condition属性

 

首先我们需要为工作流添加如下代码:

 

Code

 

     接下来回到工作流设计视图中,选择IfElse Activity左边第一个条件分支。右击找到它的属性,选择Condition属性,选择“代码条件”。

     展开Condition属性,又会出现一个Condition属性,选择“判断性别”,如下图:

     然后从工具箱中找到Code Activity拖拽到两个分支中,如下图:

分别设置两个分支的ExecuteCode事件,代码如下:

 

Code

 

最后我们编写控制台应用程序,代码如下:

 

Code

 

执行代码后效果如下图:

 

[源代码下载] 

 

[下一节]

posted @ 2008-11-01 18:23 o﹎箜絔┌↘ 阅读(1130) | 评论 (0)编辑

     Windows Workflow Foundation 有时也被称为Windows WF,它是由微软开发的,为开发者开发基于工作流或基于流程的软件解决方案提供单一平台。Windows Workflow Foundation建筑在.NET之上,是.NET Framework3.0的主要组成部分。

     接下来动手开发我们第一个工作流。

     首先,启动Visual Studio 2008。第一步,创建一个“顺序工作流控制台应用程序”项目,如下图

 

 

     创建新项目后,开发环境如下图

     

     解决方案中的Workflow.cs文件是工作流本身,而Program.cs文件是启动工作流程序的代码

     1.工作流

     在解决方案中右击Workflow1.cs,然后选择“查看代码”。

     添加姓名和消息属性,如图

     接下来回到设计视图,从工具箱中找到“Code”控件,拖动到设计界面中,然后将它的“Name”属性更改为“createMessageActivity”,此时会发现Code Activity右上方会出现一个红色惊叹号,如下图:

     这个惊叹号说明没有设置ExcuteCode属性,我们需要创建这个事件,右击它的属性,找到事件,如下图:

双击这个事件,写入一下代码

private void createMessageActivity_ExecuteCode(object sender, EventArgs e)
{
     message = String.Format("Hello {0} !", name);

 

 

2.控制台应用程序

 

     在“解决方案资源管理器”中打开Program.cs文件。增加一个名为message的string类型变量,修改WorkflowCompleted事件用来接受工作流发送的消息。

     //接收问候消息
     string message = string.Empty;
     workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {
     message = e.OutputParameters["Message"].ToString();
     waitHandle.Set();
      };

     接下来,将用户的名字传入工作流中,可以使用Dictionary<string,object>集合。     

     Dictionary<string, object> InParms = new Dictionary<string, object>();
     InParms.Add("姓名", "无忧");

     WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Demo1.Workflow1), InParms);

     最后在控制台输出消息

     Console.WriteLine(message);

     结果如下图:

     

 

[源代码下载] 

 

 [下一节]

posted @ 2008-11-01 12:48 o﹎箜絔┌↘ 阅读(1069) | 评论 (4)编辑