MVVM設計模式之旅 – 通用的命令附加行爲

標題:Adventures in MVVM – Generalized Command Behavior Attachments

   網上有很多關於WPF和Silverlight技術描述附加行爲的例子。在WVVM模型中這些例子對命令綁定結合的非常好。不過有個問題是,對每一個行爲,都會有許伴隨代碼,因爲依賴屬性必須是靜態的,他們不能被抽象成普通的類。

   如果你想附加爲一個Control控件添加一個MouseEnterBehavior的行爲,你需要創建兩到三個依賴屬性在MouseEnter這個類中。他們分別是MouseEnter.Command,MouseEnter.MouseEnterBehavior 和可供選擇的MouseEnter.CommandParameter.

 public class MouseEnter 
    {
        private static readonly DependencyProperty BehaviorProperty =
            DependencyProperty.RegisterAttached(
                "MouseEnterBehavior",
                typeof(MouseEnterBehavior),
                typeof(Control),
                null);

        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(MouseEnter),
                new PropertyMetadata(OnSetCommand));

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached(
                "CommandParameter",
                typeof(object),
                typeof(MouseEnter),
                new PropertyMetadata(OnSetParameter))
//接下來就是處理一些依賴屬性的更改

        private static void OnSetCommand(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var target = dependencyObject as Control;
            if (target == null)
                return;

            GetOrCreateBehavior(target).Command = args.NewValue as ICommand;
        }

        private static void OnSetParameter(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var target = dependencyObject as Control;
            if (target != null)
            {
                GetOrCreateBehavior(target).CommandParameter = args.NewValue;
            }
        }

        protected static MouseEnterBehavior GetOrCreateBehavior(Control control)
        {
            var behavior = control.GetValue(BehaviorProperty) as MouseEnterBehavior;
            if (behavior == null)
            {
                behavior = new MouseEnterBehavior(control);
                control.SetValue(BehaviorProperty, behavior);
            }

            return behavior;
        }

     雖然依賴屬性是靜態的,但是Silverlight 仍然需要你添加靜態的Get和Set方法:

        public static void SetCommand(Control control, ICommand command) { control.SetValue(CommandProperty, command); }
        public static ICommand GetCommand(Control control) { return control.GetValue(CommandProperty) as ICommand; }
        public static void SetCommandParameter(Control control, object parameter) { control.SetValue(CommandParameterProperty, parameter); }
        public static object GetCommandParameter(Control buttonBase) { return buttonBase.GetValue(CommandParameterProperty); }

    經典的案例就是複製粘貼,完成其他相同的功能。爲題就是你需要更改三處不同的類型和許多字符串。
如果你調用的不得當,那麼將不會程序講不會起作用。能編譯,但是就是不能運行,或者程序停在在xaml中出現轉化錯誤。

每當我必須運用複製粘貼達到重用時,我便很沮喪,畏縮。在這種情況下,那些是絕對必須要的,如this。
我相信降低風險的方法就是粘貼之後瘋狂的而複雜的更改。這就是之所以提出用AttachmentBasse這個類來達到通用的代碼的原因。

上述代碼就可以縮減爲:

 public class MouseEnter : Attachment<Control, MouseEnterBehavior, MouseEnter>
    {
        private static readonly DependencyProperty BehaviorProperty = Behavior();
        public static readonly DependencyProperty CommandProperty = Command(BehaviorProperty);
        public static readonly DependencyProperty CommandParameterProperty = Parameter(BehaviorProperty);

        public static void SetCommand(Control control, ICommand command) { control.SetValue(CommandProperty, command); }
        public static ICommand GetCommand(Control control) { return control.GetValue(CommandProperty) as ICommand; }
        public static void SetCommandParameter(Control control, object parameter) { control.SetValue(CommandParameterProperty, parameter); }
        public static object GetCommandParameter(Control buttonBase) { return buttonBase.GetValue(CommandParameterProperty); }
    }

這裏你如果需要複製,你僅僅需要修改第一行就行了。

1.類名是什麼?MouseEnter,修改兩處

2.行爲應該附加到那中類型的控件上?Control

3.你想附加那種類型的行爲?MouseEnterBehavoir

 處理減少配置複雜程度外,實際代碼有原來的58行,縮短到11行代碼,我認爲這是一個偉大的勝利。

      這這塊代碼中,我用到了來自prism框架中的CommandBehaviorBase類,他是通用約束中的一部分。如果你想對你的Behaviors用一些其他東西,替代他,按照你想的。我確信你自己的關於命令行爲的基類將是非常的簡潔。

下面就是Attachment的基類:

 

 public class Attachment<TargetT, BehaviorT, AttachmentT>
        where TargetT : Control
        where BehaviorT : CommandBehaviorBase<TargetT>
    {
        public static DependencyProperty Behavior()
        {
            return DependencyProperty.RegisterAttached(
                typeof(BehaviorT).Name,
                typeof(BehaviorT),
                typeof(TargetT),
                null);
        }

        public static DependencyProperty Command(DependencyProperty behaviorProperty)
        {
            return DependencyProperty.RegisterAttached(
                "Command",
                typeof (ICommand),
                typeof(AttachmentT),
                new PropertyMetadata((target, args) => OnSetCommandCallback(target, args, behaviorProperty)));
        }

        public static DependencyProperty Parameter(DependencyProperty behaviorProperty)
        {
            return DependencyProperty.RegisterAttached(
                "CommandParameter",
                typeof (object),
                typeof (AttachmentT),
                new PropertyMetadata((target, args) => OnSetParameterCallback(target, args, behaviorProperty)));
        }

        protected static void OnSetCommandCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e, DependencyProperty behaviorProperty)
        {
            var target = dependencyObject as TargetT;
            if (target == null)
                return;

            GetOrCreateBehavior(target, behaviorProperty).Command = e.NewValue as ICommand;
        }

        protected static void OnSetParameterCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e, DependencyProperty behaviorProperty)
        {
            var target = dependencyObject as TargetT;
            if (target != null)
            {
                GetOrCreateBehavior(target, behaviorProperty).CommandParameter = e.NewValue;
            }
        }

        protected static BehaviorT GetOrCreateBehavior(Control control, DependencyProperty behaviorProperty)
        {
            var behavior = control.GetValue(behaviorProperty) as BehaviorT;
            if (behavior == null)
            {
                behavior = Activator.CreateInstance(typeof(BehaviorT), control) as BehaviorT;
                control.SetValue(behaviorProperty, behavior);
            }

            return behavior;
        }
    }

最後,爲了完成例子,這裏給出MouseEnterBehavoior 的示例:

 public class MouseEnterBehavior : CommandBehaviorBase<Control>
    {
        public MouseEnterBehavior(Control selectableObject)
            : base(selectableObject)
        {
            selectableObject.MouseEnter += (sender, args) => ExecuteCommand();
        }
    }


  在Xaml中應用該行爲,如下:

<Button Behaviors:MouseEnter.Command="{Binding MouseEnter}" Behaviors:MouseEnter.CommandParameter="Optional Paremeter"/> 


                 *********************翻譯文章結束*********************

第一次翻譯,不足之處還請大家多多包涵。之後繼續退出WPF相關翻譯的文章,請關注……

原文地址:http://geekswithblogs.net/HouseOfBilz/archive/2009/08/21/adventures-in-mvvm-ndash-generalized-command-behavior-attachments.aspx

 本文章轉載請聲明文章出處,以表示對作者的尊重。謝謝。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章