ICommand CanExecute не запускается после PropertyChanged?
Я получил приложение WPF, которое показывает кнопку, привязанную к такой команде:
<Button Command="{Binding Path=TestrunStartCommand}" Content="GO!">
Команда определяется так:
public ICommand TestrunStartCommand
{
get { return new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress); }
}
public bool IsTestrunInProgress
{
get{
return _isTestrunInProgress;
}
set{
_isTestrunInProgress = value;
RaisePropertyChanged(IsTestrunInProgressPropertyName);
}
}
Проблема в том, что кнопка не будет активирована сразу после того, как я установил для IsTestrunInProgress
значение false, но только после того, как я IsTestrunInProgress
внутри окна приложения.
Не могли бы вы помочь мне понять это поведение и показать, как это исправить?
Дальнейшее чтение: шаблон команды wpf - когда он запрашивает, может выполнить
Ответы
Ответ 1
Интерфейс ICommand
предоставляет событие ICommand.CanExecuteChanged
, которое используется для информирования пользовательского интерфейса, когда необходимо переопределить состояние компонентов IsEnabled
компонентов, управляемых командами.
В зависимости от реализации используемого RelayCommand
, вам может потребоваться поднять это событие; Многие реализации выставляют метод, например RelayCommand.RaiseCanExecuteChanged()
, который вы можете вызвать, чтобы заставить пользовательский интерфейс обновляться.
В некоторых реализациях RelayCommand
используется CommandManager.RequerySuggested
, и в этом случае вам нужно будет вызвать CommandManager.InvalidateRequerySuggested()
, чтобы заставить пользовательский интерфейс обновиться.
Короче говоря, вам нужно будет вызвать один из этих методов из вашего средства настройки свойств.
Обновление
Когда состояние кнопки определяется при изменении активного фокуса, я считаю, что используется CommandManager
. Поэтому в настройщике вашего свойства после присвоения поля поддержки вызовите CommandManager.InvalidateRequerySuggested()
.
Обновление 2
Реализация RelayCommand
из инструментария MVVM light. При использовании WPF/.NET реализация переносит методы и события, открытые с помощью CommandManager
. Это будет означать, что эти команды работают автоматически в большинстве ситуаций (где пользовательский интерфейс изменяется или сфокусированный элемент изменяется). Но в некоторых случаях, таких как этот, вам необходимо вручную принудительно выполнить команду для повторного запроса. Правильный способ сделать это с помощью этой библиотеки - вызвать метод RaiseCanExecuteChanged()
на RelayCommand
.
Ответ 2
Это так важно и легко пропустить, я повторяю то, что @Samir сказал в комментарии. Г-н Лоран Бугнион написал в blog:
Однако в WPF 4 и WPF 4.5 есть улов: CommandManager перестанет работать после обновления MVVM Light до V5. Что вы заметите, так это то, что ваши элементы пользовательского интерфейса (кнопки и т.д.) Перестанут быть отключенными/включенными, когда делегат RelayCommands CanExecute возвращает false.
Если вы спешите, вот исправление: в любом классе, который использует RelayCommand, замените строку:
using GalaSoft.MvvmLight.Command;
с:
using GalaSoft.MvvmLight.CommandWpf;
Ответ 3
Вы можете попробовать с CommandManager.InvalidateRequerySuggested.
Во всяком случае, это не помогало мне иногда в прошлом. Для меня лучшим решением оказалось привязать логическое свойство к свойству зависимости Button.IsEnabled
.
В вашем случае что-то вроде
IsEnabled={Binding IsTestrunInProgress}
Ответ 4
Проблема в том, что свойство ICommand TestrunStartCommand всегда возвращает новый объект команды всякий раз, когда к нему обращаются.
Простое исправление - создать объект ICommand один раз и использовать его снова и снова.
private ICommand _testRunCommand = null;
public ICommand TestrunStartCommand
{
get
{
return _testRunCommand ?? (_testRunCommand = new RelayCommand(TestrunStartExecute, () => !IsTestrunInProgress));
}
}
Это было довольно простое исправление, и это сработало для меня.