Команды в WPF обновление
кто-нибудь знает как я могу заставить CanExecute чтобы вызвать пользовательскую команду (Josh Smith's RelayCommand)?
как правило, CanExecute вызывается всякий раз, когда взаимодействие происходит в пользовательском интерфейсе. Если я нажимаю что-то, мои команды обновляются.
у меня ситуация, когда условие CanExecute включается / выключается таймером за кулисами. Потому что это не зависит от взаимодействия с пользователем, CanExecute не вызывается, пока пользователь не взаимодействует с ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС. Конечным результатом является то, что мой Button остается включить/отключить, пока пользователь не нажмет на него. После щелчка, он обновляется правильно. Иногда Button отображается включен, но когда пользователь нажимает на него меняется на Отключено вместо запуска.
как я могу принудительно обновить код, когда таймер изменяет свойство, которое влияет на CanExecute? Я пытался стрелять PropertyChanged (INotifyPropertyChanged) на свойство, которое влияет CanExecute, но это не помогло.
пример XAML:
<Button Content="Button" Command="{Binding Cmd}"/>
пример кода:
private ICommand m_cmd;
public ICommand Cmd
{
if (m_cmd == null)
m_cmd = new RelayCommand(
(param) => Process(),
(param) => EnableButton);
return m_cmd;
}
// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
6 ответов:
Я был в курсе CommandManager.InvalidateRequerySuggested () давно, и использовал его, но он не работал для меня иногда. Я, наконец, понял, почему это было так! Даже если он не бросает, как некоторые другие действия, вы должны вызвать его в главном потоке.
вызов его в фоновом потоке будет работать, но иногда оставьте пользовательский интерфейс отключенным. Я действительно надеюсь, что это поможет кому-то, и спасет их от часов, которые я просто потратил впустую.
обходной путь для этого является обязательным
IsEnabledсвойства:<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>а затем реализовать это свойство в ViewModel. Это также делает его немного легче для UnitTesting для работы со свойствами, а не команды, чтобы увидеть, если команда может быть выполнена в определенный момент времени.
Я лично считаю это более удобным.
возможно, этот вариант вам подойдет:
public interface IRelayCommand : ICommand { void UpdateCanExecuteState(); }реализация:
public class RelayCommand : IRelayCommand { public event EventHandler CanExecuteChanged; readonly Predicate<Object> _canExecute = null; readonly Action<Object> _executeAction = null; public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null) { _canExecute = canExecute; _executeAction = executeAction; } public bool CanExecute(object parameter) { if (_canExecute != null) return _canExecute(parameter); return true; } public void UpdateCanExecuteState() { if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs()); } public void Execute(object parameter) { if (_executeAction != null) _executeAction(parameter); UpdateCanExecuteState(); } }Через просто:
public IRelayCommand EditCommand { get; protected set; } ... EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted); protected override bool CanEditCommandExecuted(object obj) { return SelectedItem != null ; } protected override void EditCommandExecuted(object obj) { // Do something } ... public TEntity SelectedItem { get { return _selectedItem; } set { _selectedItem = value; //Refresh can execute EditCommand.UpdateCanExecuteState(); RaisePropertyChanged(() => SelectedItem); } }XAML:
<Button Content="Edit" Command="{Binding EditCommand}"/>
спасибо ребятам за советы. Вот немного кода о том, как маршалировать этот вызов из потока BG в поток пользовательского интерфейса:
private SynchronizationContext syncCtx; // member variableВ конструкторе:
syncCtx = SynchronizationContext.Current;в фоновом потоке, чтобы вызвать запрос:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );надеюсь, что это поможет.
- Майкл
для обновления только одного GalaSoft.MvvmLight.CommandWpf.RelayCommand вы могли бы использовать
mycommand.RaiseCanExecuteChanged();и для меня, я создал метод расширения:
public static class ExtensionMethods { public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd) { System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); })); } public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd) { System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); })); } }
Comments