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.