在使用MVVM模式时, 按照模式的规则是尽量不直接使用事件. 所以对于以前一直使用事件模式的同行来说确实有点头疼. 还好微软给我们提供了几种间接使用事件(命令)的方法, 下面我就来看看这几种方法:
ICommand
ICommand定义了一个接口, 使用它可以轻松的将实现ICommand的接口的命令绑定到包含命令(Command)的控件上(例如Button.Command), 在.net framework库中实现的这个接口的类不是很多, 我所知道的两个: RoutedCommand 和 RoutedUICommand, 而且这两个使用起来比较麻烦, 在这里我就不谈了, 有兴趣的同行可以研究一下. 那么, 除了上述的两个类以外还有自定义类实现ICommand和Prism的DelegateCommand. 我们先看一下自定义的;
1. 自定义命令
MyCommand是实现ICommand接口的一个类. 在Execute方法中我们调用传入的Action(Action包含所要实现的功能的方法), IsEnabledExecute是自定义的一个bool类型, 通过设置该值可以启用(禁用)ICommand所绑定到的控件的启用禁用状态.
1 public class MyCommand : ICommand 2 3 { 4 5 public MyCommand(Action<object> action) 6 7 { 8 9 if (action == null) throw new ArgumentNullException(); 10 11 _action = action; 12 13 } 14 15 16 17 private readonly Action<object> _action; 18 19 20 21 private bool _isEnabledExecute = true; //默认为启用状态 22 23 public bool IsEnabledExecute 24 25 { 26 27 get { return _isEnabledExecute; } 28 29 set 30 31 { 32 33 if (_isEnabledExecute != value) 34 35 { 36 37 _isEnabledExecute = value; 38 39 if (CanExecuteChanged != null) 40 41 { 42 43 CanExecuteChanged(this, new EventArgs()); 44 45 } 46 47 } 48 49 } 50 51 } 52 53 54 55 #region ICommand 接口 56 57 public event EventHandler CanExecuteChanged; 58 59 60 61 public bool CanExecute(object parameter) 62 63 { 64 65 return _isEnabledExecute; 66 67 } 68 69 70 71 public void Execute(object parameter) 72 73 { 74 75 _action(parameter); 76 77 } 78 79 #endregion 80 81 }
定义好MyCommand后我们就可以使用了, 代码如下:
1 public class MvvmEventViewModel 2 { 3 4 public MvvmEventViewModel() 5 6 { 7 8 /*自定义命令*/ 9 10 MyCommandInstance = new MyCommand(MyCommandMethod); 11 12 } 13 14 15 16 /*自定义命令*/ 17 18 public MyCommand MyCommandInstance { get; private set; } 19 20 21 22 /*Prism命令*/ 23 24 public DelegateCommand<object> MyDelegateCommandInstance 25 { get; private set; } 26 27 public bool CanExecute(object parameter) 28 29 { 30 31 return MyDelegateCommandInstance.IsActive; 32 33 } 34 35 36 37 private void MyCommandMethod(object sender) 38 39 { 40 41 if (sender is string) 42 43 { 44 45 MessageBox.Show("Hello," + sender.ToString()); 46 47 } 48 49 } 50 51 }
<!--XAML-->
1 View Code 2 3 <Button Width="180" 4 5 Height="23" 6 7 Margin="0,0,7,0" 8 9 Command="{Binding MyCommandInstance}" 10 11 CommandParameter="MyCommand" 12 13 Content="MyCommand Button" /> 14 15 <ToggleButton Width="180" 16 17 Height="23" 18 19 Content="启用/禁用" 20 21 IsChecked="{Binding MyCommandInstance.IsEnabledExecute}" />
首先定义了MvvmEventViewModel, 在MvvmEventViewModel中实例化了MyCommand, 将Button所在的界面或者用户控件的属性DataContext绑定上MvvmEventViewModel的实例, 最后直接在Button的Command属性上绑定MyCommandInstance;
2.prism命令
prism是微软的一个开源框架, 其为WPF(SL)而生, 自然也少不了MVVM模式的一些辅助类, 其中命令就是典型的辅助类. 在上面我们是自定义命令, 其实在prism框架中已经提供了命令类, 那就是DelegateCommand, 该类还为泛型提供了支持; 不仅如此prism还提供了多个命令的绑定CompositeCommand, 为一个Button命令可以执行多个方法问题提供了廉价的解决方案.
2.1. DelegateCommand 命令
DelegateCommand 命令与我们上面自定义的命令一样, 都是可以绑定到Button的Command属性上, 使用该类少了几行代码, 多了一份省心和安全. 如果让我选择我会选择DelegateCommand类而摒弃自定义类, 因为它可以实现我们需要的功能, 所以就没必要再重造轮子.
1 View Code 2 3 public class MvvmEventViewModel 4 5 { 6 7 public MvvmEventViewModel() 8 9 { 10 11 /*Prism命令*/ 12 13 MyDelegateCommandInstance = new DelegateCommand<object>(MyCommandMethod, CanExecute); 14 15 MyDelegateCommandInstance.IsActive = true; 16 17 MyDelegateCommandInstance.IsActiveChanged += (s, e) => 18 19 { 20 21 MyDelegateCommandInstance.RaiseCanExecuteChanged(); 22 23 }; 24 25 } 26 27 28 29 /*Prism命令*/ 30 31 public DelegateCommand<object> MyDelegateCommandInstance { get; private set; } 32 33 public bool CanExecute(object parameter) 34 35 { 36 37 return MyDelegateCommandInstance.IsActive; 38 39 } 40 41 42 43 private void MyCommandMethod(object sender) 44 45 { 46 47 if (sender is string) 48 49 { 50 51 MessageBox.Show("Hello," + sender.ToString()); 52 53 } 54 55 } 56 57 }
<!--XAML-->
1 <Button Width="180" 2 3 Height="23" 4 5 Margin="0,0,7,0" 6 7 Command="{Binding MyCommandInstance}" 8 9 CommandParameter="MyCommand" 10 11 Content="MyCommand Button" /> 12 13 <ToggleButton Width="180" 14 15 Height="23" 16 17 Content="启用/禁用" 18 19 IsChecked="{Binding MyCommandInstance.IsEnabledExecute}" />
2.2.CompositeCommand 命令
CompositeCommand 命令可以理解为一个命令组. 将ICommand注册到CompositeCommand中, 然后绑定在Command上, 就可以让Command触发注册到CompositeCommand的所有命令.
2.2.1.定义一个CompositeCommand命令
1 public class Comm 2 3 { 4 5 #region CompositeCommand 6 7 private static CompositeCommand _compositeCommand = new CompositeCommand(); 8 9 public static CompositeCommand CompositeCommand 10 11 { 12 13 get { return _compositeCommand; } 14 15 } 16 17 #endregion 18 19 }
2.2.2.使用CompositeCommand命令
Comm.CompositeCommand.RegisterCommand(MyCommandInstance);
Comm.CompositeCommand.RegisterCommand(MyDelegateCommandInstance);
<!--XAML-->
xmlns:comm="clr-namespace:Blogs.WPF"
<Button Width="180" Height="23" Margin="0,0,7,0" HorizontalAlignment="Left" Command="{x:Static comm:Comm.CompositeCommand}" CommandParameter="DelegateCommand" Content="CompositeCommand Button" />
将我们刚才定义的两个命令MyCommandInstance和MyDelegateCommandInstance注册到Comm.CompositeCommand中, 然后在一个Button上绑定 comm:Comm.CompositeCommand 此时我们单击CompositeCommand Button时发现触发了两个命令. 同样的我们也可以使用UnregisterCommand卸载命令.
2.3.将其他事件绑定到ICommand上
在上述的例子中我们只是在Button的Command属性上绑定ICommand, 那么对于一些特殊事件(如Loaded, MouseDown, MouseUp)我们该怎么处理呢? 网上也有一些传统的方法, 将控件直接传送到VM中, 使用+=创建特殊的事件. 其实还有更好的办法, 那就是System.Windows.Interactivity.dll 组件.
在你的项目中引入System.Windows.Interactivity.dll, 然后在页面中添加如下代码: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
OK, 现在就让我们就来使用这两个组件, 代码简单, 请看:
public DelegateCommand LostFocusCommand { get; private set; } LostFocusCommand = new DelegateCommand(LostFocusMethod); private void LostFocusMethod() { MessageBox.Show("捕获到了.嘿牟嘿牟."); }
<TextBlock Background="OrangeRed" Text="左键按下时我能捕获到"> <i:Interaction.Triggers> <!-- 当单击鼠标左键按下时 --> <i:EventTrigger EventName="MouseLeftButtonDown"> <i:InvokeCommandAction Command="{Binding LostFocusCommand}" /> </i:EventTrigger> </i:Interaction.Triggers> </TextBlock>
注: 名字起的有点不对应. 不想改了, 大家知道就可以.
以上是使用System.Windows.Interactivity.dll组件可以将事件直接绑定到命令上, 但是我觉得这样麻烦, 如果可以直接使用事件岂不是更爽. 呵呵. 能提出问题就能解答问题. 我们再看另一个组件 Microsoft.Expression.Interactions.dll .
同样的, 项目中引用 Microsoft.Expression.Interactions.dll
添加命名空间
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" <UserControl> <i:Interaction.Triggers> <i:EventTrigger EventName="Loaded"> <ei:CallMethodAction MethodName="View_Loaded" TargetObject="{Binding}" /> </i:EventTrigger> </i:Interaction.Triggers> </UserControl>
注意: 在多层项目中. 只要你使用System.Windows.Interactivity.dll, Microsoft.Expression.Interactions.dll 这两个组件, 就必须在启动层中引用这两个DLL, 否则报错.