WPF - Как заставить команду пересмотреть 'CanExecute' через его CommandBindings - PullRequest
122 голосов
/ 27 августа 2009

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

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

Похоже, что нет никаких открытых методов для RoutedCommand или CommandBinding для этого.

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

Ответы [ 6 ]

164 голосов
/ 27 августа 2009

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

CommandManager.InvalidateRequerySuggested();

Подробнее о MSDN

79 голосов
/ 17 июня 2011

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

23 голосов
/ 12 декабря 2011

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

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

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);
}
14 голосов
/ 08 февраля 2017

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

Решение заключается в следующем:

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

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

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

2 голосов
/ 22 мая 2015

Я реализовал решение для обработки зависимости свойств от команд, здесь ссылка 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 }
 );
0 голосов
/ 11 марта 2019

Это то, что сработало для меня: поместите CanExecute перед командой в XAML.

...