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:
And as always here is a small demo project: attachedcommands.zip
Enjoy


























Marlon Grech said
am May 2 2009 @ 2:35 pm
great job dude….. looks great!!! 5 Stars from me!!!!
BANG IT
sacha said
am May 3 2009 @ 2:56 pm
Thanks Marlon. You inspired this one dude, to credit to ya buddy
Dew Drop - May 4, 2009 | Alvin Ashcraft's Morning Dew said
am May 4 2009 @ 1:09 pm
[...] WPF : Attached Commands (Sacha Barber) [...]
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 ?
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
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!
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.
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
sacha said
am May 19 2009 @ 8:12 pm
Yeah parameter should be easy to do. Glad you like
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?
David Vennikov said
am May 25 2009 @ 12:10 pm
Is this possible?
David Vennikov said
am May 25 2009 @ 12:11 pm
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
Ray Akkanson said
am May 26 2009 @ 12:29 pm
Simple is better offcourse. Do you have a sample project Sacha?
Ray Akkanson
sacha said
am May 26 2009 @ 12:53 pm
There is a link at bottom of post
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?
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
MikeB said
am June 9 2009 @ 12:07 pm
Thank you for your response.
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?
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
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
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
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
sacha said
am September 2 2009 @ 3:44 pm
Dan not sure about that. I think you would have to try it
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.
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.
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
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