学习笔记 -- AttachedProperty, DependencyProperty, Trigger, Action, Behavior, Command [Original]

Attached Property DependencyProperty Trigger Action Behavior Command

1. 附加属性

> Attached Property

> 附加属性是XAML定义的一个概念.附加属性旨在用作可在任何对象上设置的一类全局属性.在 WPF中,附加属性通常定义为没有常规属性“包装”的一种特殊形式的依赖项属性. [MSDN]

> 附加属性不属于被附加的对象, 但是可以对被附加的对象产生影响.

> e.g. :   在C#代码中, 创建一个按钮btn, Button是没有Left属性的, 所以可以用Canvas为btn设置一个附加属性, Left=10;

Button btn = new Button();
Canvas.SetLeft(btn, 10);
在xaml代码中可以看的很清楚, Button btn 被设置了 Canvas.Left="10"的附加属性;

<Canvas >
<Button x:Name="btn" Canvas.Left="10">
</Canvas>
比较常见的, 在Blend里面, 为一个按钮添加Action的触发器, 可以看到生成了Interaction.Triggers附加属性触发器.

<Button x:Name="btn">
<i:Interaction.Triggers>
<i:EventTrigger />
</i:Interaction.Triggers>
</Button>
2. 依赖项属性

> DependencyProperty

> 可通过方法设置的属性,如样式、数据绑定、动画和继承.[MSDN] 在UI上应用广泛, 可以实时和界面交互

> WPF提供了一组服务,这些服务可用于扩展CLR属性的功能.这些服务通常统称为 WPF 属性系统. 由 WPF 属性系统支持的属性称为依赖项属性.

> 只有继承了DependencyObject的类里面才能注册依赖性属性. 因为DependencyObject类提供了读写依赖性属性值的SetValue和GetValue的方法.

> UIElement类继承了DependencyObject类, 所以FrameworkElement, Control, UserControl等界面元素都是DependencyObject,依赖对象.

> 一般注册DependencyProperty, 只要在继承了DependencyObject的类中, 在VS敲入”propdp”就会自动生成模板代码;

> DevDiv中有篇详细的介绍很不错.

     http://www.devdiv.com/thread-51606-1-1.html
     http://www.devdiv.com/thread-51607-1-1.html 

3. Trigger

> 表示应用属性值或有条件地执行操作的触发器. [MSDN ]

> Expression Blend 中提供了以下触发器:

DataStoreChangedTrigger, 根据对数据存储所做的修改调用操作.

DataTrigger, 根据数据绑定属性的值调用操作.

EventTrigger, 根据诸如鼠标单击、页面加载或其他交互等事件调用操作.

KeyTrigger, 在按下键盘上的组合键时调用操作.

PropertyChangedTrigger, 根据对元素或数据存储属性所做的修改调用操作.

StoryboardCompletedTrigger, 在情节要完成之后调用操作.

TimerTrigger, 根据计时器调用操作.

> WPF以及其他Toolkit, SDKs 都提供了很多的触发器.

http://www.cnblogs.com/luluping/archive/2011/07/26/2117681.html

> Trigger可以是单触发器(Trigger), 也可以是多触发器(MultiTrigger), 也可以限定任意个条件(Condition);

4. Action

> System.Windows.Interactivity中定义了很多的Action, 比如TriggerAction, InvokeCommandAction, CallMethodAction等;

> Action, 可以理解为, 只要相应的外部Trigger触发了,就去做一件事, 比如执行一段代码, 调用一个函数.

> 可以自定义Action, 如下, 自定义一个简单的TriggerAction, 最简单的Action就只需要继承TriggerAction, 然后指定作用的类型, 如DependencyObject, 然后override Invoke方法就行了; 下面的代码实现的就是在外面触发这个Action的时候, 弹出”Hello World”对话框;

public class NewAction : System.Windows.Interactivity.TriggerAction<DependencyObject>
{
    protected override void Invoke(object parameter)
        {
            MessageBox.Show(“Hello World”);
        }
    }
当然, 也可以为Action设置属性, 可以是普通属性, 也可以是依赖性属性, 如果需要数据绑定的话, 可以定义成依赖项属性, 这个就很有用, 比如上面的MessageBox显示的是UI上某个需要被监测的属性值, 那就只需要把Action自己的属性值绑定到UI上那个属性值, 在Invoke方法中对这个属性值操作, 就可以在每次Action发生时动态显示. 比如, 加上下面这几句:
public string Msg
        {
            get { return (string)GetValue(MsgProperty); }
            set { SetValue(MsgProperty, value); }
        }
    public static readonly DependencyProperty MsgProperty =
            DependencyProperty.Register("Msg", typeof(string), typeof(NewAction), null);
这几句定义了一个依赖性属性Msg, 加到NewAction类中, 那么NewAction就有了属性值, 在Blend就可以看到NewAction多了Msg这一项, 就可以把它绑定到任何地方. 比如我们可以把它绑定到一个数据源, 然后要在Action执行时显示这个数据, 就可以在Invoke中实现, 把上面的MessageBox.Show(Msg)就可以了.

clip_image002

> 总之, Action就是在外边Trigger触发的时候做出一个动作, 可以给Action定义属性, 最简单的Action实现只要override Invoke()方法即可, 当然还可以override OnAttached(), OnDetaching()等其他方法.

5. Behavior

> 可以理解为给AssociatedObject的一种能力, 不需要自行添加触发器, 也不用自定义Action, 而可以看做是Behavior 在内部封装了一个或多个触发和相应的Action响应.

> Behavior继承了IAttachedObject接口, 可以是任意类型的AssociatedObject成员, 作用于AssociatedObject对象.

> 自定义一个Behavior, 例如, 要求用鼠标拖拽对象时,对象的Width跟随变化, 可以如下这样设计:

public class NewBehaviors:System.Windows.Interactivity.Behavior<FrameworkElement>
    {
        // override OnAttached
        protected override void OnAttached()
        {
            // 注册AssociatedObject的一个或多个事件, 作为Behavior的内部Trigger
            this.AssociatedObject.MouseMove  = new MouseEventHandler(AssociatedObject_MouseMove);
            base.OnAttached();
        }
        Point oldPosition = new Point(-10000, -10000);
        // 定义AssociatedObject的事件响应方法, 相当于Behavior的内部Action
        // 实现的是, 当鼠标拖拽AssociatedObject时候,其Width跟随变化
        void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            var newPosition = e.GetPosition(AssociatedObject); 
            var deltaPosition = new Point(newPosition.X - oldPosition.X, newPosition.Y - oldPosition.Y); 
            AssociatedObject.Width  = deltaPosition.X;
            AssociatedObject.Height  = deltaPosition.Y;
            oldPosition = newPosition;
        }
        // override OnDetaching,在Behavior解除在AssociatedObject依附时执行,不再订阅事件消息
        protected override void OnDetaching()
        {
            this.AssociatedObject.MouseMove -= new MouseEventHandler(AssociatedObject_MouseMove);
            base.OnDetaching();
        }
    }

6. Command

> System.Windows.Input.ICommand接口:

. CanExecute : 此方法由Command定义, 在系统收到Command发送的CanExecuteChanged通知时由系统调用, 并返回bool值, 判断是否执行Execute()方法; 通常此方法的返回布尔值会自动关联设置了此Command的UI的IsEnabled属性;

. Execute : Command命令调用的执行方法.

. CanExecuteChanged : 此事件由此Command本身抛出, 只要UI设置了此Command, 事件就会被系统自动订阅, 然后系统即调用CanExecute()方法判断是否执行Execute().

> 在Silverlight中, 支持命令启动的UI的类有: ButtonBase, Hyperlink 及其派生类.

> 可以自己定义一个Command, 需要自己组织上面的CanExecute(), Execute(), CanExecuteChanged三者之间的关系, 并重写两个方法.

比如, 可以自定义一个绑定到Button上的Command, 要求延时10s后Button才会生效(Enable), 然后每次点击Button的时候, 都会弹出”hello world”的对话框作为响应.

// 创建一个MyCommand类, 继承自ICommand.

// 创建一个MyCommand类, 继承自ICommand.
     public class MyCommand : ICommand
        {
    
    // 此示例中,用的是比较简单的CanExecute方法, 直接返回can值, 
    当需要界定多个条件才返回true时, 此方法就可以改写为各个条件的组合, 可以有效扩展

        public bool CanExecute(object parameter)
        {
            return can;
        }
    // 重写Excute()方法, 此方法就是弹出一个”Hello World”的MessageBox.
        public void Execute(object parameter)
        {
            MessageBox.Show("hello world");
        }
    // 显式继承接口成员 CanExecuteChanged
        public event EventHandler CanExecuteChanged;

    // 定义一个1Tick/s的定时器
        public System.Windows.Threading.DispatcherTimer tmr = 
        new System.Windows.Threading.DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
        
      // 定义一个开始计时的时间
    private DateTime startTime { get; set; }
    
    // 构造函数, 注册定时器Tick事件, 并启动定时器,开始计时.
        public MyCommand()
        {
            this.tmr.Tick  = new EventHandler(tmr_Tick);
            tmr.Start();
            startTime = DateTime.Now;
        }

        bool can = false;

    // Tick事件处理函数, 定时检查是否10s已到, 并在条件成立时抛出CanExecuteChanged事件.
        void tmr_Tick(object sender, EventArgs e)
        {
            if (DateTime.Now - startTime >= TimeSpan.FromSeconds(10))
            {
                can = true;
                tmr.Stop();
                this.tmr.Tick -= new EventHandler(tmr_Tick);
                if (CanExecuteChanged != null)
                {
                    CanExecuteChanged(this, null);
                }
            }
        }
}
     此例中, 有CanExecuteChanged事件抛出, 有CanExecute()判断,还有Execute()执行方法, 这就是一个简单完整的Command.
> Galasoft提供的MvvmLight Tookit提供了已经完成内部实现的ICommand命令: RelayCommand.
. public RelayCommand(Action execute);
. public RelayCommand(Action execute, Func<bool> canExecute);
调用的时候, 任选上面的两个方法, 然后定义相应的Action 委托就可以了.
 
 
 

posted @ 2012-07-22 22:21  Vancee  阅读(561)  评论(0编辑  收藏  举报