How to get the EventArgs as a CommandParameter using the AttachedCommandBehaviour

I have been asked many times how can I get the event args as a command parameter when using the AttachedCommandBehaviour.

Well the library does not support such a feature. I can do that yet I feel like the EventArgs should not flow down to the ViewModel because as such that is a UI thing… Yet in some case (such as for example if you want to set the Handled property to true of the Event args) this is needed.

I decided not to add this in the library yet I am gonna show how one can get this to work with some Attached Behavior.

The idea is to have a Attached Dependency property that handles the event and stores the last Event Args in another Attached Dependency Property. Here is the code to do so

   1: public class MouseEventArgsHandler

   2: {

   3:     #region LastMouseEventArgs

   4:  

   5:     /// <summary>

   6:     /// LastMouseEventArgs Attached Dependency Property

   7:     /// </summary>

   8:     public static readonly DependencyProperty LastMouseEventArgsProperty =

   9:         DependencyProperty.RegisterAttached("LastMouseEventArgs", typeof(MouseButtonEventArgs), typeof(MouseEventArgsHandler),

  10:             new FrameworkPropertyMetadata((MouseButtonEventArgs)null));

  11:  

  12:     /// <summary>

  13:     /// Gets the LastMouseEventArgs property.  This dependency property 

  14:     /// indicates ....

  15:     /// </summary>

  16:     public static MouseButtonEventArgs GetLastMouseEventArgs(DependencyObject d)

  17:     {

  18:         return (MouseButtonEventArgs)d.GetValue(LastMouseEventArgsProperty);

  19:     }

  20:  

  21:     /// <summary>

  22:     /// Sets the LastMouseEventArgs property.  This dependency property 

  23:     /// indicates ....

  24:     /// </summary>

  25:     public static void SetLastMouseEventArgs(DependencyObject d, MouseButtonEventArgs value)

  26:     {

  27:         d.SetValue(LastMouseEventArgsProperty, value);

  28:     }

  29:  

  30:     #endregion

  31:  

  32:     #region HandleMouseDoubleClick

  33:  

  34:     /// <summary>

  35:     /// HandleMouseDoubleClick Attached Dependency Property

  36:     /// </summary>

  37:     public static readonly DependencyProperty HandleMouseDoubleClickProperty =

  38:         DependencyProperty.RegisterAttached("HandleMouseDoubleClick", typeof(bool), typeof(MouseEventArgsHandler),

  39:             new FrameworkPropertyMetadata((bool)false,

  40:                 new PropertyChangedCallback(OnHandleMouseDoubleClickChanged)));

  41:  

  42:     /// <summary>

  43:     /// Gets the HandleMouseDoubleClick property.  This dependency property 

  44:     /// indicates ....

  45:     /// </summary>

  46:     public static bool GetHandleMouseDoubleClick(DependencyObject d)

  47:     {

  48:         return (bool)d.GetValue(HandleMouseDoubleClickProperty);

  49:     }

  50:  

  51:     /// <summary>

  52:     /// Sets the HandleMouseDoubleClick property.  This dependency property 

  53:     /// indicates ....

  54:     /// </summary>

  55:     public static void SetHandleMouseDoubleClick(DependencyObject d, bool value)

  56:     {

  57:         d.SetValue(HandleMouseDoubleClickProperty, value);

  58:     }

  59:  

  60:     /// <summary>

  61:     /// Handles changes to the HandleMouseDoubleClick property.

  62:     /// </summary>

  63:     private static void OnHandleMouseDoubleClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

  64:     {

  65:         Control control = d as Control;

  66:         if (control != null)

  67:         {

  68:             if ((bool)e.NewValue)

  69:                 control.MouseDoubleClick += ControlMouseDoubleClick;

  70:             else

  71:                 control.MouseDoubleClick -= ControlMouseDoubleClick;

  72:         }

  73:     }

  74:  

  75:     static void ControlMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)

  76:     {

  77:         SetLastMouseEventArgs((DependencyObject)sender, e);

  78:     }

  79:  

  80:     #endregion

  81: }

then you can use that value as the CommandParameter… Something like this

   1: <ListBox ItemsSource="{Binding Data}" 

   2:         local:MouseEventArgsHandler.HandleMouseDoubleClick="True"

   3:         acb:CommandBehavior.Event="MouseDoubleClick" 

   4:         acb:CommandBehavior.Command="{Binding DoIt}"

   5:         acb:CommandBehavior.CommandParameter="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(local:MouseEventArgsHandler.LastMouseEventArgs)}" />

 

and here is what the command in the ViewModel looks like

   1: DoIt = new SimpleCommand

   2: {

   3:     ExecuteDelegate = x =>

   4:         {

   5:             //set the event as handled

   6:             ((MouseButtonEventArgs)x).Handled = true;

   7:             System.Diagnostics.Debug.WriteLine("Event handled");

   8:         }

   9: };

 

The down side of this is that you have to do an attached property for every different event you want to handle, yet at the same time this should be a rare case and if it is not then you should really re think what you are doing with MVVM.

 

Hope it helps🙂

I created a demo project for anyone that wants to have a look.

13 thoughts on “How to get the EventArgs as a CommandParameter using the AttachedCommandBehaviour

  1. Great stuff!😉
    As soon as I saw your code on OnHandleMouseDoubleClickChanged I thought “Of course! Silly me!”. But that’s always the way, isn’t it?😀

  2. Ouch! I’ve found a nasty problem.

    This only works if the dependency properties are created internally in a specific order. local:MouseEventArgsHandler.HandleMouseDoubleClick=”True” must be executed before any CommandBehaviour dependency property, otherwise the command won’t have access to MouseEventArgsHandler.LastMouseEventArgs, since subscriptions to events are executed in the same order you subscribed to them.
    The problem, of course, is that this is XAML and you can’t control in which order they execute. I thought that the xaml parser would do it in the same order you declare them, but this doesn’t seem to be the case😦.

  3. I’ve fixed it using a Trigger. When MouseEventArgsHandler.HandleMouseDoubleClick is True, then I set the CommandBehaviour properties in setters, this way I’m sure about the order they get defined.

  4. Great stuff indeed !

    The same is really neat BUT for some reason where I use it in own MV-VM CommandBehavior.GetCommand() returns null in ControlMouseDoubleClick(object sender …) as if the sender had lost trace of the command somewhere.

    Any idea of what happens ?

  5. A little note regarding the v.2:
    I’m using your class with a MouseLeftDownButton event on a border. So, the line “Control control = d as Control” don’t suite for Border. I change it in “UIElement element = d as UIElement” and it works also for Border.
    Thank you so much

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s