This post is about how control events, like MouseDoubleClick, MouseEnter ect., can be handled in a MVVM design
First a general Behaviour class
public static partial class Behaviours
{
public static class EventBehaviourFactory
{
public static DependencyProperty CreateCommandExecutionEventBehaviour(RoutedEvent routedEvent, string propertyName, Type ownerType)
{
DependencyProperty property = DependencyProperty.RegisterAttached(propertyName, typeof(ICommand), ownerType,
new PropertyMetadata(null,
new ExecuteCommandOnRoutedEventBehaviour(routedEvent).PropertyChangedHandler));
return property;
}
/// <summary>
/// An internal class to handle listening for an event and executing a command,
/// when a Command is assigned to a particular DependencyProperty
/// </summary>
private class ExecuteCommandOnRoutedEventBehaviour : ExecuteCommandBehaviour
{
private readonly RoutedEvent _routedEvent;
public ExecuteCommandOnRoutedEventBehaviour(RoutedEvent routedEvent)
{
_routedEvent = routedEvent;
}
/// <summary>
/// Handles attaching or Detaching Event handlers when a Command is assigned or unassigned
/// </summary>
/// <param name="sender"></param>
/// <param name="oldValue"></param>
/// <param name="newValue"></param>
protected override void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue)
{
UIElement element = sender as UIElement;
if (element == null) { return; }
if (oldValue != null)
{
element.RemoveHandler(_routedEvent, new RoutedEventHandler(EventHandler));
}
if (newValue != null)
{
element.AddHandler(_routedEvent, new RoutedEventHandler(EventHandler));
}
}
protected void EventHandler(object sender, RoutedEventArgs e)
{
HandleEvent(sender, e);
}
}
internal abstract class ExecuteCommandBehaviour
{
protected DependencyProperty _property;
protected abstract void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue);
protected void HandleEvent(object sender, EventArgs e)
{
DependencyObject dp = sender as DependencyObject;
if (dp == null)
{
return;
}
ICommand command = dp.GetValue(_property) as ICommand;
if (command == null)
{
return;
}
if (command.CanExecute(e))
{
command.Execute(e);
}
}
/// <summary>
/// Listens for a change in the DependencyProperty that we are assigned to, and
/// adjusts the EventHandlers accordingly
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void PropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// the first time the property changes,
// make a note of which property we are supposed
// to be watching
if (_property == null)
{
_property = e.Property;
}
object oldValue = e.OldValue;
object newValue = e.NewValue;
AdjustEventHandlers(sender, oldValue, newValue);
}
}
}
}
The ControlEventBehaviour class
public class ControlEventBehaviour
{
#region Button
public static readonly DependencyProperty MouseEnterCommand = Behaviours.EventBehaviourFactory.CreateCommandExecutionEventBehaviour
(Button.MouseEnterEvent, "MouseEnterCommand", typeof(ControlEventBehaviour));
public static void SetMouseEnterCommand(DependencyObject o, ICommand value)
{
o.SetValue(MouseEnterCommand, value);
}
public static ICommand GetMouseEnterCommand(DependencyObject o)
{
return o.GetValue(MouseEnterCommand) as ICommand;
}
public static readonly DependencyProperty MouseLeaveCommand = Behaviours.EventBehaviourFactory.CreateCommandExecutionEventBehaviour
(Button.MouseLeaveEvent, "MouseLeaveCommand", typeof(ControlEventBehaviour));
public static void SetMouseLeaveCommand(DependencyObject o, ICommand value)
{
o.SetValue(MouseLeaveCommand, value);
}
public static ICommand GetMouseLeaveCommand(DependencyObject o)
{
return o.GetValue(MouseLeaveCommand) as ICommand;
}
public static readonly DependencyProperty MouseDoubleClickCommand = Behaviours.EventBehaviourFactory.CreateCommandExecutionEventBehaviour
(Button.MouseDoubleClickEvent, "MouseDoubleClickCommand", typeof(ControlEventBehaviour));
public static void SetMouseDoubleClickCommand(DependencyObject o, ICommand value)
{
o.SetValue(MouseDoubleClickCommand, value);
}
public static ICommand GetMouseDoubleClickCommand(DependencyObject o)
{
return o.GetValue(MouseDoubleClickCommand) as ICommand;
}
#endregion Button
}
The shown example only handle button events, but with slight modification can be used for all WPF controls.
In the View:
<Button Command="{Binding Path=ButtonClickCommand}"
utilities:ControlEventBehaviour.MouseEnterCommand="{Binding Path=MouseEnter}"
utilities:ControlEventBehaviour.MouseLeaveCommand="{Binding Path=MouseLeave}"
utilities:ControlEventBehaviour.MouseDoubleClickCommand="{Binding Path=ButtonDoubleClick}">
And finally in the ViewModel. This is only for MouseEnter, but I'm sure that you get the gist of it :)
private RelayCommand _mouseEnter = null;
public virtual RelayCommand MouseEnter
{
get
{
if (_mouseEnter == null)
{
_mouseEnter = new RelayCommand(param =>
{
try
{
}
catch { }
}, param => { return true; });
}
return _mouseEnter;
}
}
As always, feel free to comment, or ask.