CommandBinding with dynamic Shortcut Support
In MVVM you normally don´t use Click-Event like in WinForms, Java Swing or other traditional GUI-Framework. On of the aims of MVVM is to reduce the binding between presentation and program logic. The way you do bind Actions to a button (or other control) is by an implementation of the RelayCommand class, which supports a basic functionality of this.
The implementation
In my applications I improved this by adding a flexable support for shortcut-bindings, which allows me to (re)bind the Menu- and Button-Command shortcuts at runtime. This is used to allow the user to change the default shortcuts in the configuration window.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
class UIRelayCommand : RelayCommand, INotifyPropertyChanged { private static readonly Dictionary<ModifierKeys, string> modifierText = new Dictionary<ModifierKeys, string>() { {ModifierKeys.None,""}, {ModifierKeys.Control,"Ctrl+"}, {ModifierKeys.Control|ModifierKeys.Shift,"Ctrl+Shift+"}, {ModifierKeys.Control|ModifierKeys.Alt,"Ctrl+Alt+"}, {ModifierKeys.Control|ModifierKeys.Shift|ModifierKeys.Alt,"Ctrl+Shift+Alt+"}, {ModifierKeys.Windows,"Win+"} }; private Key _key; public Key Key { get { return _key; } set { _key = value; RaisePropertyChanged("Key"); RaisePropertyChanged("GestureText"); } } private ModifierKeys _modifiers; public ModifierKeys Modifiers { get { return _modifiers; } set { _modifiers = value; RaisePropertyChanged("Modifiers"); RaisePropertyChanged("GestureText");} } public string GestureText { get { return modifierText[_modifiers] + _key.ToString(); } } public UIRelayCommand(Action<object> execute, Predicate<object> canExecute, Key key, ModifierKeys modifiers) : base(execute, canExecute) { _key = key; _modifiers = modifiers; } public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); } } |
The ViewModel implementation is quite the same like the RelayCommand Class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
private ICommand _newFileCommand; public ICommand NewFileCommand { get { if (_newFileCommand == null) _newFileCommand = new UIRelayCommand(p => OnNewFile(p), p => CanNewFile(p), Key.N, ModifierKeys.Control); //default shortcut return _newFileCommand; } } protected void OnNewFile(object p) { //create new file... } protected bool CanNewFile(object p) { return true; } |
Binding in the View works like this:
1 2 3 4 5 |
<Window.InputBindings> <KeyBinding Command="{Binding NewFileCommand}" Key="{Binding NewFileCommand.Key}" Modifiers="{Binding NewFileCommand.Modifiers}" /> </Window.InputBindings> ... <MenuItem Header="New File" Command="{Binding NewFileCommand}" InputGestureText="{Binding NewFileCommand.GestureText}" /> |
Changing the shortcut at runtime is really simple: just assign a different key and/or modifier to the object hold by the