RoutedUICommand vs ICommand in ViewModel and using InputBinding

In major implementations of the ICommand interface, such as DelegateCommand and RelayCommand, the InputGestures property contained in the RoutedCommand class is missing. This property supports binding to KeyGesture, and the Text property of RoutedUICommand supports control header customization. For instance:

<MenuItem Header="File"> <MenuItem Command="Open" /> 

The result is a menu item that says "Open Ctrl + O" in the File menu. For gestures, InputBindings will display the input tab to the command, but you will lose support for InputGestureText.

How can you keep the ease of binding to the ICommands view model when defining KeyGestures and Text for commands inside XAML or the view model? For example, I would like the command opened in the context menu and in the main menu to display the same title and InputGestureText that RoutedUICommand supports, but the implementation of the command is inside the view model, and not inside the window code behind.

+4
source share
1 answer

Looking at the MenuItem in the reflector, we see how the MenuItem receives Header / InputGesture values ​​that:

 private static object CoerceInputGestureText(DependencyObject d, object value) { RoutedCommand command; MenuItem item = (MenuItem) d; if ((string.IsNullOrEmpty((string) value) && !item.HasNonDefaultValue(InputGestureTextProperty)) && ((command = item.Command as RoutedCommand) != null)) { InputGestureCollection inputGestures = command.InputGestures; // Get appropriate gesture.... } return value; } 

There is similar code to force the use of the header property based on the current command, but in this case it searches for RoutedUICommand . This tells us that the commands must be an instance of RoutedCommand / RoutedUICommand in order to use this MenuItem function.

Looking at the RoutedCommand in the reflector, there is no easy way to create a DelegateCommand that comes from the RoutedCommand , because the CanExecute / Execute methods are not virtual.

We could write something like:

 public class DelegateCommand : RoutedCommand, ICommand { bool ICommand.CanExecute(object parameter) { // Insert delegate can execute logic } void ICommand.Execute(object parameter) { // Insert delegate execute logic } } 

But this does not stop calling the implicit CanExecute / Execute methods on the RoutedCommand . This may not be a problem.

Alternatively, we can create a custom MenuItem that is smart enough to look for our DelegateCommand (or somewhere else) and use its text / gestures.

 public class MyMenuItem : MenuItem { static MyMenuItem() { InputGestureTextProperty.OverrideMetadata(typeof(MyMenuItem), new FrameworkPropertyMetadata(string.Empty, null, CoerceInputGestureText)); } private static object CoerceInputGestureText(DependencyObject d, object value) { MenuItem item = (MenuItem)d; var command = item as DelegateCommand; if ((string.IsNullOrEmpty((string)value) && DependencyPropertyHelper.GetValueSource(item, InputGestureTextProperty).BaseValueSource == BaseValueSource.Default && command != null) { InputGestureCollection inputGestures = command.InputGestures; // Get appropriate gesture.... } // Call MenuItem Coerce var coerce = InputGestureTextProperty.GetMetadata(typeof(MenuItem)).CoerceValueCallback; return coerce(d, value); } } 
+4
source

Source: https://habr.com/ru/post/1394653/


All Articles