Хороший подход для этого - использовать прикрепленное поведение . Само поведение может перехватить нужное вам событие и запустить соответствующую команду с параметрами, которые вы хотите. По сути, это тот же код, но он просто удаляет его из кода и позволяет вам выражать свои намерения исключительно в XAML.
Есть несколько примеров поведения на CodePlex , или вот базовый пример, который выполняет команду:
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
/// <summary>
/// Trigger action to execute an ICommand command
/// </summary>
public class ExecuteCommand : TriggerAction<FrameworkElement>
{
#region Dependency Properties
/// <summary>
/// Command parameter
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(ExecuteCommand), new UIPropertyMetadata(null, OnCommandParameterChanged));
/// <summary>
/// Command to be executed
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(ExecuteCommand), new UIPropertyMetadata(null, OnCommandChanged));
#region Public Properties
/// <summary>
/// Gets or sets the command
/// </summary>
public ICommand Command
{
get
{
return (ICommand)this.GetValue(CommandProperty);
}
set
{
this.SetValue(CommandProperty, value);
}
}
/// <summary>
/// Gets or sets the command parameter
/// </summary>
public object CommandParameter
{
get
{
return (object)this.GetValue(CommandParameterProperty);
}
set
{
this.SetValue(CommandParameterProperty, value);
}
}
#endregion
/// <summary>
/// Executes the command if it is not null and is able to execute
/// </summary>
/// <param name="parameter">This argument not used</param>
protected override void Invoke(object parameter)
{
if (this.Command != null && this.Command.CanExecute(this.CommandParameter))
{
this.Command.Execute(this.CommandParameter);
}
}
/// <summary>
/// Called on command change
/// </summary>
/// <param name="oldValue">old ICommand instance</param>
/// <param name="newValue">new ICommand instance</param>
protected virtual void OnCommandChanged(ICommand oldValue, ICommand newValue)
{
}
/// <summary>
/// Called on command parameter change
/// </summary>
/// <param name="oldValue">old ICommand instance</param>
/// <param name="newValue">new ICommand instance</param>
protected virtual void OnCommandParameterChanged(object oldValue, object newValue)
{
}
/// <summary>
/// Called on command parameter change
/// </summary>
/// <param name="o">Dependency object</param>
/// <param name="e">Dependency property</param>
private static void OnCommandParameterChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
ExecuteCommand invokeCommand = o as ExecuteCommand;
if (invokeCommand != null)
{
invokeCommand.OnCommandParameterChanged((object)e.OldValue, (object)e.NewValue);
}
}
/// <summary>
/// Called on command change
/// </summary>
/// <param name="o">Dependency object</param>
/// <param name="e">Dependency property</param>
private static void OnCommandChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
ExecuteCommand invokeCommand = o as ExecuteCommand;
if (invokeCommand != null)
{
invokeCommand.OnCommandChanged((ICommand)e.OldValue, (ICommand)e.NewValue);
}
}
#endregion
}
}
Затем можно использовать пространство имен System.Windows.Interactivity (сборка включена в Blend 3), чтобы перехватить событие и запустить команду следующим образом:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<triggers:ExecuteCommand Command="{Binding MyCommand}" CommandParameter="MyParameter" />
</i:EventTrigger>
</i:Interaction.Triggers>
Для более сложных событий с несколькими параметрами вам может потребоваться создать определенное поведение, а не использовать общее поведение, как в примере выше. Обычно я предпочитаю создавать свой собственный тип для хранения параметров и сопоставления с ним, а не с определенным требованием EventArgs в моей ViewModel.
* Одна вещь, которую я должен добавить, это то, что я определенно не парень типа «0 кода в коде позади», и я думаю, что принудительное принудительное соблюдение этого принципа несколько упускает смысл MVVM. Пока код содержит без логики и, следовательно, ничего, что действительно не нужно тестировать, я могу жить с некоторыми небольшими фрагментами кода для «преодоления разрыва» между View и ViewModel. Это также иногда необходимо, если у вас есть «умное представление», как я его обычно называю, такое как элемент управления браузером или что-то, с чем вам нужно общаться из вашей ViewModel. Некоторые люди будут получать вилы, чтобы даже предложить такую вещь, поэтому я оставил этот бит до последнего и ответил на ваш вопрос первым: -) *