博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

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

Posted on 2008-04-13 15:25  生鱼片  阅读(8584)  评论(31编辑  收藏  举报

当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{..}

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

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    {}


定制工具箱行为

我们还可以定制工具箱行为,就是当我们把一个活动从工具箱拖到工作流设计器上时所做的动作,我另外定义了一个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):开发可复用的宿主程序