坚持学习WF(5):自定义活动(CustomActivity)

当WF提供的标准活动不能满足我们的需求的时候,我们就需要定义自己的活动。工作流引擎并不会区别一个活动是WF提供的标准活动还是第三方自定义活动.自定义活动有两种方式,组合方式和继承方式.组合是你从工具箱里拖出你需要的活动将他们组织在一起形成一个新的活动;使用继承的方式我们需要编写一个类,该类可以继承Activity类或其他的类,比如SequenceActivity等.组合的方式比较简单,下面我们就使用继承的方式来自定义一个活动。


实现逻辑
 

我现在要完成一个这样的活动,我去银行存钱,当我存钱的时候会有一个友情提示,提示我保护好您的密码,然后如果存钱的数额小于1000就提示说钱太少,不能存,如果大于1000就提示存款成功.

下面我们就新建一个Activity名字叫CreditActivity,我们在它的属性里将Base类设置为Activity,这个基类已经足够了,然后我们增加一个依赖属性AccountProperty来表示存款数额,代码如下:

public   static  DependencyProperty AccountProperty   =  System.Workflow.ComponentModel.DependencyProperty.Register( 
           
" Account " typeof (Int32),  typeof (CreditActivity)); 
      
 [Description( " 存款数额 " )] 
       [Category(
" 自定义活动示例 " )] 
       [Browsable(
true )] 
       [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 
       
public  Int32 Account 
       { 
           
get  
           { 
               
return  ((Int32)( base .GetValue( 
                   CreditActivity.AccountProperty))); 
           } 
           
set  
           { 
               
base .SetValue(CreditActivity.AccountProperty, value); 
           } 
       }

然后在增加一个依赖事件BeCarefulEvent ,用于当用户存钱的提示它注意保护密码,代码如下:

public   static  DependencyProperty BeCarefulEvent  =  DependencyProperty.Register( " BeCareful " typeof (EventHandler < CustomActivityEventArgs > ),  typeof (CreditActivity)); 
       [DescriptionAttribute(
" 友情提示:注意保护密码 " )] 
       [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] 
       [BrowsableAttribute(
true )] 
       [Category(
" Handlers " )] 
       
public   event  EventHandler < CustomActivityEventArgs >  BeCareful 
       { 
           add 
           { 
               
base .AddHandler(CreditActivity.BeCarefulEvent, value); 
           } 
           remove 
           { 
               
base .RemoveHandler(CreditActivity.BeCarefulEvent, value); 
           } 
       }

然后我们要实现我们的逻辑代码,我们需要重写Activity类的Execute方法,并在该方法中引发BeCarefulEvent事件。这个方法返回一个ActivityExecutionStatus,代表当前Activity实例的运行状态的枚举值,Return.base.Execute(executionContext)这个默认的返回ActivityExecutionStatus.Closed. ActivityExecutionContext是提供活动运行环境的相关信息.代码如下

protected   override  ActivityExecutionStatus Execute( ActivityExecutionContext executionContext) 
        { 
            CustomActivityEventArgs customActivityEventArgs 
=   new  CustomActivityEventArgs( this .Description); 
            
this .RaiseGenericEvent < CustomActivityEventArgs > (BeCarefulEvent,  this , customActivityEventArgs); 
            
// simulate an account lookup 
             if  (Account  <=   1000
            { 
                Console.WriteLine(
" 钱也太少了吧,要大于1000的 " ); 
            } 
            
else  
            { 
                Console.WriteLine(
" 存好了,下回多存点啊 " ); 
            } 

            
return   base .Execute(executionContext); 
        }

下面我就在Workflow中设置它的存款数额为600,和BeCarefulEvent事件,如下图:

3

(图 一)

BeCarefulEvent的事件处理程序onBeCareful的代码如下:

private   void  onBeCareful( object  sender, CaryActivityLibrary.CustomActivityEventArgs e) 
        { 
            Console.WriteLine(e.ActivityDescription
+ " :小心保护密码 " );
            Console.WriteLine()
         }

现在我们来运行我们的程序:

4


提高设计体验

上面我们只是简单的实现我们的逻辑,但很多时候并不是逻辑是最重要的,我们的设计体验同样重要,下面我们说说自定义活动中怎样才能提高设计的体验。


自定义活动图标

不知道你注意到没有,在(图一)中的我们自定义的活动前的图标很好看,这个也是我自己选的,只要下面的一行代码就可以实现:

[ToolboxBitmap( typeof (CreditActivity),  " Test.png " )] 
public   partial   class  CreditActivity : System.Workflow.ComponentModel.Activity{dot.gif..}

注意图片的生成操作属性应该设置为“嵌入的资源”,如下图:

5


自定义活动主题

(图一)中活动的主题的样式也是我们自己可以定制的,我们新建一个CreditActivityTheme类,继承自ActivityDesignerTheme,在其构造函数中设置我们想要的样式,代码如下:

public   class  CreditActivityTheme : ActivityDesignerTheme 
    { 
        
public  CreditActivityTheme(WorkflowTheme theme) 
            : 
base (theme) 
        { 
            
this .BackColorStart  =  Color.LightSteelBlue; 
            
this .BackColorEnd  =  Color.Gainsboro; 
            
this .BorderStyle  =  DashStyle.DashDot; 
            
this .BorderColor  =  Color.DarkRed; 
            
this .BackgroundStyle  =  LinearGradientMode.ForwardDiagonal;            
        } 
    }

其实Visual Studio本身也提供了强大的主题设计器。


自定义活动行为

我们不但可以设置样式,还可以增加以下的行为来用另一种方式设置我们的存款数额,如下图:

6

要实现上面的并不难,你需要定义一个CreditActivityDesigner类继承自ActivityDesigner,代码如下:

[ActivityDesignerTheme( typeof (CreditActivityTheme))] 
    
public   class  CreditActivityDesigner : ActivityDesigner 
    { 
        
protected   override  System.Collections.ObjectModel.ReadOnlyCollection < DesignerAction >  DesignerActions 
        { 
            
get  
            { 
                List
< DesignerAction >  list  =   new  List < DesignerAction > (); 
                
foreach  (DesignerAction temp  in   base .DesignerActions) 
                { 
                    list.Add(
new  DesignerAction( this , temp.ActionId, temp.Text)); 
                } 
                
return  list.AsReadOnly(); 
            } 
        } 

        
protected   override  ActivityDesignerVerbCollection Verbs 
        { 
            
get  
            { 
                ActivityDesignerVerbCollection NewVerbs 
=   new  ActivityDesignerVerbCollection(); 
                NewVerbs.AddRange(
base .Verbs); 
                ActivityDesignerVerb menu 
=   new  ActivityDesignerVerb( this , DesignerVerbGroup.View,  " 设置存款数额啦 " new  EventHandler(menu_click)); 
                NewVerbs.Add(menu); 
                
return  NewVerbs; 
            } 
        } 
 
         private   void  menu_click( object  sender, EventArgs e) 
        { 
            
using  (CreditActivitySet wf  =   new  CreditActivitySet()) 
            { 
                
int  v  =  ( int ) this .Activity.GetValue(CreditActivity.AccountProperty); 
                wf.Value 
=  v.ToString(); 
                wf.ShowDialog(); 
                
this .Activity.SetValue(CreditActivity.AccountProperty, Convert.ToInt32(wf.Value)); 
            } 
        } 
    }

CreditActivitySet 是设置数额的window form。


自定义活动验证器

你可能还会记得当你拖动一个CodeActivity的时候,如果你没有设置它的ExecuteCode属性,它会有一个叹号,编译不能通过。下面我们也来实现同样的效果,如果我们没有设置存款数额Account,我们就也出现类似的提示,我们定义一个CreditActivityValidator类继承自ActivityValidator。代码如下:

public   class  CreditActivityValidator : ActivityValidator 
    { 
        
public   override  ValidationErrorCollection Validate( 
            ValidationManager vManager, 
object  obj) 
        { 
            ValidationErrorCollection errors 
=   base .Validate(vManager, obj); 
            
if  (obj  is  CreditActivity) 
            { 
                CreditActivity creditActivity 
=  obj  as  CreditActivity; 
                
if  (creditActivity.Parent  !=   null
                { 
                    
if  (creditActivity.Account  ==   0
                    { 
                        errors.Add( 
                            ValidationError.GetNotSetValidationError( 
                                
" Account " )); 
                    } 
                } 
            } 
            
return  errors; 
        } 
    }

效果如下图:

7

我们是通过以下特性来应用到我们自定义的活动上的 : 

 [ActivityValidator( typeof (CreditActivityValidator))] 
    [Designer(
typeof (CreditActivityDesigner))] 
    
public   partial   class  CreditActivity : System.Workflow.ComponentModel.Activity    {dot.gif}


定制工具箱行为

我们还可以定制工具箱行为,就是当我们把一个活动从工具箱拖到工作流设计器上时所做的动作,我另外定义了一个CaryCompositeActivity 活动,继承自 SequenceActivity,这样就可以包含子活动了,当我们把CaryCompositeActivity 拖到工作流设计器时,我们给他添加一些默认的子活动,我们编写了一个CaryCompositeActivityToolboxItem 并继承自ActivityToolboxItem,代码如下:

[Serializable] 
   
public   class  CaryCompositeActivityToolboxItem : ActivityToolboxItem 
   { 
      
  public  CaryCompositeActivityToolboxItem() 
           : 
base () 
       {    } 
        public  CaryCompositeActivityToolboxItem( 
           SerializationInfo info, StreamingContext context) 
               : 
base (info, context) 
       { } 
       
protected   override  IComponent[] CreateComponentsCore(IDesignerHost host) 
       { 
           
CaryCompositeActivity activity  =   new  CaryCompositeActivity(); 
           
IfElseActivity ifElse  =   new  IfElseActivity( " ifElse1 " ); 
           
ifElse.Activities.Add( new  IfElseBranchActivity( " ifFirstCondition " )); 
           ifElse.Activities.Add(
new  IfElseBranchActivity( " ifSecondCondition " )); 
           ifElse.Activities.Add(
new  IfElseBranchActivity( " elseBranch " )); 
           activity.Activities.Add(ifElse); 
           
return   new  IComponent[] { activity }; 
       } 
   }

然后我们在写一个类来实现该活动只能添加指定的子活动,我们编写一个类CaryCompositeActivityDesigner 继承自SequentialActivityDesigner,代码如下:

public   class  CaryCompositeActivityDesigner : SequentialActivityDesigner 
    { 
        
private   static  List < Type >  allowedActivityTypes  =   new  List < Type > (); 
        
static  CaryCompositeActivityDesigner() 
        { 
            
allowedActivityTypes.Add( typeof (IfElseActivity)); 
            allowedActivityTypes.Add(
typeof (IfElseBranchActivity)); 
            allowedActivityTypes.Add(
typeof (CodeActivity)); 
        } 
        
public   override   bool  CanInsertActivities(HitTestInfo insertLocation, 
            ReadOnlyCollection
< Activity >  activitiesToInsert) 
        { 
            
Boolean result  =   true
            
if  (activitiesToInsert  !=   null
            { 
                
foreach  (Activity activity  in  activitiesToInsert) 
                { 
                    result 
=  (allowedActivityTypes.Contains(activity.GetType())); 
                    
if  (result  ==   false
                    { 
                        
break
                    } 
                } 
            } 
            
return  result; 
        } 
    }


效果如下图:

8

allowedActivityTypes 集合中我们指定了只能添加 IfElseActivity,IfElseBranchActivity,CodeActivity三种活动,但是在它的子活动IfElseBranchActivity中就没有这个限制了,因为IfElseBranchActivity也有自己的设计器。

代码下载:CaryActivity
上一篇:坚持学习WF(4):活动(Activity)和依赖属性(DependencyProperty)
下一篇:坚持学习WF(6):开发可复用的宿主程序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值