Blend Behavior的 使用簡介 實現原理以及在MVVM框架的妙用


Behavior的實現原理以及在MVVM框架的妙用

 

摘要 : Behavior 是微軟在Blend中新添加的功能,通過在Blend中通過拖拖鼠標很容易就可以通過behavior給元素添加移動 旋轉 拉伸等等效果,並且實現行為的代碼和UI元素是分離的因此行為是可以復用的大大的提高了編程的靈活性並減少了編寫重復枯燥的代碼。雖然行為是在Blend中使用的並且使用起來十分簡答,但是背后的實現原理以及思想還是值得研究一番。本文將從behavior的基本概念、具體使用方法、實現原理以及在項目中的具體實踐對behavior做一個介紹。

大綱:

1 Behavior基本概念

Behavior 行為是微軟在Blend中提供的一種新的功能,我覺得其最大的好處我覺得是可以復用,可以將實現的一個功能比如一個動畫效果 事件響應方法等附加到某個元素上 那么被附加的元素就具有了相應的功能而不需要重復實現這些功能。比如Blend中自帶了一些已經實現的行為只需要拖到需要用到的元素上元素就具有了對應的行為 比如拖動、放大縮小等等; 實現行為的代碼和使用行為的元素可以處於不同的程序集中兩者是松耦合的。

2 Blend 中的Behavior以及自定義Behavior

在blend中有一些的行為如圖1-1所示:

 

使用非常簡單只需要將相應的行為拖到需要應用此行為的元素上即可

<i:Interaction.Behaviors>

<el:MouseDragElementBehavior/>

</i:Interaction.Behaviors>

以上代碼是將行為拖到某個元素后Blend自動生成的代碼,其實就是給元素賦了一個附加屬性的值,被附加該行為的元素就可以拖動了。

 

自定義行為:

以上是Blend中以實現的行為,接下來介紹如何自定義行為這里也是自己實現一個支持鼠標拖動的行為。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace BehaviorLibrary
{
    public class DiagInCanvasBehavior:Behavior<UIElement>
    {
        private Canvas canvas;
        private bool isDragging=false;
        private Point mousePoint;

        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.MouseDown += AssociatedObject_MouseDown;
            this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            this.AssociatedObject.MouseDown -= AssociatedObject_MouseDown;
            this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
        }

        private void AssociatedObject_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (canvas == null)
            {
                canvas = (Canvas)System.Windows.Media.VisualTreeHelper.GetParent(this.AssociatedObject);
            }

            isDragging = true;
            mousePoint = e.GetPosition(this.AssociatedObject);
            AssociatedObject.CaptureMouse();
        }

        private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                Point point = e.GetPosition(canvas);
                Canvas.SetTop(AssociatedObject, point.Y - mousePoint.Y);
                Canvas.SetLeft(AssociatedObject, point.X - mousePoint.X);
            }
        }

        private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isDragging)
            {
                AssociatedObject.ReleaseMouseCapture();
                isDragging = false;
            }
        }
    }
}
View Code

 

在上述自定義的拖放行為就是從Behavior類繼承 然后重載OnAttached()方法 ,在這個方法中可以給被附加行為的對象的事件添加具體的響應方法(也即各種具體行為的實現方法);

Behavior的主要作用就是可以附加、優點就是已經實現的行為在將來可以復用,非常的靈活簡單。

當然在上述實現的拖放操作也是有其局限性的 首先被附加的對象是從UIElement繼承的並且能接受上述使用到的幾個鼠標事件,在使用過程中發現將其附加在Button TextBox上時調試發現無法響應鼠標事件深層次的原因還請大家幫忙分析分析.。

3 Behavior具體實現原理

講到Behavior的實現原理不得不提附加屬性,附加屬性是個神奇的東西,對於一般屬性或是依賴屬性若某個對象沒有改屬性或依賴屬性那么直接給他賦值肯定報錯,而附加屬性則不同,只要該對象從DependencyObject繼承那么就可以給該對象設置附加屬性,並且被附加的對象對該屬性一無所知例如Canvas.Top Grid.Row等;(這里只是對於附加屬性的做一個簡單片面的介紹 具體了解還需參考其他文檔);具體到Behavior就是利用了附加屬性附加的功能,可以將行為附加到任何依賴對象;

在這里通過Reflector這一.NET的強大反編譯工具反編譯System.Windows.Interactivity程序集來看看behavior的具體實現,這里介紹Behavior類 以及Interaction

using System;

    using System.Globalization;

    using System.Threading;

    using System.Windows;

    using System.Windows.Media.Animation;

 

    public abstract class Behavior : Animatable, IAttachedObject

    {

        private DependencyObject associatedObject;

        private EventHandler AssociatedObjectChanged;

        private Type associatedType;

 

        internal event EventHandler AssociatedObjectChanged

        {

            add

            {

                EventHandler handler2;

                EventHandler associatedObjectChanged = this.AssociatedObjectChanged;

                do

                {

                    handler2 = associatedObjectChanged;

                    EventHandler handler3 = (EventHandler) Delegate.Combine(handler2, value);

                    associatedObjectChanged = Interlocked.CompareExchange<EventHandler>(ref this.AssociatedObjectChanged, handler3, handler2);

                }

                while (associatedObjectChanged != handler2);

            }

            remove

            {

                EventHandler handler2;

                EventHandler associatedObjectChanged = this.AssociatedObjectChanged;

                do

                {

                    handler2 = associatedObjectChanged;

                    EventHandler handler3 = (EventHandler) Delegate.Remove(handler2, value);

                    associatedObjectChanged = Interlocked.CompareExchange<EventHandler>(ref this.AssociatedObjectChanged, handler3, handler2);

                }

                while (associatedObjectChanged != handler2);

            }

        }

 

        internal Behavior(Type associatedType)

        {

            this.associatedType = associatedType;

        }

 

        public void Attach(DependencyObject dependencyObject)

        {

            if (dependencyObject != this.AssociatedObject)

            {

                if (this.AssociatedObject != null)

                {

                    throw new InvalidOperationException(ExceptionStringTable.CannotHostBehaviorMultipleTimesExceptionMessage);

                }

                if ((dependencyObject != null) && !this.AssociatedType.IsAssignableFrom(dependencyObject.GetType()))

                {

                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, ExceptionStringTable.TypeConstraintViolatedExceptionMessage, new object[] { base.GetType().Name, dependencyObject.GetType().Name, this.AssociatedType.Name }));

                }

                base.WritePreamble();

                this.associatedObject = dependencyObject;

                base.WritePostscript();

                this.OnAssociatedObjectChanged();

                this.OnAttached();

            }

        }

 

        protected override Freezable CreateInstanceCore()

        {

            return (Freezable) Activator.CreateInstance(base.GetType());

        }

 

        public void Detach()

        {

            this.OnDetaching();

            base.WritePreamble();

            this.associatedObject = null;

            base.WritePostscript();

            this.OnAssociatedObjectChanged();

        }

 

        private void OnAssociatedObjectChanged()

        {

            if (this.AssociatedObjectChanged != null)

            {

                this.AssociatedObjectChanged(this, new EventArgs());

            }

        }

 

        protected virtual void OnAttached()

        {

        }

 

        protected virtual void OnDetaching()

        {

        }

 

        protected DependencyObject AssociatedObject

        {

            get

            {

                base.ReadPreamble();

                return this.associatedObject;

            }

        }

 

        protected Type AssociatedType

        {

            get

            {

                base.ReadPreamble();

                return this.associatedType;

            }

        }

 

        DependencyObject IAttachedObject.AssociatedObject

        {

            get

            {

                return this.AssociatedObject;

            }

        }

    }

}
View Code

//*******************Interaction類使用行為的類*******************//

namespace System.Windows.Interactivity

{

    using System;

    using System.Runtime.CompilerServices;

    using System.Windows;

 

    public static class Interaction

    {

        private static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached("ShadowBehaviors", typeof(BehaviorCollection), typeof(Interaction), new FrameworkPropertyMetadata(new PropertyChangedCallback(Interaction.OnBehaviorsChanged)));

        private static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached("ShadowTriggers", typeof(System.Windows.Interactivity.TriggerCollection), typeof(Interaction), new FrameworkPropertyMetadata(new PropertyChangedCallback(Interaction.OnTriggersChanged)));

 

        public static BehaviorCollection GetBehaviors(DependencyObject obj)

        {

            BehaviorCollection behaviors = (BehaviorCollection) obj.GetValue(BehaviorsProperty);

            if (behaviors == null)

            {

                behaviors = new BehaviorCollection();

                obj.SetValue(BehaviorsProperty, behaviors);

            }

            return behaviors;

        }

 

        public static System.Windows.Interactivity.TriggerCollection GetTriggers(DependencyObject obj)

        {

            System.Windows.Interactivity.TriggerCollection triggers = (System.Windows.Interactivity.TriggerCollection) obj.GetValue(TriggersProperty);

            if (triggers == null)

            {

                triggers = new System.Windows.Interactivity.TriggerCollection();

                obj.SetValue(TriggersProperty, triggers);

            }

            return triggers;

        }

 

        internal static bool IsElementLoaded(FrameworkElement element)

        {

            return element.IsLoaded;

        }

 

        private static void OnBehaviorsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)

        {

            BehaviorCollection oldValue = (BehaviorCollection) args.OldValue;

            BehaviorCollection newValue = (BehaviorCollection) args.NewValue;

            if (oldValue != newValue)

            {

                if ((oldValue != null) && (oldValue.AssociatedObject != null))

                {

                    oldValue.Detach();

                }

                if ((newValue != null) && (obj != null))

                {

                    if (newValue.AssociatedObject != null)

                    {

                        throw new InvalidOperationException(ExceptionStringTable.CannotHostBehaviorCollectionMultipleTimesExceptionMessage);

                    }

                    newValue.Attach(obj);

                }

            }

        }

 

        private static void OnTriggersChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)

        {

            System.Windows.Interactivity.TriggerCollection oldValue = args.OldValue as System.Windows.Interactivity.TriggerCollection;

            System.Windows.Interactivity.TriggerCollection newValue = args.NewValue as System.Windows.Interactivity.TriggerCollection;

            if (oldValue != newValue)

            {

                if ((oldValue != null) && (oldValue.AssociatedObject != null))

                {

                    oldValue.Detach();

                }

                if ((newValue != null) && (obj != null))

                {

                    if (newValue.AssociatedObject != null)

                    {

                        throw new InvalidOperationException(ExceptionStringTable.CannotHostTriggerCollectionMultipleTimesExceptionMessage);

                    }

                    newValue.Attach(obj);

                }

            }

        }

 

        internal static bool ShouldRunInDesignMode

        {

            [CompilerGenerated]

            get

            {

                return <ShouldRunInDesignMode>k__BackingField;

            }

            [CompilerGenerated]

            set

            {

                <ShouldRunInDesignMode>k__BackingField = value;

            }

        }

    }

}
View Code

 

Interaction類中我們可以看到  private static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached("ShadowBehaviors", typeof(BehaviorCollection), typeof(Interaction), new FrameworkPropertyMetadata(new PropertyChangedCallback(Interaction.OnBehaviorsChanged))); BehaviorsProperty這個附加屬性 回顧在Blend中自動生成的代碼如下

<i:Interaction.Behaviors>

<el:MouseDragElementBehavior/>

</i:Interaction.Behaviors>

就是給附加行為的元素設置了Interaction.Behaviors這個附加屬性,這里注冊附加屬性時非常重要一點就是PropertyChangedCallback這個回調委托通過這個函數可以得到此附加屬性被附加的對象的一個引用;

在講這個回調函數之前先來看看Behavior類的功能 Behavior實現了IAttachedObject   接口 該接口定義如下;

  public interface IAttachedObject   

{

         DependencyObject AssociatedObject { get; }     

         void Attach(DependencyObject dependencyObject);   

         void Detach();

    }

實現這個接口的類的實例可以被附加到另一個對象,通常我們在自定義行為的時候不是直接繼承自IAttachedObject接口,而是繼承自從 IAttachedObject繼承的一些類,這些類都有可重載的OnAttached(),OnDetached()方法,比如 在上面所介紹的自定義行為就是從Behavior類繼承然后重載上述方法; 再回到Interaction類BehaviorsProperty 的類型是 BehaviorCollection 其實是Behavior類的集合形式這樣就可以給一個元素同時附加多個行為,因此了解了Behavior類的代碼就對BehaviorCollection也能觸類旁通。在了解了Behavior類以及IAttachedObject接口后接着講PropertyChangedCallback回調內托在該委托調用的方法OnBehaviorsChanged 中就是將行為附加到被附加屬性附加的對象   newValue.Attach(obj);(回調函數中的關鍵代碼)

通過上面的源碼分析我們可以發現行為的實現機制還是很簡單的,利用了附加屬性以及IAttachedObject接口 給附加的對象添加一些特定的代碼也即某些行為比如拖放操作就是通過響應附加對象的幾個鼠標事件來實現了一個簡單的拖放功能;

4Blend中的Behavior的擴展應用

為了滿足MVVM模式的要求 需要將View層后台無代碼,用戶在View上的操作通過命令在ViewModel層中進行響應,然而除了Button ContextMenu等幾個有限的控件內置實現了事件觸發命令的功能 大部分的控件的相關事件並無法和ViewModel建立關聯,為了解決上述問題在我們開發項目中用到的MVVM框架中借鑒Blend中Behavior行為的實現機制通過附加的思想可以將View層控件觸發的事件 與命令建立聯系 從而在ViewModel層執行。

下面給出具體實現代碼:

using System;

using System.Collections.Specialized;

using System.Linq;

using System.Reflection;

using System.Windows;

using System.Windows.Input;

 

namespace Wpf.MVVM.Foundation.Behaviors

{

    ///<summary>

    ///功能描述:1.執行事件與命令的鏈接與移除鏈接功能

    ///          2.繼承至Freezable,主要利用其與WPF的傳遞DataContext功能

    ///創建人:*** 

    ///編寫日期:2013-08-01

    ///</summary>

    public class CommandEvent : Freezable

    {

        #region 依賴屬性定義

        /// <summary>

        /// Command依賴屬性定義

        /// </summary>

        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandEvent), new UIPropertyMetadata(null));

 

        /// <summary>

        /// CommandParameter依賴屬性定義,Command的參數

        /// </summary>

        public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(CommandEvent), new UIPropertyMetadata(null));

 

        /// <summary>

        /// 事件依賴屬性定義

        /// </summary>

        public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(String), typeof(CommandEvent), new UIPropertyMetadata(string.Empty));

        #endregion //依賴屬性定義

 

        #region 屬性/變量定義

        /// <summary>

        /// 獲取或設置事件Event依賴屬性

        /// </summary>

        public string Event

        {

            get { return (string)GetValue(EventProperty); }

            set { SetValue(EventProperty, value); }

        }

 

        /// <summary>

        /// 獲取或設置命令Command依賴屬性

        /// </summary>

        public ICommand Command

        {

            get { return (ICommand)GetValue(CommandProperty); }

            set { SetValue(CommandProperty, value); }

        }

 

        /// <summary>

        ///獲取或設置命令參數CommandParameter依賴屬性

        /// </summary>

        public object CommandParameter

        {

            get { return GetValue(CommandParameterProperty); }

            set { SetValue(CommandParameterProperty, value); }

        }

        #endregion 屬性/變量定義

 

        #region 私有函數

        /// <summary>

        /// 將事件和命令進行鏈接

        /// </summary>

        /// <param name="target">所附加的窗體對象</param>

        internal void Subscribe(object target)

        {

            if (target == null)//若附加對象為null,直接退出

                return;

 

            string eventName = Event;

            EventInfo ei = target.GetType().GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance);//在附加窗體對象中查找事件類型

            if (ei != null)

            {

                ei.RemoveEventHandler(target, GetEventMethod(ei));//處於安全目的,首先移除事件處理委托

                ei.AddEventHandler(target, GetEventMethod(ei));//添加事件處理委托

                return;

            }

 

            //移除事件名稱字符串中可能存在的命令空間字符串

            int dotPos = eventName.IndexOf(':');

            if (dotPos > 0)

                eventName = eventName.Substring(dotPos + 1);

 

 

            dotPos = eventName.IndexOf('.');   // 查找依賴對象的附加路由事件

            if (dotPos > 0 && eventName.Length > dotPos)

            {

 

                var attachedEvent = EventManager.GetRoutedEvents().Where(evt => evt.ToString() == eventName).SingleOrDefault();//在事件管理中,查找對應的路由事件

                if (attachedEvent == null)//未查找到,直接返回

                    return;

 

                FrameworkElement fe = target as FrameworkElement;

                if (fe == null)

                    return;

 

                fe.RemoveHandler(attachedEvent, GetRoutedEventMethod());//處於安全目的,首先移除路由事件處理委托

                fe.AddHandler(attachedEvent, GetRoutedEventMethod());//添加路由事件處理委托

            }

        }

 

        /// <summary>

        /// 解除事件和命令的鏈接

        /// </summary>

        /// <param name="target">所附加的窗體對象</param>

        internal void Unsubscribe(object target)

        {

            if (target == null)//若附加對象為null,直接退出

                return;

 

            EventInfo ei = target.GetType().GetEvent(Event, BindingFlags.Public | BindingFlags.Instance);

            if (ei != null)//常規事件,直接移除事件委托

            {

                ei.RemoveEventHandler(target, GetEventMethod(ei));

                return;

            }

 

            string eventName = Event;

 

            //移除事件名稱字符串中可能存在的命令空間字符串

            int dotPos = eventName.IndexOf(':');

            if (dotPos > 0)

                eventName = eventName.Substring(dotPos + 1);

 

            dotPos = eventName.IndexOf('.'); // 查找依賴對象的附加路由事件

            if (dotPos > 0 && eventName.Length > dotPos)

            {

                //在事件管理中,查找對應的路由事件

                var attachedEvent = EventManager.GetRoutedEvents().Where(evt => evt.Name == eventName).SingleOrDefault();

                if (attachedEvent != null)

                {

                    FrameworkElement fe = target as FrameworkElement;

                    if (fe != null)//移除路由事件處理委托

                        fe.RemoveHandler(attachedEvent, GetRoutedEventMethod());

                }

            }

        }

 

        /// <summary>

        ///  獲取事件的執行委托

        /// </summary>

        /// <param name="ei">事件信息</param>

        /// <returns>返回事件的執行委托,失敗,返回null</returns>

        private Delegate GetEventMethod(EventInfo ei)

        {

            if (ei == null || ei.EventHandlerType == null)

                return null;

 

            return Delegate.CreateDelegate(ei.EventHandlerType, this, GetType().GetMethod("OnEventRaised", BindingFlags.NonPublic | BindingFlags.Instance));

        }

 

        /// <summary>

        /// 獲取路由事件的執行委托

        /// </summary>

        /// <returns>返回路由事件的執行委托,失敗,返回null</returns>

        private Delegate GetRoutedEventMethod()

        {

            return Delegate.CreateDelegate(typeof(RoutedEventHandler), this, GetType().GetMethod("OnEventRaised", BindingFlags.NonPublic | BindingFlags.Instance));

        }

 

        /// <summary>

        /// 該方法被事件調用,其調用命令處理函數

        /// </summary>

        /// <param name="sender">事件發送者</param>

        /// <param name="e">參數</param>

        private void OnEventRaised(object sender, EventArgs e)

        {

            if (Command == null)

               return;

            ICommand c = Command;

            if (Command.CanExecute(CommandParameter))

                Command.Execute(CommandParameter);

        }

        #endregion //私有函數

 

        #region Freezable 抽象成員實現

        /// <summary>

        /// 未實現,返回null

        /// </summary>

        /// <returns>返回null</returns>

        protected override Freezable CreateInstanceCore()

        {

            return null;

        }

        #endregion Freezable 抽象成員實現

    }

 

 

    ///<summary>

    ///功能描述:1.事件與命令映射的集合

    ///          2.繼承至FreezableCollection<T>,主要為在WFP Visual Tree 上傳遞DataContext對象

    ///創建人:*** 

    ///編寫日期:2013-08-01

    ///</summary>

    public class CommandEventCollection : FreezableCollection<CommandEvent>

    {

        #region 屬性/變量

        private object _target;//附加對象

        #endregion //屬性/變量

 

        #region 構造函數

        /// <summary>

        /// 構造函數

        /// </summary>

        public CommandEventCollection()

        {

            ((INotifyCollectionChanged)this).CollectionChanged += OnCollectionChanged;//添加集合變更事件處理函數

        }

        #endregion //構造函數

 

        #region 私有函數

        /// <summary>

        /// 集合中的命令和事件進行鏈接

        /// </summary>

        /// <param name="target">附加對象</param>

        internal void Subscribe(object target)

        {

            _target = target;//初始化附加對象

 

            foreach (var item in this)

                item.Subscribe(target);

        }

 

        /// <summary>

        /// 集合中的命令和事件解除鏈接

        /// </summary>

        /// <param name="target">附加對象</param>

        internal void Unsubscribe(object target)

        {

            foreach (var item in this)

                item.Unsubscribe(target);

 

            _target = null;

        }

 

        /// <summary>

        /// 事件與命令的集合變更函數

        /// </summary>

        /// <param name="sender">事件觸發者</param>

        /// <param name="e">參數</param>

        private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

        {

            switch (e.Action)//集合變更類型

            {

                case NotifyCollectionChangedAction.Add://添加新元素

                    foreach (var item in e.NewItems)

                        OnItemAdded((CommandEvent)item);

                    break;

 

                case NotifyCollectionChangedAction.Remove://移除元素

                    foreach (var item in e.OldItems)

                        OnItemRemoved((CommandEvent)item);

                    break;

 

                case NotifyCollectionChangedAction.Replace://更新元素

                    foreach (var item in e.OldItems)

                        OnItemRemoved((CommandEvent)item);

 

                    foreach (var item in e.NewItems)

                        OnItemAdded((CommandEvent)item);

                    break;

 

                case NotifyCollectionChangedAction.Move:

                    break;

 

                case NotifyCollectionChangedAction.Reset://集合重置

                    foreach (var item in this)

                        OnItemRemoved(item);

 

                    foreach (var item in this)

                        OnItemAdded(item);

                    break;

 

                default:

                    return;

            }

        }

 

        /// <summary>

        /// 添加新元素

        /// </summary>

        /// <param name="item">新元素</param>

        private void OnItemAdded(CommandEvent item)

        {

            if (item != null && _target != null)

            {

                item.Subscribe(_target);

            }

        }

 

        /// <summary>

        /// 移除元素

        /// </summary>

        /// <param name="item">元素</param>

        private void OnItemRemoved(CommandEvent item)

        {

            if (item != null && _target != null)

            {

                item.Unsubscribe(_target);

            }

        }

        #endregion //私有函數

    }

 

    ///<summary>

    ///功能描述:1.管理事件與命令鏈接集合

    ///          2.繼承至FreezableCollection<T>,主要為在WFP Visual Tree 上傳遞DataContext對象

    ///          3.注意,若為生命周期事件(加載、激活、關閉、已關閉等),請使用生命周期事件行為代替

    ///創建人:*** 

    ///編寫日期:2013-08-01

    ///</summary>

    /// <example>

    /// <![CDATA[

    /// 

    /// <Behaviors:EventCommander.Mappings>

    ///    <Behaviors:CommandEvent Command="{Binding MouseEnterCommand}" Event="MouseEnter" />

    ///    <Behaviors:CommandEvent Command="{Binding MouseLeaveCommand}" Event="MouseLeave" />

    /// </Behaviors:EventCommander.Mappings>

    /// 

    /// ]]>

    /// </example>

    public static class EventCommander

    {

        #region 依賴屬性定義

        /// <summary>

        /// 事件與命令映射集合附加依賴屬性定義

        /// </summary>

        internal static readonly DependencyProperty MappingsProperty = DependencyProperty.RegisterAttached(

                           "InternalMappings",

                            typeof(CommandEventCollection),

                            typeof(EventCommander),

                            new UIPropertyMetadata(null, OnMappingsChanged)

                            );

 

        /// <summary>

        /// Mappings附加依賴屬性讀取方法

        /// </summary>

        /// <param name="obj">附加對象</param>

        /// <returns>返回附加依賴屬性值</returns>

        public static CommandEventCollection GetMappings(DependencyObject obj)

        {

            return InternalGetMappingCollection(obj);

        }

 

        /// <summary>

        /// Mappings附加依賴屬性設置方法

        /// </summary>

        /// <param name="obj">附加對象</param>

        /// <param name="value">設置值</param>

        public static void SetMappings(DependencyObject obj, CommandEventCollection value)

        {

            obj.SetValue(MappingsProperty, value);

        }

        #endregion //依賴屬性定義

 

        #region 私有函數

        /// <summary>

        /// 獲取Mapping依賴屬性值,若為null,則初始化為空集合

        /// </summary>

        /// <param name="obj">附加對象</param>

        /// <returns>返回Mapping依賴屬性值</returns>

        internal static CommandEventCollection InternalGetMappingCollection(DependencyObject obj)

        {

            var map = obj.GetValue(MappingsProperty) as CommandEventCollection;

            if (map == null)//屬性值為null,初始化新集合

            {

                map = new CommandEventCollection();

                SetMappings(obj, map);

            }

 

            return map;

        }

 

        /// <summary>

        /// Mapping附加依賴屬性值變更處理函數

        /// </summary>

        /// <param name="target">附加對象</param>

        /// <param name="e">信息參數</param>

        private static void OnMappingsChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)

        {

            if (e.OldValue != null)

            {

                CommandEventCollection cec = e.OldValue as CommandEventCollection;

                if (cec != null)//解除命令與事件鏈接

                    cec.Unsubscribe(target);

            }

 

            if (e.NewValue != null)

            {

                CommandEventCollection cec = e.NewValue as CommandEventCollection;

                if (cec != null)//進行命令與事件鏈接

                    cec.Subscribe(target);

            }

        }

        #endregion //私有函數

    }

}
View Code

 

上述代碼實現思想和Blend中的Behavior相似,使用方法如下所示

 <Behaviors:EventCommander.Mappings>

 <Behaviors:CommandEvent Command="{Binding MouseEnterCommand}" Event="MouseEnter" /> 

<Behaviors:CommandEvent Command="{Binding MouseLeaveCommand}" Event="MouseLeave" />   </Behaviors:EventCommander.Mappings>

上述代碼完成的功能就是將UI的MouseEnter MouseLeave事件分別綁定到ViewModel層的MouseEnterCommand MouseLeaveCommand命令;這樣就可以很方便的把View層的UI事件響應代碼轉移到ViewModel層。

 

上述實現的思路就是給UI元素賦MappingsProperty附加屬性的值 在注冊的回調函數中獲取被附加該依賴屬性的UI元素對象,然后通過反射查找到對應名稱的事件,並給對應事件添加響應函數然后再響應函數中觸發綁定的命令;從而實現了前台UI事件觸發ViewModel層的命令的效果;

大體思路如上所示可以結合Blend Behavior的實現代碼 對比了解。另外希望各位大神提出寶貴的意見,包括上述實現的不足的地方 ,代碼中問題或是有更好地實現方法 ,衷心希望相互交流,共同學習,共同進步;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM