WPF-как заставить команду повторно оценить 'CanExecute' через его CommandBindings


у меня есть Menu каждая MenuItem в иерархии есть свои Command свойство установлено в RoutedCommand Я определил. Связанный CommandBinding обеспечивает обратный вызов для оценки CanExecute который управляет включенным состоянием каждого MenuItem.

этой почти строительство. Пункты меню изначально приходят с правильными включенными и отключенными состояниями. Однако, когда данные, что мой CanExecute обратный вызов использует изменения, мне нужна команда для повторного запроса результата мой обратный вызов для того, чтобы это новое состояние было отражено в пользовательском интерфейсе.

там, кажется, нет никаких публичных методов на RoutedCommand или CommandBinding для этого.

обратите внимание, что обратный вызов используется снова, когда я нажимаю или набираю элемент управления (я предполагаю, что он запускается при вводе, потому что наведение мыши не вызывает обновления).

5 116

5 ответов:

Не самый красивый в книге, но вы можете использовать CommandManager для аннулирования всех commandbinding:

CommandManager.InvalidateRequerySuggested();

Посмотреть подробнее на сайте MSDN

для тех, кто сталкивается с этим позже; если вы случайно используете MVVM и Prism, то Prism's DelegateCommand реализация ICommand предоставляет .RaiseCanExecuteChanged() метод для этого.

Я не мог использовать CommandManager.InvalidateRequerySuggested(); потому что я получил удар по производительности.

я использовал MVVM Helperделегирует команду, которая выглядит как ниже (я немного подправил ее для нашего req). вы должны позвонить command.RaiseCanExecuteChanged() от VM

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

/// <summary>
/// This method can be used to raise the CanExecuteChanged handler.
/// This will force WPF to re-query the status of this command directly.
/// </summary>
public void RaiseCanExecuteChanged()
{
    if (canExecute != null)
        OnCanExecuteChanged();
}

/// <summary>
/// This method is used to walk the delegate chain and well WPF that
/// our command execution status has changed.
/// </summary>
protected virtual void OnCanExecuteChanged()
{
    EventHandler eCanExecuteChanged = _internalCanExecuteChanged;
    if (eCanExecuteChanged != null)
        eCanExecuteChanged(this, EventArgs.Empty);
}

если вы свернули свой собственный класс, который реализует ICommand вы можете потерять много автоматических обновлений статуса заставляя вас полагаться на ручное обновление больше, чем должно быть необходимо. Он также может сломаться InvalidateRequerySuggested(). Проблема в том, что простой ICommand реализация не может связать новую команду с CommandManager.

решение состоит в том, чтобы использовать следующее:

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

    public void RaiseCanExecuteChanged()
    {
        CommandManager.InvalidateRequerySuggested();
    }

таким образом подписчики присоединяются к CommandManager а не ваш класс и может должным образом участвовать в изменении статуса команды.

я реализовал решение для обработки зависимости свойств от команд, здесь Ссылка https://stackoverflow.com/a/30394333/1716620

благодаря этому у вас будет такая команда:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
    //execute
    () => {
      Console.Write("EXECUTED");
    },
    //can execute
    () => {
      Console.Write("Checking Validity");
       return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
    },
    //properties to watch
    (p) => new { p.PropertyX, p.PropertyY }
 );