Команды в WPF обновление
кто-нибудь знает как я могу заставить CanExecute
чтобы получить вызов по пользовательской команде (Josh Smith's RelayCommand
)?
как правило, CanExecute
вызывается всякий раз, когда взаимодействие происходит в пользовательском интерфейсе. Если я нажимаю что-то, мои команды обновляются.
у меня ситуация, когда условие CanExecute
включается / выключается таймером за кулисами. Потому что это не зависит от взаимодействия с пользователем, CanExecute
не вызывается, пока пользователь не взаимодействует с ПОЛЬЗОВАТЕЛЬСКИЙ ИНТЕРФЕЙС. Конечным результатом является то, что my Button
остается включить/отключить, пока пользователь не нажмет на него. После щелчка он обновляется правильно. Иногда Button
отображается включено, но когда пользователь нажимает, он меняется на Отключено вместо запуска.
как я могу принудительно обновить код, когда таймер изменяет свойство, которое влияет на CanExecute
? Я пытался стрелять!--10--> (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. Это также облегчает работу с свойствами, а не с командами, чтобы увидеть, может ли команда быть выполнена в определенный момент времени.
Я лично считаю это более удобным.
возможно, этот вариант вам подойдет:
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 в поток UI:
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(); }));
}
}