WPF : Attached Commands

In my last post I showed you how to execute ViewModel ICommand(s) from FrameworkElement using the new Blend 3 Behaviours Dll. Well this time I want to show you how to run a ViewModel ICommand from any FrameworkElement without the use of the Blend 3 Behaviours Dll.

I should state that this post was inspired by a good friend of mine Marlon Grechs AttachedCommandBehavior V2 aka ACB blog post. I loved and have used Marlons idea, but he uses dynamically created IL, to create the correct type of RoutedEvent handler that matches the event you are trying to use to trigger the ICommand. It is way cool, but I couldn’t help but think there was an easier way. I like simple things, they suit me, as I am a simple man.

So what I thought was why not just use an Attached DP or 2, and a bit of Reflection, and Event hooking/Binding and ala-kazam, job done.

Here is how, the ViewModel looks like this

   1:  /// <summary>
   2:  /// A small demo view model with a single
   3:  /// ICommand exposed, that will be executed
   4:  /// using the new Blend3 Interactivity
   5:  /// functionality, such as TargetedTriggerAction<T>
   6:  /// </summary>
   7:  public class DemoViewModel : ViewModelBase
   8:  {
   9:      #region Data
  10:      //Commands
  11:      private ICommand demoCommand = null;
  12:  
  13:      #endregion
  14:  
  15:      #region Ctor
  16:      public DemoViewModel()
  17:      {
  18:          //wire up command
  19:          demoCommand = new SimpleCommand
  20:          {
  21:              CanExecuteDelegate = x => true,
  22:              ExecuteDelegate = x =>
  23:                  {
  24:                      MessageBox.Show("In the ViewModel");
  25:                  }
  26:          };
  27:      }
  28:      #endregion
  29:  
  30:      #region Public Properties
  31:  
  32:      public ICommand DemoCommand
  33:      {
  34:          get { return demoCommand; }
  35:      }
  36:      #endregion
  37:  }

I am actually using Marlon Grechs  SimpleCommand but if you prefer you can use Prisms DelegateCommand, or Josh Smiths RelayCommand, they all work.

So next all I do is create some attached behaviour using the magic of Attached DPs. Here is the full code.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Windows;
   6:  using System.Windows.Input;
   7:  using System.Reflection;
   8:  using System.Windows.Media;
   9:  
  10:  namespace AttachedCommands
  11:  {
  12:      public static class CommandBehavior
  13:      {
  14:          #region TheCommandToRun
  15:  
  16:          /// <summary>
  17:          /// TheCommandToRun : The actual ICommand to run
  18:          /// </summary>
  19:          public static readonly DependencyProperty TheCommandToRunProperty =
  20:              DependencyProperty.RegisterAttached("TheCommandToRun",
  21:                  typeof(ICommand),
  22:                  typeof(CommandBehavior),
  23:                  new FrameworkPropertyMetadata((ICommand)null));
  24:  
  25:          /// <summary>
  26:          /// Gets the TheCommandToRun property.  
  27:          /// </summary>
  28:          public static ICommand GetTheCommandToRun(DependencyObject d)
  29:          {
  30:              return (ICommand)d.GetValue(TheCommandToRunProperty);
  31:          }
  32:  
  33:          /// <summary>
  34:          /// Sets the TheCommandToRun property.  
  35:          /// </summary>
  36:          public static void SetTheCommandToRun(DependencyObject d, ICommand value)
  37:          {
  38:              d.SetValue(TheCommandToRunProperty, value);
  39:          }
  40:          #endregion
  41:  
  42:          #region RoutedEventName
  43:  
  44:          /// <summary>
  45:          /// RoutedEventName : The event that should actually execute the
  46:          /// ICommand
  47:          /// </summary>
  48:          public static readonly DependencyProperty RoutedEventNameProperty =
  49:              DependencyProperty.RegisterAttached("RoutedEventName", typeof(String),
  50:              typeof(CommandBehavior),
  51:                  new FrameworkPropertyMetadata((String)String.Empty,
  52:                      new PropertyChangedCallback(OnRoutedEventNameChanged)));
  53:  
  54:          /// <summary>
  55:          /// Gets the RoutedEventName property.  
  56:          /// </summary>
  57:          public static String GetRoutedEventName(DependencyObject d)
  58:          {
  59:              return (String)d.GetValue(RoutedEventNameProperty);
  60:          }
  61:  
  62:          /// <summary>
  63:          /// Sets the RoutedEventName property.  
  64:          /// </summary>
  65:          public static void SetRoutedEventName(DependencyObject d, String value)
  66:          {
  67:              d.SetValue(RoutedEventNameProperty, value);
  68:          }
  69:  
  70:          /// <summary>
  71:          /// Hooks up a Dynamically created EventHandler (by using the 
  72:          /// <see cref="EventHooker">EventHooker</see> class) that when
  73:          /// run will run the associated ICommand
  74:          /// </summary>
  75:          private static void OnRoutedEventNameChanged(DependencyObject d,
  76:              DependencyPropertyChangedEventArgs e)
  77:          {
  78:              String routedEvent = (String)e.NewValue;
  79:  
  80:              //If the RoutedEvent string is not null, create a new
  81:              //dynamically created EventHandler that when run will execute
  82:              //the actual bound ICommand instance (usually in the ViewModel)
  83:              if (!String.IsNullOrEmpty(routedEvent))
  84:              {
  85:                  EventHooker eventHooker = new EventHooker();
  86:                  eventHooker.ObjectWithAttachedCommand = d;
  87:  
  88:                  EventInfo eventInfo = d.GetType().GetEvent(routedEvent,
  89:                      BindingFlags.Public | BindingFlags.Instance);
  90:  
  91:                  //Hook up Dynamically created event handler
  92:                  if (eventInfo != null)
  93:                  {
  94:                      eventInfo.AddEventHandler(d,
  95:                          eventHooker.GetNewEventHandlerToRunCommand(eventInfo));
  96:                  }
  97:              }
  98:          }
  99:          #endregion
 100:      }
 101:  
 102:      /// <summary>
 103:      /// Contains the event that is hooked into the source RoutedEvent
 104:      /// that was specified to run the ICommand
 105:      /// </summary>
 106:      sealed class EventHooker
 107:      {
 108:          #region Public Methods/Properties
 109:          /// <summary>
 110:          /// The DependencyObject, that holds a binding to the actual
 111:          /// ICommand to execute
 112:          /// </summary>
 113:          public DependencyObject ObjectWithAttachedCommand { get; set; }
 114:  
 115:          /// <summary>
 116:          /// Creates a Dynamic EventHandler that will be run the ICommand
 117:          /// when the user specified RoutedEvent fires
 118:          /// </summary>
 119:          /// <param name="eventInfo">The specified RoutedEvent EventInfo</param>
 120:          /// <returns>An Delegate that points to a new EventHandler
 121:          /// that will be run the ICommand</returns>
 122:          public Delegate GetNewEventHandlerToRunCommand(EventInfo eventInfo)
 123:          {
 124:              Delegate del = null;
 125:  
 126:              if (eventInfo == null)
 127:                  throw new ArgumentNullException("eventInfo");
 128:  
 129:              if (eventInfo.EventHandlerType == null)
 130:                  throw new ArgumentException("EventHandlerType is null");
 131:  
 132:              if (del == null)
 133:                  del = Delegate.CreateDelegate(eventInfo.EventHandlerType, this,
 134:                        GetType().GetMethod("OnEventRaised",
 135:                          BindingFlags.NonPublic |
 136:                          BindingFlags.Instance));
 137:  
 138:              return del;
 139:          }
 140:          #endregion
 141:  
 142:          #region Private Methods
 143:  
 144:          /// <summary>
 145:          /// Runs the ICommand when the requested RoutedEvent fires
 146:          /// </summary>
 147:          private void OnEventRaised(object sender, EventArgs e)
 148:          {
 149:              ICommand command = (ICommand)(sender as DependencyObject).
 150:                  GetValue(CommandBehavior.TheCommandToRunProperty);
 151:  
 152:              if (command != null)
 153:              {
 154:                  command.Execute(null);
 155:              }
 156:          }
 157:          #endregion
 158:      }
 159:  
 160:  }

NOTE :

If you look at the ICommand interface you will notice that the Execute method look like this

void Execute(
    Object parameter
)

I have not catered for the ICommand parameter in my implementation, but if you want to do that it should be simply a case of yet another attached property, which you could then feed into the EventHooker class shown above, and then pass to the command when executing. So instead of command.Execute(null); you could do something like command.Execute(commandParameter);

Basically I do not use the command parameter that often, so did not include it, but you could, it would be trivial.

The last thing to do is to show you how to use this in a View, here is how.

   1:  <Window x:Class="AttachedCommands.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:local="clr-namespace:AttachedCommands"
   5:      Title="Window1" Height="300" Width="300">
   6:      <Grid>
   7:  
   8:          <Grid.RowDefinitions>
   9:              <RowDefinition Height="*"/>
  10:              <RowDefinition Height="*"/>
  11:          </Grid.RowDefinitions>
  12:  
  13:          <!-- Fires DemoCommand ICommand when MouseDown RoutedEvent occurs -->
  14:          <Grid Grid.Row="0"
  15:                  local:CommandBehavior.RoutedEventName="MouseDown"
  16:                  local:CommandBehavior.TheCommandToRun=
  17:                      "{Binding Path=DemoCommand}"
  18:                  Background="Orange">
  19:              <Label VerticalAlignment="Center" HorizontalAlignment="Center"
  20:                     Content="MouseDown to raise Command"/>
  21:  
  22:          </Grid>
  23:  
  24:          <!-- Fires  DemoCommand ICommand when MouseWheel RoutedEvent occurs -->
  25:          <Grid Grid.Row="1"
  26:                  local:CommandBehavior.RoutedEventName="MouseWheel"
  27:                  local:CommandBehavior.TheCommandToRun=
  28:                      "{Binding Path=DemoCommand}"
  29:                  Background="Pink">
  30:  
  31:              <Label VerticalAlignment="Center" HorizontalAlignment="Center"
  32:                     Content="MouseWheel to raise Command"/>
  33:  
  34:  
  35:          </Grid>
  36:      </Grid>
  37:  
  38:  </Window>

And here is a small screen shot of it running:

ABC

And as always here is a small demo project: attachedcommands.zip

Enjoy

28 Comments so far »

  1. Marlon Grech said

    am May 2 2009 @ 2:35 pm

    great job dude….. looks great!!! 5 Stars from me!!!!

    BANG IT

  2. sacha said

    am May 3 2009 @ 2:56 pm

    Thanks Marlon. You inspired this one dude, to credit to ya buddy

  3. Dew Drop - May 4, 2009 | Alvin Ashcraft's Morning Dew said

    am May 4 2009 @ 1:09 pm

    [...] WPF : Attached Commands (Sacha Barber) [...]

  4. vladisld said

    am May 13 2009 @ 1:38 pm

    Thanks for a very usable class.

    One question though. It seems that only one event could be hooked to a command for particular dependency object – am I wrong ?

  5. sacha said

    am May 13 2009 @ 1:53 pm

    vladisld

    Yeah what you say is true, it suited my needs. I fyou want multiple events for a single command, perhaps you could implement some sort of collection, and each item within collection could have its own CommandEvent class.

    Actually from memory Mark Jumal did something like this

    Maybe have a look at http://www.julmar.com/blog/mark/PermaLink,guid,8b3e4279-70a5-431e-8fa3-4c1e047df311.aspx

  6. Jeff Brand said

    am May 18 2009 @ 12:55 am

    So the CanExecute is not called? I set it to false in the demo code and everything still works. Any way to get that enabled?

    Thanks!

  7. sacha said

    am May 18 2009 @ 7:40 am

    The only way to do this 100% correctly is by using ICommandSource, and as there are so few controls that implement this such as Button, MenuItem, and ListBoxItem, the only thing you could do is check for the command.CanExecut state just before you run it.

    I didn’t do this as it is not what I was after, but I think that should work.

    So just do this

    private void OnEventRaised(object sender, EventArgs e)
    {
    ICommand command = (ICommand)(sender as DependencyObject).
    GetValue(CommandBehavior.TheCommandToRunProperty);

    if (command != null)
    {
    if (command.CanExecute(null))
    command.Execute(null);
    }
    }

    That works, I just tried it.

  8. martin said

    am May 19 2009 @ 5:06 pm

    Hello,

    your Class is great! Many Thanks. I missed a CommandParameter Propertie. Your class is very easy to understand so could implement the CommandParameter on my own.
    :)

    greetings

  9. sacha said

    am May 19 2009 @ 8:12 pm

    Yeah parameter should be easy to do. Glad you like

  10. David Vennikov said

    am May 25 2009 @ 12:03 pm

    Hi,
    Sacha, great solution!

    One question though – what if I want to apply more than one command to the same control on different events.

    e.g:

    Is it possible?

  11. David Vennikov said

    am May 25 2009 @ 12:10 pm

    Is this possible?

  12. David Vennikov said

    am May 25 2009 @ 12:11 pm

  13. sacha said

    am May 25 2009 @ 12:11 pm

    David

    Sure that would be possible. Take this code and wrap it is some class. Then have a collection of those classes and use that as an attached DP. Where each item in the collection can have whatever command/event you want.

    So basically you would have something like

    <TextBox>
    <TextBox.MyAttachedEventsCommandCollection>
    <local:AttachedCommandCollection>
    <local:AttachedCommand CommandToUse=”{Binding SomeCommand1}” Event=”MouseDown”/>
    <local:AttachedCommand CommandToUse=”{Binding SomeCommand2}” Event=”MouseUp”/>
    </local:AttachedCommandCollection>
    </TextBox.MyAttachedEventsCollection>
    </TextBox>

    See what I mean

  14. Ray Akkanson said

    am May 26 2009 @ 12:29 pm

    Simple is better offcourse. Do you have a sample project Sacha?

    Ray Akkanson

  15. sacha said

    am May 26 2009 @ 12:53 pm

    There is a link at bottom of post

  16. MikeB said

    am June 9 2009 @ 11:48 am

    Hi, thanks for your article, is there a way to use this class with dynamically created buttons? For example, I have a list box that contains buttons (I call it a command bar) that is created from the database based on a users security. The Database contains the name of the button along with the name of the ICommand. Is there a way to convert the string name of the ICommand to an ICommand?

  17. sacha said

    am June 9 2009 @ 11:52 am

    MikeB

    You could not use this class in the situation you state. You could use parts of it, but the part to get the ICommand, you would need to change that to a String and use Reflection to get a Property of the DataContext (ViewModel) that has the name of the String supplied to the “CommandToRun” DP

  18. MikeB said

    am June 9 2009 @ 12:07 pm

    Thank you for your response.

  19. ChrisH said

    am July 23 2009 @ 8:13 pm

    Great code! Very helpful indeed. However I’m having trouble implementing the solution you outlined for David:

    How do I implement the MyAttachedEventsCommandCollection?

  20. sacha said

    am July 24 2009 @ 7:36 am

    Chris

    Go check out my MVVM framework library which I have been writing about lately.

    http://www.codeproject.com/KB/WPF/CinchII.aspx

    That has the answers

  21. Yazid said

    am July 24 2009 @ 9:42 am

    Hi, excellent article. I have used your sample so that onMouseDown I execute a command on the ViewModel.

    How do I pass the mousePosition to the ViewModel?

    TIA
    Yaz

  22. sacha said

    am July 24 2009 @ 9:51 am

    Yazid you could do that using a Binding or pass it into an extra DP called Param, which you then use to pass to the command in the ViewModel.

    For a more complete ICommand/Event implemenation that caters for parameters see my new MVVM framework article

    http://www.codeproject.com/KB/WPF/CinchII.aspx

  23. Dan said

    am September 2 2009 @ 3:17 pm

    Sacha, great article!

    What if I wanted to link to an attached event instead of a native one like MouseDown. For instance “Microsoft.Surface.Presentation.Contacts.ContactDownEvent”

    Thanks

  24. sacha said

    am September 2 2009 @ 3:44 pm

    Dan not sure about that. I think you would have to try it

  25. Shree Menon said

    am September 16 2009 @ 2:06 pm

    Thanks for this great Class. As said before it is so simple that it took only few minutes to understand and also add command parameter. I am using this in Prism

    Thanks again.

  26. sacha said

    am September 16 2009 @ 2:12 pm

    You should look at my Cinch MVVM framework for a better Commanding idea. Its called EventCommander, and passes the args to the ICommand also.

  27. Arun nair said

    am January 2 2010 @ 2:51 pm

    Hi,
    Thanks for this great class. It is really useful. I made a small modification to include the command parameter. The change I made was to the OnEventRaised method of the EventHooker class. It now looks like this:
    private void OnEventRaised(object sender, EventArgs e)
    {
    ICommand command = (ICommand)(sender as DependencyObject).GetValue(CommandBehavior.TheCommandToRunProperty);
    object parameter = (sender as DependencyObject).GetValue(CommandBehavior.CommandParameterProperty);

    if (command != null)
    {
    command.Execute(parameter);
    }
    }
    I do not know if this is the best way to go about it, but it works.

    Arun

  28. sacha said

    am January 2 2010 @ 2:59 pm

    This is already done for what I did after this blog. I did a massive ammount of work on a MVVM Framework called Cinch. Which is at cinch.codeplex.com and there is loads of documentation that describes how to use it all

Comment RSS · TrackBack URI

Leave a comment

Name: (Required)

eMail: (Required)

Website:

Comment: