MVVM里Command绑定中获取事件参数EventArgs

Posted by baicai on September 3, 2014

通过扩展interactivity的InvokeCommandAction来实现事件参数传递。</span> </p>

先来看普通的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类</span> </p>

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>此操作应调用的命令的名称。&lt;/value>
		/// <remarks>如果设置了此属性和 Command 属性,则此属性将被后者所取代。&lt;/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>要执行的命令。&lt;/value>
		/// <remarks>如果设置了此属性和 CommandName 属性,则此属性将优先于后者。&lt;/remarks>
		public ICommand Command {
			get {
				return (ICommand)base.GetValue(ExInvokeCommandAction.CommandProperty);
			}
			set {
				base.SetValue(ExInvokeCommandAction.CommandProperty, value);
			}
		}
		/// <summary>
		/// 获得或设置命令参数。这是依赖属性。
		/// </summary>
		/// <value>命令参数。&lt;/value>
		/// <remarks>这是传递给 ICommand.CanExecute 和 ICommand.Execute 的值。&lt;/remarks>
		public object CommandParameter {
			get {
				return base.GetValue(ExInvokeCommandAction.CommandParameterProperty);
			}
			set {
				base.SetValue(ExInvokeCommandAction.CommandParameterProperty, value);
			}
		}
		/// <summary>
		/// 调用操作。
		/// </summary>
		/// <param name="parameter">操作的参数。如果操作不需要参数,则可以将参数设置为空引用。&lt;/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,参数也能得到。</span> </p>

转自@林老师的专栏</span>林老师的专栏</a>林老师的专栏</a> </p>