WPF Actions

Databinding an action to code in WPF MVVM can be done in two ways:

ICommand

The simplest is to use an ICommand implementation for the code to execute.

Typically the ICommand implementation has a reference to the VM class, allowing it access to the state. Any changes that are to occur in the interface as a result of the action are accomplished by changes to properties in the VM instance, which are databound to the View.

Here is an example command:

namespace CharacterForms.Command
{
    public class CustomerSearchByKey:ICommand
    {
        CharacterMainViewModel vm;
        public CustomerSearchByKey(CharacterMainViewModel _vm)
        {
            vm = _vm;
        }

        public bool CanExecute(object parameter)
        {
            return (vm.Key >=0);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        } 

        public void Execute(object parameter)
        {
            vm.UnderlyingCharacter = vm.CharacterService
                                           .FetchByKey(vm.Key);
        }
    }
}

Note the CanExecute property that will control the enabled property of the bound input source.

The above action would be bound to the View as follows:

The VM instance would expose an instance of the command as a property:

        public ICommand FetchCommand { get; set; }

Initialised in the VM’s constructor

        public CharacterMainViewModel(ICharacterService service)
        {
            ...
            FetchCommand = new CustomerSearchByKey(this);
        }

And the interface would bind to the instance as follows:

        <Button Content="Search"
                ...
                Command="{Binding FetchCommand}" />

Note that the command has a CanExecute property. In order for it to function as you would expect, the text box that is bound to the Key property on the VM object require binding like this:

 Text="{Binding Path=Key, UpdateSourceTrigger=PropertyChanged}"

to allow the CanExecute property to be reevaluated when the user types in the box.

WPF Routed Commands

This option allows for more flexible interception off action intentions.

In this case a class is constructed containing the strongly typed command names:

namespace CharacterForms
{
    internal static class RoutedAppCommands
    {
        public static RoutedUICommand DoItCommand;

        static RoutedAppCommands()
        {
            var doItInputs = new InputGestureCollection();

            doItInputs.Add(
                 new KeyGesture(Key.B,
                                ModifierKeys.Control |
                                ModifierKeys.Shift)
            );

            DoItCommand = new RoutedUICommand("Do It",
                                              "DoIt",
                                              typeof(RoutedAppCommands),
                                              doItInputs);
        }
    }
}

In this case the command definitions are for a single command: DoItCommand.

Bindings are created between actions triggered by input gestures, and code that will execute the intention of the action.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            ...
            var vm = new ...
            this.DataContext = vm;

            var doIt = new CommandBinding(RoutedAppCommands.DoItCommand);
            doIt.Executed += new ExecutedRoutedEventHandler(vm.doIt_Executed);
            CommandBindings.Add(doIt);

        }
    }

In this case the key ^-shift B is bound to the command, and a method on the VM instance will be executed when the event occurs.

doIt also has a CanExecute property that can be used to control whether the event source is enabled.

The command can also be bound in the xaml to a control:

        <Button ... Command="tl:RoutedAppCommands.DoItCommand" />

where tl is the xml namespace added to reference the c# namespace the commands are in. Note that this will execute the same action method as pressing the key.

 

Leave a Reply