6

I would like to use ApplicationCommands.Cut, copy, paste, save,... They seem interesting because of the command routing, keybindings and the fact that some controls use them. I understand how I can bind to relay/delegatecommands on my VM but I can't seem to get my head around the Application commands. I found a couple of old answers but no other information and I am kind of reluctant of following those routes.

This seems like something common but yet informations seems to be very limited. How is this commonly achieved? (With or without using PRISM, MVVM Light, ...)

Old answers:

How to bind ApplicationCommands to a ViewModel but this seems strange to me to solve this using a behavior

WPF - Handle an ApplicationCommand in the ViewModel but I don't think that's MVVM in the accepted answer.

Using attached properties in an old article: CommandBindings with MVVM referencing another article.

Community
  • 1
  • 1
Jef Patat
  • 999
  • 1
  • 10
  • 26
  • 1
    Sorry for not having time for the a detailed answer, but the first of your links is on the right track. The example there tightly couples the behavior to a view model ( this: `AssociatedObject.SelectedItem as NestingItemTreeViewModelBase;`). This prevents the behavior to be truly reusable. Instead of casting, you should expose `ICommand` dependency properties. You'd bind these DPs to your ViewModel containing the logic for the command and then relay the application commands to the commands bound to this DPs. This will comply with the MVVM pattern. Hope that gives you a direction – Tseng Jan 26 '16 at 22:58
  • Could you elaborate on this part: "and then relay the application commands to the commands bound to this DPs"? Also, I don't have the need for a usercontrol so where do these dependency properties go? – Jef Patat Jan 27 '16 at 07:48
  • 1
    You can add DPs to behaviors too. They derive from `DependencyObject` too, as all WPF controls also do. Inside i.e. `CopyCommandCanExecute` you call the `e.CanExecute = CopyCommand.CanExecute(null)` and `CopyCommand.Execute(null)` respectively (`CopyCommand` is wrapper around the CopyCommand DP). In Xaml you'd bind your ViewModel to it ` – Tseng Jan 27 '16 at 08:31

1 Answers1

1

It has been a while since I asked this question but it looks like a lot of people are looking at it. I ended up using a behavior which connects the VM command list to the FrameworkElement. This seems to be the most flexible and resusable solution.

public class AttachCommandBindingsBehavior : Behavior<FrameworkElement>
{
    public ObservableCollection<CommandBinding> CommandBindings
    {
        get
        {
            return (ObservableCollection<CommandBinding>)GetValue(CommandBindingsProperty);
        }
        set
        {
            SetValue(CommandBindingsProperty, value);
        }
    }
    public static readonly DependencyProperty CommandBindingsProperty = DependencyProperty.Register("CommandBindings", typeof(ObservableCollection<CommandBinding>), typeof(AttachCommandBindingsBehavior), new PropertyMetadata(null, OnCommandBindingsChanged));

    private static void OnCommandBindingsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        AttachCommandBindingsBehavior attachCommandBindingsBehavior = (AttachCommandBindingsBehavior)sender;

        if (attachCommandBindingsBehavior == null)
            return;

        ObservableCollection<CommandBinding> commandBindings = (ObservableCollection<CommandBinding>)e.NewValue;

        if (commandBindings != null)
        {
            if (attachCommandBindingsBehavior.CommandBindings != null)
                attachCommandBindingsBehavior.CommandBindings.CollectionChanged -= attachCommandBindingsBehavior.CommandBindings_CollectionChanged;

            attachCommandBindingsBehavior.CommandBindings.CollectionChanged += attachCommandBindingsBehavior.CommandBindings_CollectionChanged;
        }
    }

    void CommandBindings_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        ObservableCollection<CommandBinding> collection = (ObservableCollection<CommandBinding>)sender;

        if (collection != null)
        {
            foreach (CommandBinding commandBinding in collection)
                AssociatedObject.CommandBindings.Add(commandBinding);
        }
    }
}

In xaml you can then do this:

<i:Interaction.Behaviors>
    <localBehaviors:AttachCommandBindingsBehavior CommandBindings="{Binding CommandBindings}"/>
</i:Interaction.Behaviors>
Jef Patat
  • 999
  • 1
  • 10
  • 26
  • 1
    To anyone using this behavior, please know that it's broken and only works if command bindings are added _after_ the behavior gets attached (which is almost never the case). Another user was having issues with this implementation, and I posted a [modified version](http://stackoverflow.com/a/48506698/175070) that should work more reliably. – Mike Strobel Jan 29 '18 at 17:31