RoutedUICommand против ICommand во ViewModel и с использованием InputBinding - PullRequest
5 голосов
/ 03 февраля 2012

В базовых реализациях интерфейса ICommand, таких как DelegateCommand и RelayCommand, отсутствует свойство InputGestures, содержащееся в классе RoutedCommand.Это свойство поддерживает привязку к KeyGesture, а свойство Text в RoutedUICommand поддерживает установку заголовка элемента управления.Например:

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

Результатом является пункт меню, помеченный: «Открыть Ctrl + O» в пункте меню «Файл».Для жестов InputBindings отобразит входную гостевую команду, но вы потеряете поддержку InputGestureText.

Как сохранить простоту привязки к ICommands модели представления при определении KeyGestures & Textдля команд внутри XAML или модели представления?Например, я хотел бы, чтобы команда, отображаемая в контекстном меню и в главном меню, отображала тот же Header & InputGestureText, который поддерживается RoutedUICommand, но реализация команды находится внутри модели представления, а не внутри кода Window.

1 Ответ

6 голосов
/ 03 февраля 2012

Глядя на MenuItem в отражателе, мы можем видеть, как MenuItem принимает значения Header / InputGesture, а именно:

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;
}

Существует аналогичный код для принудительного использования свойства Headerоснованный на текущей команде, но в этом случае он ищет RoutedUICommand.Это говорит нам о том, что команды должны быть экземпляром RoutedCommand / RoutedUICommand, чтобы использовать эту функцию MenuItem.

Глядя на RoutedCommand в отражателе, не существует простого способасоздайте DelegateCommand, который получен из RoutedCommand, потому что его методы CanExecute / Execute не являются виртуальными.

Мы могли бы написать что-то вроде:

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

Но это делаетне препятствует вызову неявных CanExecute / Execute методов RoutedCommand.Что может или не может быть проблемой.

В качестве альтернативы, мы можем создать пользовательский MenuItem, который достаточно умен, чтобы искать нашу DelegateCommand (или где-то еще) и использовать ее текст / жесты.

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);
    }

}
...