MVVM Passing EventArgs As Command Parameter
I've always come back here for the answer so I wanted to make a short simple one to go to.
There are multiple ways of doing this:
1. Using WPF Tools. Easiest.
Add Namespaces:
System.Windows.Interactivitiy
Microsoft.Expression.Interactions
XAML:
Use the EventName
to call the event you want then specify your Method
name in the MethodName
.
<Window>
xmlns:wi="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions">
<wi:Interaction.Triggers>
<wi:EventTrigger EventName="SelectionChanged">
<ei:CallMethodAction
TargetObject="{Binding}"
MethodName="ShowCustomer"/>
</wi:EventTrigger>
</wi:Interaction.Triggers>
</Window>
Code:
public void ShowCustomer()
{
// Do something.
}
2. Using MVVMLight. Most difficult.
Install GalaSoft NuGet package.
Get the namespaces:
System.Windows.Interactivity
GalaSoft.MvvmLight.Platform
XAML:
Use the EventName
to call the event you want then specify your Command
name in your binding. If you want to pass the arguments of the method, mark PassEventArgsToCommand
to true.
<Window>
xmlns:wi="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight">
<wi:Interaction.Triggers>
<wi:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding CommandNameHere}"
PassEventArgsToCommand="True" />
</wi:EventTrigger>
</wi:Interaction.Triggers>
</Window>
Code Implementing Delegates: Source
You must get the Prism MVVM NuGet package for this.
using Microsoft.Practices.Prism.Commands;
// With params.
public DelegateCommand<string> CommandOne { get; set; }
// Without params.
public DelegateCommand CommandTwo { get; set; }
public MainWindow()
{
InitializeComponent();
// Must initialize the DelegateCommands here.
CommandOne = new DelegateCommand<string>(executeCommandOne);
CommandTwo = new DelegateCommand(executeCommandTwo);
}
private void executeCommandOne(string param)
{
// Do something here.
}
private void executeCommandTwo()
{
// Do something here.
}
Code Without DelegateCommand
: Source
using GalaSoft.MvvmLight.CommandWpf
public MainWindow()
{
InitializeComponent();
CommandOne = new RelayCommand<string>(executeCommandOne);
CommandTwo = new RelayCommand(executeCommandTwo);
}
public RelayCommand<string> CommandOne { get; set; }
public RelayCommand CommandTwo { get; set; }
private void executeCommandOne(string param)
{
// Do something here.
}
private void executeCommandTwo()
{
// Do something here.
}
3. Using Telerik EventToCommandBehavior. It's an option.
You'll have to download it's NuGet Package.
XAML
:
<i:Interaction.Behaviors>
<telerek:EventToCommandBehavior
Command="{Binding DropCommand}"
Event="Drop"
PassArguments="True" />
</i:Interaction.Behaviors>
Code:
public ActionCommand<DragEventArgs> DropCommand { get; private set; }
this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);
private void OnDrop(DragEventArgs e)
{
// Do Something
}
It's not easily supported. Here's an article with instructions on how to pass EventArgs as command parameters.
You might want to look into using MVVMLight - it supports EventArgs in command directly; your situation would look something like this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Navigated">
<cmd:EventToCommand Command="{Binding NavigatedEvent}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
I try to keep my dependencies to a minimum, so I implemented this myself instead of going with EventToCommand of MVVMLight. Works for me so far, but feedback is welcome.
Xaml:
<i:Interaction.Behaviors>
<beh:EventToCommandBehavior Command="{Binding DropCommand}" Event="Drop" PassArguments="True" />
</i:Interaction.Behaviors>
ViewModel:
public ActionCommand<DragEventArgs> DropCommand { get; private set; }
this.DropCommand = new ActionCommand<DragEventArgs>(OnDrop);
private void OnDrop(DragEventArgs e)
{
// ...
}
EventToCommandBehavior:
/// <summary>
/// Behavior that will connect an UI event to a viewmodel Command,
/// allowing the event arguments to be passed as the CommandParameter.
/// </summary>
public class EventToCommandBehavior : Behavior<FrameworkElement>
{
private Delegate _handler;
private EventInfo _oldEvent;
// Event
public string Event { get { return (string)GetValue(EventProperty); } set { SetValue(EventProperty, value); } }
public static readonly DependencyProperty EventProperty = DependencyProperty.Register("Event", typeof(string), typeof(EventToCommandBehavior), new PropertyMetadata(null, OnEventChanged));
// Command
public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } }
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(EventToCommandBehavior), new PropertyMetadata(null));
// PassArguments (default: false)
public bool PassArguments { get { return (bool)GetValue(PassArgumentsProperty); } set { SetValue(PassArgumentsProperty, value); } }
public static readonly DependencyProperty PassArgumentsProperty = DependencyProperty.Register("PassArguments", typeof(bool), typeof(EventToCommandBehavior), new PropertyMetadata(false));
private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var beh = (EventToCommandBehavior)d;
if (beh.AssociatedObject != null) // is not yet attached at initial load
beh.AttachHandler((string)e.NewValue);
}
protected override void OnAttached()
{
AttachHandler(this.Event); // initial set
}
/// <summary>
/// Attaches the handler to the event
/// </summary>
private void AttachHandler(string eventName)
{
// detach old event
if (_oldEvent != null)
_oldEvent.RemoveEventHandler(this.AssociatedObject, _handler);
// attach new event
if (!string.IsNullOrEmpty(eventName))
{
EventInfo ei = this.AssociatedObject.GetType().GetEvent(eventName);
if (ei != null)
{
MethodInfo mi = this.GetType().GetMethod("ExecuteCommand", BindingFlags.Instance | BindingFlags.NonPublic);
_handler = Delegate.CreateDelegate(ei.EventHandlerType, this, mi);
ei.AddEventHandler(this.AssociatedObject, _handler);
_oldEvent = ei; // store to detach in case the Event property changes
}
else
throw new ArgumentException(string.Format("The event '{0}' was not found on type '{1}'", eventName, this.AssociatedObject.GetType().Name));
}
}
/// <summary>
/// Executes the Command
/// </summary>
private void ExecuteCommand(object sender, EventArgs e)
{
object parameter = this.PassArguments ? e : null;
if (this.Command != null)
{
if (this.Command.CanExecute(parameter))
this.Command.Execute(parameter);
}
}
}
ActionCommand:
public class ActionCommand<T> : ICommand
{
public event EventHandler CanExecuteChanged;
private Action<T> _action;
public ActionCommand(Action<T> action)
{
_action = action;
}
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter)
{
if (_action != null)
{
var castParameter = (T)Convert.ChangeType(parameter, typeof(T));
_action(castParameter);
}
}
}