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

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

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

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

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

5 ответов

Решение

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

CommandManager.InvalidateRequerySuggested();

Посмотреть больше информации на MSDN

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

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

Я использовал команду Делегирования MVVM Helper, которая выглядит как показано ниже (я немного подправил ее для нашего требования). ты должен позвонить 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);
}

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

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

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

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

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

Я реализовал решение для обработки зависимости свойств от команд, здесь ссылка /questions/40015109/mvvm-privyazat-relaycommand-canexecute-k-svojstvu/40015117#40015117

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

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

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

Другие вопросы по тегам