Introduction
The WPF Commands are really a cool and powerful tool that really help when you are trying to separate concerns by using patterns such as M-V-VM. If you haven’t had a look at my previous post on the ICommand I suggest that you do that before reading this post.
The only thing that I see a bit limited is the fact that you can use Commands only when a control implements the ICommandSource (such as ButtonBase etc). Well to say the truth you can still use commands if a Control does not implement ICommandSource but you have to do some plumbing yourself. Another problem that I see is that when a control implements the ICommandSource you are bound to the fact that the command will get executed according to the event being hooked up in the control implementation of ICommandSource, for example in case of a Button the Command would be executed when the Click event is raised. But what if you don’t want to execute a command when the Click event is fired, what if you want a command to be fired when the MouseRightButtonDown… One of my favorite WPF gurus, John Gossman blogged on this, read his post here.
I really love this approach yet you have to create an AttachedProperty for every different event you want to hook to.
It would be nice if you could supply the name of an Event in an AttachedProperty and the rest is done for you, i.e calling the Command when the event is fired… something like this….
1: <Border Background="Aqua"
2: local:CommandBehavior.Event="MouseDown"
3: local:CommandBehavior.Command="{Binding SomeCommand}"
4: local:CommandBehavior.CommandParameter="{Binding}"/>
What are the benefits of such an approach
1. Hook a Command to any Event and any control
2. You can change the event to which a command is hooked at runtime
3. You can Databind the Command, the CommandParameter and also the Event which will execute your Command (This is shown in the demo by binding the event to a combobox populated with event names.
This solution would not only let the ViewModel control what is shown in the View but also control the Behavior of the UI if needed.
With this the ViewModel now has the power to control also the behavior of the the UI !
And here comes the AttachedCommandBehavior
I had some spare time to play around with this and I wrote a small prototype for this. My implementation consists of a 3 Attached properties where the user can specify the Event, Command and CommandParameter. Once these properties are set the hooking of the event is done automatically so that when the specified event is fired the Command is called. Here is a breakdown of the classes I created…
CommandBehavior
Defines the Attached properties to be used on a control that need to hook an event to a Command.
Properties you will be using to set up a CommandBehavior
– BehaviorProperty
This is a private property that is used internal to create the CommandBehaviorBinding
– Event
This is an attached property that stores the Event name (as a string) for the Event you want to hook.
– Command
This is an attached property that stores the instance of the ICommand you want to execute when the event (specified in the Event property) is raised
– CommandParameter
This is an attached property that stores the object that you want to pass as parameter to the Command being executed
CommandBehaviorBinding
Defines the binding between an event and a command. This entity is also responsible of executing the command when the event is raised
EventHandlerGenerator
Static class that is able to create a method at runtime that has the same signature as the EventHandlerType of the Event so that we can register to any kind of event. PLEASE NOTE: This uses Reflection and also DynamicMethod which construct the IL for the event handler at runtime!
The code is very simple. All we are doing here is to create an event handler at runtime that has the same signature as the Event type. The EventHandler that we create on runtime calls a method of the CommandBehaviorBinding that executes the Command. By using reflection we get the EventInfo of the EventName set in the Attached property and hook the EventHandler we created at runtime to the Event. That’s basically it.
I think the best thing to do here is to have a look at the source code I provided in this post and see how it all works out.
Using AttachedCommandBehavior
You can specify the event name and the Command by doing something like this
1: <Border Background="Aqua" Width="300" Margin="0,0,10,0" Height="25"
2: local:CommandBehavior.Event="MouseDown"
3: local:CommandBehavior.Command="{Binding SomeCommand}"
4: local:CommandBehavior.CommandParameter="{Binding}" />
Here we are stating that when the MouseDown event is raised on the Border the SomeCommand should execute. Simple enough…
But we can do much more… We can also make the event dynamic…. something like this…
1: <WrapPanel>
2: <Border Background="Aqua" Width="300" Margin="0,0,10,0" Height="25"
3: local:CommandBehavior.Event="{Binding ElementName=events1, Path=SelectedItem}"
4: local:CommandBehavior.Command="{Binding SomeCommand}"
5: local:CommandBehavior.CommandParameter="{Binding ElementName=events1, Path=SelectedItem}"
6: />
7: <ComboBox ItemsSource="{Binding Events}" MinWidth="150" SelectedIndex="1" x:Name="events1"/>
8: </WrapPanel>
So here what we are doing is bind the Event attached property to the selected item in the ComboBox (The combobox is populated with a list of Event names that the border exposes). So when the user changed the selection of the ComboBox the Command will start being executed when the newly selected event is raised, which CommandBinding dynamic 🙂
Limitations
– You can only hook to Events that have a Delegate of type void (for example EventHandler). I did this by design not to over complicate the generation of the dynmaic EventHandler + because most events for controls are all void.
– This solution uses Reflection so it cannot be used in XBAPs because of the security restrictions there are in XBAPs
Conclusion
This is just a prototype that I have built in a couple of hours… nothing really fancy… I did not do a lot of testing on this so please keep that in mind if you are gonna use this in a production environment. I really like this solution because it is really flexible and makes the use of Commands more easy for any kind of Control and any kind of Event! No more code behind to hook up events 😀
One thing I did not add is the support for the CanExecute. I was not sure if it really makes sense in this context… Yet if you want to add it, it’s really easy all you have to do is handle the Command’s CanExecuteChanged event and call the CanExecute there 🙂
Hope you like this… Looking forward to hear feedback 🙂
Download the source code here