MVVM之旅-给任意的事件绑定命令(Adventures in MVVM – Binding Commands to ANY Event) .

 

当我实现MVVM模式时,令我最为头疼一件事是需要给事件绑定命令。当我使用Prism框架时,我得到一个Button.Click的命令绑定,但是每一个其他的时间都需要单独的进行处理。做这些的时候,需要很多的容易出错的样板代码。在我过去的工作岗位上,我发表一些代码来减轻疼痛。然而,仍需要你针对每一个你想进行绑定的事件写一个新的行为和附加内容。

有一段时间了,我的想法只是直接绑定命令到时间。在这期间我遇到很多困难。例如,每一个事件处理都有一个同的事件参数类型。这需要所有的处理都是动态的。我仍然不能创建一个内置的命令绑定--我将确信对每一个单一控件绑定到超过1个的事件。因此我需要创建一个绑定的集合。创建结构体数据就是给自己创造麻烦---绑定仅工作在VisualTree的FrameWorkElements上。这需要我来写自己的绑定基于我的通用的行为。

以下是非常松散的基础下Chinch MVVM框架。我测试了这些代码在Silverlight和WPF,并且运行真的不错!

假定我有一个像如下的ViewMode:

  1. public class MainPageViewModel : INotifyPropertyChanged  
  2. {  
  3.     ...  
  4.     public ICommand MouseLeaveCommand { getprivate set; }  
  5.     public ICommand MouseEnterCommand { getprivate set; }  
  6.     public ICommand ClickCommand { getprivate set; }  
  7.     ...  
  8. }  

我能绑定命令到一个控件的时间上,以一Button为例:

  1. Button Content="Click Me">  
  2.     <Behaviors:Events.Commands>  
  3.         <Behaviors:EventCommandCollection>  
  4.             <Behaviors:EventCommand CommandName="MouseEnterCommand" EventName="MouseEnter" />  
  5.             <Behaviors:EventCommand CommandName="MouseLeaveCommand" EventName="MouseLeave" />  
  6.             <Behaviors:EventCommand CommandName="ClickCommand" EventName="Click" />  
  7.         </Behaviors:EventCommandCollection>  
  8.     </Behaviors:Events.Commands>  
  9. </Button>  

我不再需写任何的额外的代码,无论何时我想附加命令到我的事件上!下面是砂锅面代码的警告:

 

  1. the XAML requires the EventCommandCollection to be declared in the XAML.  I struggled to figure out how to eliminate this but gave up.  Someone smarter than me might be able to tell me what I am doing wrong.
  2. This code does not consider command properties.  Every command assumes a null parameter.  If you need parameters (like data context), then you’ll have to do something differently (either use the old-school mechanism or extend this code to handle some special event types).
  3. You don’t bind directly to the command.  Instead, you declare the name of the command (Notice CommandName is not bound).  The behavior binds for you using a primitive mechanism.

下面给出命令的行为,它可以完成所有的工作:

  1. public class Events  
  2. {  
  3.     private static readonly DependencyProperty EventBehaviorsProperty =  
  4.         DependencyProperty.RegisterAttached(  
  5.         "EventBehaviors",  
  6.         typeof(EventBehaviorCollection),  
  7.         typeof(Control),  
  8.         null);  
  9.   
  10.     private static readonly DependencyProperty InternalDataContextProperty =  
  11.         DependencyProperty.RegisterAttached(  
  12.         "InternalDataContext",  
  13.         typeof(Object),  
  14.         typeof(Control),  
  15.         new PropertyMetadata(null, DataContextChanged));  
  16.   
  17.     private static void DataContextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)  
  18.     {  
  19.         var target = dependencyObject as Control;  
  20.         if (target == nullreturn;  
  21.   
  22.         foreach (var behavior in GetOrCreateBehavior(target))  
  23.             behavior.Bind();  
  24.     }  
  25.   
  26.     public static readonly DependencyProperty CommandsProperty =  
  27.         DependencyProperty.RegisterAttached(  
  28.         "Commands",  
  29.         typeof(EventCommandCollection),  
  30.         typeof(Events),  
  31.         new PropertyMetadata(null, CommandsChanged));  
  32.   
  33.     public static EventCommandCollection GetCommands(DependencyObject dependencyObject)  
  34.     {  
  35.         return dependencyObject.GetValue(CommandsProperty) as EventCommandCollection;  
  36.     }  
  37.   
  38.     public static void SetCommands(DependencyObject dependencyObject, EventCommandCollection eventCommands)  
  39.     {  
  40.         dependencyObject.SetValue(CommandsProperty, eventCommands);  
  41.     }  
  42.   
  43.     private static void CommandsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)  
  44.     {  
  45.         var target = dependencyObject as Control;  
  46.         if (target == nullreturn;  
  47.   
  48.         var behaviors = GetOrCreateBehavior(target);  
  49.         foreach (var eventCommand in e.NewValue as EventCommandCollection)  
  50.         {  
  51.             var behavior = new EventBehavior(target);  
  52.             behavior.Bind(eventCommand);  
  53.             behaviors.Add(behavior);  
  54.         }  
  55.   
  56.     }  
  57.   
  58.     private static EventBehaviorCollection GetOrCreateBehavior(FrameworkElement target)  
  59.     {  
  60.         var behavior = target.GetValue(EventBehaviorsProperty) as EventBehaviorCollection;  
  61.         if (behavior == null)  
  62.         {  
  63.             behavior = new EventBehaviorCollection();  
  64.             target.SetValue(EventBehaviorsProperty, behavior);  
  65.             target.SetBinding(InternalDataContextProperty, new Binding());  
  66.         }  
  67.   
  68.         return behavior;  
  69.     }  
  70. }  
  71.   
  72. public class EventCommand  
  73. {  
  74.     public string CommandName { getset; }  
  75.     public string EventName { getset; }  
  76. }  
  77.   
  78. public class EventCommandCollection : List<EventCommand>  
  79. {  
  80. }  
  81.   
  82. public class EventBehavior : CommandBehaviorBase<Control>  
  83. {  
  84.     private EventCommand _bindingInfo;  
  85.   
  86.     public EventBehavior(Control control)  
  87.         : base(control)  
  88.     {  
  89.   
  90.     }  
  91.   
  92.     public void Bind(EventCommand bindingInfo)  
  93.     {  
  94.         ValidateBindingInfo(bindingInfo);  
  95.   
  96.         _bindingInfo = bindingInfo;  
  97.   
  98.         Bind();  
  99.     }  
  100.   
  101.     private void ValidateBindingInfo(EventCommand bindingInfo)  
  102.     {  
  103.         if(bindingInfo == nullthrow new ArgumentException("bindingInfo");  
  104.         if (string.IsNullOrEmpty(bindingInfo.CommandName)) throw new ArgumentException("bindingInfo.CommandName");  
  105.         if (string.IsNullOrEmpty(bindingInfo.EventName)) throw new ArgumentException("bindingInfo.EventName");  
  106.     }  
  107.   
  108.     public void Bind()  
  109.     {  
  110.         ValidateBindingInfo(_bindingInfo);  
  111.         HookPropertyChanged();  
  112.         HookEvent();  
  113.         SetCommand();  
  114.     }  
  115.   
  116.     public void HookPropertyChanged()  
  117.     {  
  118.         var dataContext = TargetObject.DataContext as INotifyPropertyChanged;  
  119.         if (dataContext == nullreturn;  
  120.   
  121.         dataContext.PropertyChanged -= DataContextPropertyChanged;  
  122.         dataContext.PropertyChanged += DataContextPropertyChanged;  
  123.     }  
  124.   
  125.     private void DataContextPropertyChanged(object sender, PropertyChangedEventArgs e)  
  126.     {  
  127.         if (e.PropertyName == _bindingInfo.CommandName)  
  128.             SetCommand();  
  129.     }  
  130.   
  131.     private void SetCommand()  
  132.     {  
  133.         var dataContext = TargetObject.DataContext;  
  134.         if (dataContext == nullreturn;  
  135.   
  136.         var propertyInfo = dataContext.GetType().GetProperty(_bindingInfo.CommandName);  
  137.         if (propertyInfo == nullthrow new ArgumentException("commandName");  
  138.   
  139.         Command = propertyInfo.GetValue(dataContext, nullas ICommand;  
  140.     }  
  141.   
  142.     private void HookEvent()  
  143.     {  
  144.         var eventInfo = TargetObject.GetType().GetEvent(  
  145.             _bindingInfo.EventName, BindingFlags.Public | BindingFlags.Instance);  
  146.         if (eventInfo == nullthrow new ArgumentException("eventName");  
  147.   
  148.         eventInfo.RemoveEventHandler(TargetObject, GetEventMethod(eventInfo));  
  149.         eventInfo.AddEventHandler(TargetObject, GetEventMethod(eventInfo));  
  150.     }  
  151.   
  152.     private Delegate _method;  
  153.     private Delegate GetEventMethod(EventInfo eventInfo)  
  154.     {  
  155.         if (eventInfo == nullthrow new ArgumentNullException("eventInfo");  
  156.         if (eventInfo.EventHandlerType == nullthrow new ArgumentException("EventHandlerType is null");  
  157.   
  158.         if (_method == null)  
  159.         {  
  160.             _method = Delegate.CreateDelegate(  
  161.                 eventInfo.EventHandlerType, this,  
  162.                 GetType().GetMethod("OnEventRaised",  
  163.                 BindingFlags.NonPublic | BindingFlags.Instance));  
  164.         }  
  165.   
  166.         return _method;  
  167.     }  
  168.   
  169.     private void OnEventRaised(object sender, EventArgs e)  
  170.     {  
  171.         ExecuteCommand();  
  172.     }  
  173. }  
  174.   
  175. public class EventBehaviorCollection : List<EventBehavior>  
  176. { }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章