Domanda
Qualcuno sa come posso forzare CanExecute
ottenere chiamato su un comando personalizzato (Josh Smith di RelayCommand
)?
Tipicamente, CanExecute
è chiamato quando si verifica l'interazione sull'interfaccia utente. Se clicco qualcosa, i miei comandi vengono aggiornati.
Ho una situazione in cui la condizione per CanExecute
è sempre acceso / spento da un timer dietro le quinte. Poiché questo non è guidato dall'interazione di utente, CanExecute
non viene chiamato fino a quando l'utente interagisce con l'interfaccia utente. Il risultato finale è che il mio Button
rimane abilitato / disabilitato fino a quando l'utente fa clic su di esso. Dopo il click, che viene aggiornato correttamente. A volte il Button
appare abilitato, ma quando l'utente fa clic cambia per portatori di handicap, invece di cottura.
Come posso forzare un aggiornamento nel codice quando il timer modifica la proprietà che colpisce CanExecute
? Ho provato a sparare PropertyChanged
(INotifyPropertyChanged
) sulla proprietà che colpisce CanExecute
, ma che non ha aiutato.
Esempio XAML:
<Button Content="Button" Command="{Binding Cmd}"/>
Esempio di codice dietro:
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; }
Altri suggerimenti
ero consapevole di CommandManager.InvalidateRequerySuggested () molto tempo fa, e l'ho usato, ma non funzionava per me a volte. Ho finalmente capito perché questo era il caso! Anche se non si butta come alcune altre azioni, si deve chiamare sul thread principale.
La chiamata su un thread in background apparirà a lavorare, ma a volte lasciare l'interfaccia utente disabilitato. Spero davvero che questo aiuta qualcuno, e li salva le ore Ho appena sprecato.
Una soluzione per questo è vincolante IsEnabled
a una proprietà:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
e quindi implementare questa proprietà nel vostro ViewModel. Questo rende anche un po 'più facile per l'unit testing di lavorare con le proprietà anziché comandi per vedere se il comando può essere eseguito in un determinato punto del tempo.
Io, personalmente, lo trovo più comodo.
Probabilmente questa variante fa per voi:
public interface IRelayCommand : ICommand
{
void UpdateCanExecuteState();
}
Implementazione:
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();
}
}
Utilizzo semplice:
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}"/>
Grazie ragazzi per le punte. Ecco un po 'di codice su come schierare quella chiamata da un thread BG al thread UI:
private SynchronizationContext syncCtx; // member variable
Nel costruttore:
syncCtx = SynchronizationContext.Current;
Al thread in background, per far scattare l'requery:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
La speranza che aiuta.
- Michael
Per aggiornare un solo GalaSoft.MvvmLight.CommandWpf.RelayCommand si potrebbe usare
mycommand.RaiseCanExecuteChanged();
e per me che ho creato un metodo di estensione:
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(); }));
}
}