在上一节中我介绍了“MVVM中轻松实现Command绑定(四)获取事件参数EventArgs”,通过Loaded事件传递控件对象,然后添加事件方法,这样做是可以的,但是不符合MVVM的思想,今日我介绍另一种方法,通过扩展interactivity的InvokeCommandAction来实现事件参数传递。
先来看普通的InvokeCommandAction方式
<Window x:Class="EventArgsInViewModel.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:loc="clr-namespace:EventArgsInViewModel" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <loc:MainWindowViewModel /> </Window.DataContext> <Grid> <Button Content="Button" Height="38" HorizontalAlignment="Left" Margin="50,52,0,0" Name="button1" VerticalAlignment="Top" Width="138"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <i:InvokeCommandAction Command="{Binding ClickCommand}" CommandParameter="{Binding ElementName=button1}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> </Grid> </Window>
现在为了扩展CommandParameter,定义ExCommandParameter类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; namespace EventArgsInViewModel { /// <summary> /// 扩展CommandParameter,使CommandParameter可以带事件参数 /// </summary> public class ExCommandParameter { /// <summary> /// 事件触发源 /// </summary> public DependencyObject Sender { get; set; } /// <summary> /// 事件参数 /// </summary> public EventArgs EventArgs { get; set; } /// <summary> /// 额外参数 /// </summary> public object Parameter { get; set; } } }
然后定义ExInvokeCommandAction类,用于扩展InvokeCommandAction
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Interactivity; using System.Windows.Input; using System.Reflection; namespace EventArgsInViewModel { /// <summary> /// 扩展的InvokeCommandAction /// </summary> 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; } } }
好了,我们把xaml改一下,现在改用我们自己创建的ExInvokeCommandAction
<Window x:Class="EventArgsInViewModel.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:loc="clr-namespace:EventArgsInViewModel" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <loc:MainWindowViewModel /> </Window.DataContext> <Grid> <Button Content="Button" Height="38" HorizontalAlignment="Left" Margin="50,52,0,0" Name="button1" VerticalAlignment="Top" Width="138"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <!--★★★扩展的InvokeCommandAction★★★--> <loc:ExInvokeCommandAction Command="{Binding ClickCommand}" CommandParameter="{Binding ElementName=button1}" /> </i:EventTrigger> </i:Interaction.Triggers> </Button> </Grid> </Window>
ViewModel代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Input; using Microsoft.Practices.Prism.Commands; using System.Windows; namespace EventArgsInViewModel { public class MainWindowViewModel { public ICommand ClickCommand { get { return new DelegateCommand<ExCommandParameter>((p) => { RoutedEventArgs args = p.EventArgs as RoutedEventArgs; MessageBox.Show(args.ToString()); }, (p) => { return true; } ); } } } }
现在点击一下按钮,显示了对应的消息框,OK,参数也能得到。是不是也很容易啊。
那么有人问,除了ExInvokeCommandAction我也会,但是ExInvokeCommandAction让人想破脑袋也不会写。其实,我也不会写,我只是用ILSpy反编译了一下,然后稍稍改动而已(这个我还是可以做到的)。
最后我想说:“其实你只要动脑筋都做到的事很多!”
下载地址:http://qing2005.download.csdn.net/