C# Disciples

my life in Avalon ….

AttachedCommandBehavior aka ACB

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…

ACB

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 :D

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

kick it on DotNetKicks.com

December 4, 2008 - Posted by marlongrech | WPF, tips and tricks | | 21 Comments

21 Comments »

  1. Marlon – this is fantastic stuff. The code could help me solve an issue I had a while ago trying to hook up a generic undo framework. It was limited by the lack of Command capability, so I’ll have a play around and see if I can get it up and running now.

    Thanks a lot Marlon.

    Comment by peteohanlon | December 4, 2008 | Reply

  2. Glad you like it Pete :)

    Comment by marlongrech | December 4, 2008 | Reply

  3. Very nice young Marlon. Looks like you is back in the saddle

    Comment by sacha | December 5, 2008 | Reply

  4. Thanks Sacha…

    Comment by Marlon Grech | December 5, 2008 | Reply

  5. The EventHandlerGenerator class is pretty nuts matey, where the heck did you think that one up from.

    Comment by sacha | December 5, 2008 | Reply

  6. Hey did you use this source http://blogs.msdn.com/joelpob/archive/2005/07/01/434728.aspx for the EventHandlerGenerator idea. Looks simliar. Neat anyway, well done you.

    Comment by sacha | December 5, 2008 | Reply

  7. Yea I learnt a lot from that post… mine is a bit different because I am calling instance methods thus I had to attach the method call the the instance of the CommandBehaviorBinding… It was fun :D

    Comment by Marlon Grech | December 5, 2008 | Reply

  8. Very cool article Marlon. I guess the attached properties and commands are the topics of this end of year :)
    I wrote in my blog about triggering commands in other event (http://www.japf.fr/?p=22) but your approach generalizes the principle in a very nice way. Thank you for your work !

    Comment by jerem2205 | December 5, 2008 | Reply

  9. Thanks Jer

    Comment by Marlon Grech | December 5, 2008 | Reply

  10. Coincidentally, someone just mentioned on the WPF Toolkit forum that they would like to execute a command on a DataGrid mouse click event. I pointed them in your direction!

    Comment by colineberhardt | December 5, 2008 | Reply

  11. [...] V2 aka ACB YOUAfter publishing my AttachedCommandBehavior library, (you can read about this here), some WPF Disciples recommended some new features to make [...]

    Pingback by AttachedCommandBehavior V2 aka ACB « C# Disciples | December 13, 2008 | Reply

  12. Really nice work. I really like this implementation. But it seems like there is no way to pass EventArgs to the command’s execute method. The only contextual data we can pass in is CommandParameter, which could be limiting for some scenerios. Thanks a lot for the excellent work.

    Comment by knight98 | May 14, 2009 | Reply

    • I came here to make the same comment… I need to check the Handled property on a MouseDoubleClick event that fires an attached command. This impplementation has saved me some headaches, but now I’ve got a problem. Does someone know how to do it? I’m taking a look at EventHandlerGenerator.CreateDelegate but so much IL code is making my head spin.

      Comment by Sergi Díaz | July 1, 2009 | Reply

  13. The change in IL is an option that I would not recommend because as such you don’t want the ViewModel to do this.

    I suggest creating an attached behaviour that does this…

    Comment by marlongrech | July 1, 2009 | Reply

  14. by an attached behaviour I mean an attached property

    Comment by marlongrech | July 1, 2009 | Reply

  15. And how would I do that with an attached property? I don’t have access to the EventArgs of an event on the ViewModel, and I don’t really want to, that would break any design.
    Actually, it makes sense to mark the event as handled once it executes a command, don’t you think so?
    I’ve solved my problem with an “old school” approach – subscribing to a ListBox MouseDoubleClick event and getting the ListBoxItem that fired the event from e.OriginalSource, from there I can execute the command in its ViewModel object, and of course I can mark the event as handled. Not an elegant solution, mind you, but it works.
    Don’t take me wrong, I love what you’ve done here, that’s why I try to use this approach as much as possible! :D .

    Comment by Sergi Díaz | July 2, 2009 | Reply

  16. The attached property would do exactly what you are doing in code behind but in a more generic manner so that you can re use it.

    Are you getting it? if you want I can write up a small code sample for you…

    Comment by marlongrech | July 2, 2009 | Reply

  17. Actually no, I don’t get it. I don’t see how I can “link” the event delegate to an attached property… As far as I know, you can only assign a method to an event in XAML. Even if I make an attached property of type delegate I fail to see how I could assign that to an event :?
    Unless… of course! I could globally subscribe to ListBoxItem.MouseDoubleClick, check if the ListBoxItem has an attached property (which would be a command in my case) and in case it has it, mark the event as handled and execute the command. Was that your idea?

    Comment by Sergi Díaz | July 2, 2009 | Reply

  18. [...] 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. [...]

    Pingback by How to get the EventArgs as a CommandParameter using the AttachedCommandBehaviour « C# Disciples | July 3, 2009 | Reply

  19. Great commands

    Comment by ipotpal | October 28, 2009 | Reply


Leave a comment