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: }
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }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>
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is a small screen shot of it running:
And as always here is a small demo project: attachedcommands.zip
Enjoy
.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }
great job dude….. looks great!!! 5 Stars from me!!!!
BANG IT
Thanks Marlon. You inspired this one dude, to credit to ya buddy
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 ?
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
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!
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.
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
Yeah parameter should be easy to do. Glad you like
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?
the code was omitted for some reason:
the code was omitted for some reason:
>>>
Is this possible?
Is this possible?
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
Simple is better offcourse. Do you have a sample project Sacha?
Ray Akkanson
There is a link at bottom of post
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?
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
Thank you for your response.
Great code! Very helpful indeed. However I’m having trouble implementing the solution you outlined for David:
How do I implement the MyAttachedEventsCommandCollection?
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
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
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
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
Dan not sure about that. I think you would have to try it
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.
You should look at my Cinch MVVM framework for a better Commanding idea. Its called EventCommander, and passes the args to the ICommand also.
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
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
hi, I just tried to download the link and it doesn’t work… is there a way you could fix that… pweeease 🙂
Unfortunately I moved blogs and I lost stuff, so no can do sorry.