在MVVM架构下,把EventArgs绑定到Command上【翻译】

  在使用MVVM架构时,我们会遇到各种各样的问题

  其中一个很常见的问题就是如何在ViewModel层处理UI事件时在后台代码文件中不写任何代码。

  在我这个例子中实现的是取得鼠标移动时的位置。

  我的解决方法如下:

  1、通过一个Behavior 取得关联对象的EventArgs,代码如下

 1 public class ExtendedInvokeCommandAction : TriggerAction<FrameworkElement>
 2     {
 3         public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command"typeof(ICommand), typeof(ExtendedInvokeCommandAction), new PropertyMetadata(null, CommandChangedCallback));
 4         public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter"typeof(object), typeof(ExtendedInvokeCommandAction), new PropertyMetadata(null, CommandParameterChangedCallback));
 5 
 6         private static void CommandParameterChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
 7         {
 8             var invokeCommand = d as ExtendedInvokeCommandAction;
 9             if (invokeCommand != null)
10                 invokeCommand.SetValue(CommandParameterProperty, e.NewValue);
11         }
12 
13         private static void CommandChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
14         {
15             var invokeCommand = d as ExtendedInvokeCommandAction;
16             if (invokeCommand != null)
17                 invokeCommand.SetValue(CommandProperty, e.NewValue);
18         }
19 
20         protected override void Invoke(object parameter)
21         {
22             if (this.Command == null)
23                 return;
24 
25             if (this.Command.CanExecute(parameter))
26             {
27                 var commandParameter = new ExtendedCommandParameter(parameter as EventArgs, this.AssociatedObject,
28                                                                     GetValue(CommandParameterProperty));
29                 this.Command.Execute(commandParameter);
30             }
31         }
32 
33         #region public properties
34 
35         public object CommandParameter
36         {
37             get { return GetValue(CommandParameterProperty); }
38             set { SetValue(CommandParameterProperty, value); }
39         }
40 
41         public ICommand Command
42         {
43             get { return GetValue(CommandProperty) as ICommand; }
44             set { SetValue(CommandParameterProperty, value); }
45         }
46 
47         #endregion
48     }

 

  2、写一个类,包含的属性有事件源、EventArgs和对象,代码如下

 1 public class ExtendedCommandParameter
 2     {
 3         public ExtendedCommandParameter(EventArgs eventArgs, FrameworkElement sender, object parameter)
 4         {
 5             EventArgs = eventArgs;
 6             Sender = sender;
 7             Parameter = parameter;
 8         }
 9 
10         public EventArgs EventArgs { getprivate set; }
11         public FrameworkElement Sender { getprivate set; }
12         public object Parameter { getprivate set; }
13     }

 

  3、为对象添加Behavior

  在我的这个例子中,我对Rectangle添加新建的类ExtendedInvokeCommandAction(即Behavior)

  4、在ViewModel层把这个Behavior绑定到Command上,在这Command上选择一个事件,通过Comman调用这个事件

  在我这个例子中,在ViewModle层我把MouseMove事件绑定到MouseMoved Command上,另外,如果需要的话,可以把任何对象绑定到CommandParameter,在我这个例子中,我把此View(变量名为userControl)绑定到了CommandParameter(不是必须的)

  

 1 
 2 <Rectangle Fill="#FF9B9BC5" Height="200" Stroke="Black" Width="200">
 3             <i:Interaction.Triggers>
 4                 <i:EventTrigger EventName="MouseMove">
 5                     <local:ExtendedInvokeCommandAction
 6                     Command="{Binding MouseMoved}"
 7                     CommandParameter="{Binding ElementName=userControl}"/>
 8                 </i:EventTrigger>
 9             </i:Interaction.Triggers>
10         </Rectangle>
11 

 

  5、在ViewModel层处理事件,代码如下

 

 1 public class MainPageViewModel : INotifyPropertyChanged
 2     {
 3         public MainPageViewModel()
 4         {
 5             MouseMoved = new MainPageCommand(this); //initialize the Command
 6         }
 7 
 8         //gets the position of the mouse
 9         private void OnMouseMove(ExtendedCommandParameter commandParameter)
10         {
11             MouseEventArgs eventArgs;
12 
13             //cast the EventArgs to the type you expect, according to the event you handle
14             //f.e. MouseMove Event  gets you MouseEventArgs
15             //     Click Event gets you RoutedEventArgs
16             if (commandParameter.EventArgs.GetType() == typeof(MouseEventArgs))
17             {
18                 eventArgs = commandParameter.EventArgs as MouseEventArgs;
19                 if (commandParameter.Parameter != null)
20                 {
21                     var view = commandParameter.Parameter as UIElement;
22                     MousePosition = eventArgs.GetPosition(view).ToString();
23                 }
24             }
25         }
26 
27         public MainPageCommand MouseMoved { getset; }
28         private string _mousePosition;
29         public string MousePosition
30         {
31             get { return _mousePosition; }
32             set { _mousePosition = value; OnPropertyChanged("MousePosition"); }
33         }
34 
35         #region INotifyChanged Members
36 
37         public event PropertyChangedEventHandler PropertyChanged;
38         internal void OnPropertyChanged(string propertyName)
39         {
40             if (this.PropertyChanged != null)
41             {
42                 this.PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
43             }
44         }
45 
46         #endregion
47 
48         #region command class
49 
50         public class MainPageCommand : ICommand
51         {
52             public MainPageCommand(MainPageViewModel view)
53             {
54                 _view = view;
55             }
56 
57             private MainPageViewModel _view;
58 
59             #region ICommand Members
60 
61             public bool CanExecute(object parameter)
62             {
63                 return true;
64             }
65 
66             public event EventHandler CanExecuteChanged;
67 
68             public void Execute(object parameter)
69             {
70                 //call the method to handle the event
71                 _view.OnMouseMove(parameter as ExtendedCommandParameter);
72             }
73 
74             #endregion
75 
76         }
77     #endregion
78 
79     }

  如果需要,可以为每一个EventArgs建一个Behavior,只要把它绑定到Command上,然后Command代码中处理EventArgs就可以了

  代码下载

  另外还有一篇英文的文章,和这篇文章差不多,但是写的更好,而且程序写的也很好,如果有兴趣的话,也看一下。文章地址为http://blog.roboblob.com/2010/01/26/binding-ui-events-from-view-to-commands-in-viewmodel-in-silverlight-4/

posted @ 2010-12-18 20:29  天神一  阅读(4061)  评论(3编辑  收藏  举报