背景
在我们进行 WPF 开发应用程序的时候不可避免的要使用到事件, 很多时候没有严格按照 MVVM 模式进行开发的时候习惯直接在 xaml 中定义事件, 然后再在对应的. cs 文件中直接写事件的处理过程, 这种处理方式写起来非常简单而且不用过多地处理考虑代码之间是否符合规范, 但是我们在写代码的时候如果完全按照 WPF 规范的 MVVM 模式进行开发的时候就应该将相应的事件处理写在 ViewModel 层, 这样整个代码才更加符合规范而且层次也更加清楚, 更加符合 MVVM 规范.
常规用法
1 引入命名空间
通过在代码中引入 System.Windows.Interactivity.dll, 引入了这个 dll 后我们就能够使用这个里面的方法来将事件映射到 ViewModel 层了, 我们来看看具体的使用步骤, 第一步就是引入命名控件
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
另外还可以通过另外一种方式来引入命名空间, 其实这两者间都是对等的.
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
2 添加事件对应的 Command
这里以 TextBox 的 GetFocus 和 LostFocus 为例来进行说明
- <TextBox Text="CommandBinding">
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="LostFocus">
- <i:InvokeCommandAction Command="{Binding OnTextLostFocus}"
- CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/>
- </i:EventTrigger>
- <i:EventTrigger EventName="GotFocus">
- <i:InvokeCommandAction Command="{Binding OnTextGotFocus}"
- CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorLevel=1, AncestorType={x:Type TextBox}}}"/>
- </i:EventTrigger>
- </i:Interaction.Triggers>
- </TextBox>
这个里面我们重点来看看这个 InvokeCommandAction 的代码结构
- namespace System.Windows.Interactivity
- {
- public sealed class InvokeCommandAction : TriggerAction<DependencyObject>
- {
- public static readonly DependencyProperty CommandProperty;
- public static readonly DependencyProperty CommandParameterProperty;
- public InvokeCommandAction();
- public string CommandName { get; set; }
- public ICommand Command { get; set; }
- public object CommandParameter { get; set; }
- protected override void Invoke(object parameter);
- }
- }
这里我们发现这里我们如果我们定义一个 Command 的话我们只能够在 Command 中获取到我们绑定的 CommandParameter 这个参数, 但是有时候我们需要获取到触发这个事件的 RoutedEventArgs 的时候, 通过这种方式就很难获取到了, 这个时候我们就需要自己去扩展一个 InvokeCommandAction 了, 这个时候我们应该怎么做呢? 整个过程分成三步:
2.1 定义自己的 CommandParameter
- public class ExCommandParameter
- {
- /// <summary>
- /// 事件触发源
- /// </summary>
- public DependencyObject Sender { get; set; }
- /// <summary>
- /// 事件参数
- /// </summary>
- public EventArgs EventArgs { get; set; }
- /// <summary>
- /// 额外参数
- /// </summary>
- public object Parameter { get; set; }
- }
这个对象除了封装我们常规的参数外还封装了我们需要的 EventArgs 属性, 有了这个我们就能将当前的事件的 EventArgs 传递进来了.
2.2 重写自己的 InvokeCommandAction
- public class ExInvokeCommandAction : TriggerAction<DependencyObject>
- {
- private string commandName;
- public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExInvokeCommandAction), null);
- public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExInvokeCommandAction), null);
- /// <summary>
- /// 获得或设置此操作应调用的命令的名称.
- /// </summary>
- /// <value > 此操作应调用的命令的名称.</value>
- /// <remarks > 如果设置了此属性和 Command 属性, 则此属性将被后者所取代.</remarks>
- public string CommandName
- {
- get
- {
- base.ReadPreamble();
- return this.commandName;
- }
- set
- {
- if (this.CommandName != value)
- {
- base.WritePreamble();
- this.commandName = value;
- base.WritePostscript();
- }
- }
- }
- /// <summary>
- /// 获取或设置此操作应调用的命令. 这是依赖属性.
- /// </summary>
- /// <value > 要执行的命令.</value>
- /// <remarks > 如果设置了此属性和 CommandName 属性, 则此属性将优先于后者.</remarks>
- public ICommand Command
- {
- get
- {
- return (ICommand)base.GetValue(ExInvokeCommandAction.CommandProperty);
- }
- set
- {
- base.SetValue(ExInvokeCommandAction.CommandProperty, value);
- }
- }
- /// <summary>
- /// 获得或设置命令参数. 这是依赖属性.
- /// </summary>
- /// <value > 命令参数.</value>
- /// <remarks > 这是传递给 ICommand.CanExecute 和 ICommand.Execute 的值.</remarks>
- public object CommandParameter
- {
- get
- {
- return base.GetValue(ExInvokeCommandAction.CommandParameterProperty);
- }
- set
- {
- base.SetValue(ExInvokeCommandAction.CommandParameterProperty, value);
- }
- }
- /// <summary>
- /// 调用操作.
- /// </summary>
- /// <param name="parameter"> 操作的参数. 如果操作不需要参数, 则可以将参数设置为空引用.</param>
- protected override void Invoke(object parameter)
- {
- if (base.AssociatedObject != null)
- {
- ICommand command = this.ResolveCommand();
- /*
- * ★★★★★★★★★★★★★★★★★★★★★★★★
- * 注意这里添加了事件触发源和事件参数
- * ★★★★★★★★★★★★★★★★★★★★★★★★
- */
- ExCommandParameter exParameter = new ExCommandParameter
- {
- Sender = base.AssociatedObject,
- Parameter = GetValue(CommandParameterProperty),
- EventArgs = parameter as EventArgs
- };
- if (command != null && command.CanExecute(exParameter))
- {
- /*
- * ★★★★★★★★★★★★★★★★★★★★★★★★
- * 注意将扩展的参数传递到 Execute 方法中
- * ★★★★★★★★★★★★★★★★★★★★★★★★
- */
- command.Execute(exParameter);
- }
- }
- }
- private ICommand ResolveCommand()
- {
- ICommand result = null;
- if (this.Command != null)
- {
- result = this.Command;
- }
- else
- {
- if (base.AssociatedObject != null)
- {
- Type type = base.AssociatedObject.GetType();
- PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
- PropertyInfo[] array = properties;
- for (int i = 0; i <array.Length; i++)
- {
- PropertyInfo propertyInfo = array[i];
- if (typeof(ICommand).IsAssignableFrom(propertyInfo.PropertyType) && string.Equals(propertyInfo.Name, this.CommandName, StringComparison.Ordinal))
- {
- result = (ICommand)propertyInfo.GetValue(base.AssociatedObject, null);
- }
- }
- }
- }
- return result;
- }
- }
这个里面的重点是要重写基类中的 Invoke 方法, 将当前命令通过反射的方式来获取到, 然后在执行 command.Execute 方法的时候将我们自定义的 ExCommandParameter 传递进去, 这样我们就能够在最终绑定的命令中获取到特定的 EventArgs 对象了.
2.3 在代码中应用自定义 InvokeCommandAction
- <ListBox x:Name="lb_selecthistorymembers"
- SnapsToDevicePixels="true"
- ItemsSource="{Binding DataContext.SpecificHistoryMembers,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
- HorizontalAlignment="Stretch"
- ScrollViewer.HorizontalScrollBarVisibility="Disabled"
- Background="#fff"
- BorderThickness="1">
- <i:Interaction.Triggers>
- <i:EventTrigger EventName="SelectionChanged">
- <interactive:ExInvokeCommandAction Command="{Binding DataContext.OnSelectHistoryMembersListBoxSelected,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=my:AnnouncementApp},Mode=TwoWay}"
- CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}">
- </interactive:ExInvokeCommandAction>
- </i:EventTrigger>
- </i:Interaction.Triggers>
- </ListBox>
注意这里需要首先引入自定义的 interactive 的命名空间, 这个在使用的时候需要注意, 另外在最终的 Command 订阅中 EventArgs 根据不同的事件有不同的表现形式, 比如 Loaded 事件, 那么最终获取到的 EventArgs 就是 RoutedEventArgs 对象, 如果是 TableControl 的 SelectionChanged 事件, 那么最终获取到的就是 SelectionChangedEventArgs 对象, 这个在使用的时候需要加以区分.
来源: http://www.bubuko.com/infodetail-3717174.html