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

About these ads

34 thoughts on “AttachedCommandBehavior aka ACB

  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.

  2. 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

  3. 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 !

  4. Pingback: AttachedCommandBehavior V2 aka ACB « C# Disciples

  5. 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.

    • 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.

  6. 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…

  7. 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.

  8. 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…

  9. 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?

  10. Pingback: How to get the EventArgs as a CommandParameter using the AttachedCommandBehaviour « C# Disciples

  11. Hi, nice work!

    But I’m having a problem, I’m trying to fire MouseLeftButtonUp as a Command but I always get null exception at CommandBehaviorBinding line 99. It seems that strategy is null. Any thoughts?

    André Carlucci

  12. Hi,
    I like it very much, finally a good way to bind my view to my ViewModel.

    Since it was handy in my project I added the capability to fire routed events also.

    Here the code:

    class RoutedCommandExecutionStrategy : IExecutionStrategy
    {

    private static ExecutedRoutedEventArgs CreateExecutedRoutedEventArgs(ICommand command, object parameter)
    {
    var construktor = typeof(ExecutedRoutedEventArgs).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
    new Type[] { typeof(ICommand), typeof(object) }, null);

    var argList = new object[] { command, parameter };
    ExecutedRoutedEventArgs foo = (ExecutedRoutedEventArgs)construktor.Invoke(BindingFlags.NonPublic, null, argList, Thread.CurrentThread.CurrentCulture);
    return foo;
    }

    #region IExecutionStrategy Member

    public CommandBehaviorBinding Behavior { get; set; }

    public void Execute(object parameter)
    {
    ExecutedRoutedEventArgs args = CreateExecutedRoutedEventArgs(Behavior.Command, parameter);
    args.RoutedEvent = CommandManager.ExecutedEvent;
    args.Source = Behavior.Owner;
    args.Handled = false;
    (Behavior.Owner as UIElement).RaiseEvent(args);
    }

    #endregion
    }

    And than add the strategy in the CommandBehaviorBindings

    public ICommand Command
    {
    get { return command; }
    set
    {
    command = value;
    //set the execution strategy to execute the command or fire an ExecutionEvent
    if (command is RoutedCommand)
    strategy = new RoutedCommandExecutionStrategy() { Behavior = this };
    else
    strategy = new CommandExecutionStrategy { Behavior = this };
    }
    }

    • Ops I meant … ” to trigger RoutedCommands” … not ” firing routed events”

      (well technical it is firing a RoutedEvent)

    • I have been attempting to attach a behavior to an expander such that when the control Expands (user clicks on the Expander Header) I can check to see if the user has logged on in the OnExpanderExpandedCommand. The problem that I am having is the expander header will expand and collapse but will not fire the MouseDown event event whit a wrapping border. If I click somewhere in the expander besides the header, the event fires and the command works. Do you have any suggestions on working with the expander?

      XAML code:

  13. Hi,

    Nice work, but I was having problems.

    I have a ListBox that is bound to an ObservableCollection (with a double click ACB attached tio it). when I was clearing down the binding using Bindingobject.Clear I was getting the error “Could not resolve event name” this was because a blank event was coming through for each binding element, I fixed this by
    changing

    binding.BindEvent(d, e.NewValue.ToString());

    to

    if (!string.IsNullOrEmpty(e.NewValue.ToString()) )
    {
    binding.BindEvent(d, e.NewValue.ToString());
    }

    in CommandBehavior OnEventChanged.

    This appears to fix the issue, but am I doing something else wrong?

    Regards

    Chris

  14. The inability to set the event handled flag to true severely limits the usefulness of this implementation. All sorts of problems appear higher up in the visual tree, for instance, if a ListBox is located in a TabItem, ListBox SelectionChanged events bubble up to the TabControl and fire SelectionChanged on the TabControl. AFAICT, there is no way to easily fix this. It’s a shame because if not for this problem, AttachedCommandBehavior would be great.

  15. Hey! Great article, thank you.
    I have a question about the attachedcommandbehavior in combination with contextmenus on a treeview.
    I have two contextmenus – one on the treeview itself and one on the treeviewitems. In both contextmenus I have a menuitem on which the same command is attached (for the click-event). When executing the command on the treeview itself, the command in the viewmodel behind gets “called” and everything’s fine. When executing the command on the treeviewitems the command in the viewmodel doesn’t get “called” and so the CommandBevahiorBinding.ExecuteCommand() method throws an exception, because the “Command”-Property is null there.
    Can someone help me out here?
    Thanks in advance,
    Jdan

  16. Hey one question. Is there a way to define if the event raised is marked as handled or allowed to continue bubbling? My scenario is that I have an expander with a double click handler on it. Then inside that expander I have something else with a double click handler on it. The interior item’s double click bubbles up to the expanders so I get the behavior of both double clicks.

  17. I keep hearing about how wonderfull wpf is in regards to binding and then i see the hundreds of posts of people who like myself trying figure out how to bind events, commands etc.

    Your code is more clever than anything i could ever write but i think it is a great indictment of MVVM and WPF that it required 4 classes and 300 lines of code so that you can wire up an event that takes one line when using code behind.

    300 vs 1 means 300 times more chance of errors and mistakes to creep in just so that some theoretical model is maintained.

    If the MVVM should be used with WPF then none of this additional code should be required, it should already be there built in and ready to use.

    complexity adds confusion and oppertunity for errors. The fewer parts the more beautifull the solution.

  18. Pingback: WPF 4.5 supports MarkupExtension in the event properties « A place for spare thoughts

  19. Hello, great job !
    Why when I’m trying to attach a behavior to a Lisviewitem (at the style) am I getting an Error 6 Cannot resolve the Style Property ‘Event’. Verify that the owning type is the Style’s TargetType, or use Class.Property syntax to specify the Property ?
    Thanks a lot.
    Regards,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s