Question
Quelqu'un sait comment je peux forcer Exemple de code derrière: CanExecute
à obtenir appelé à une commande personnalisée (Josh Smith <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; }
La solution
Autres conseils
Je connaissais CommandManager.InvalidateRequerySuggested () il y a longtemps, et l'a utilisé, mais ça ne marchait pas pour moi parfois. J'ai finalement compris pourquoi ce fut le cas! Même si elle ne jette pas comme d'autres actions, vous devez appeler sur le thread principal.
L'appeler sur un thread d'arrière-plan semble fonctionner, mais parfois quitter l'interface utilisateur désactivé. J'espère vraiment que cela aide quelqu'un, et les enregistre les heures que je viens de perdre.
Une solution pour cela lie IsEnabled
à une propriété:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
et puis mettre en œuvre cette propriété dans votre ViewModel. Cela rend également un peu plus facile pour le UnitTesting de travailler avec les propriétés plutôt que des commandes pour voir si la commande peut être exécutée à un certain moment.
Personnellement, je trouve plus pratique.
Probablement cette variante vous convient:
public interface IRelayCommand : ICommand
{
void UpdateCanExecuteState();
}
Mise en œuvre:
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();
}
}
Utilisation simple:
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}"/>
Merci les gars pour les conseils. Voici un peu de code sur la façon de maréchal cet appel d'un fil BG au fil de l'interface utilisateur:
private SynchronizationContext syncCtx; // member variable
Dans le constructeur:
syncCtx = SynchronizationContext.Current;
Sur le fil d'arrière-plan, pour déclencher le requery:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
L'espoir qui aide.
- Michael
Pour mettre à jour un seul GalaSoft.MvvmLight.CommandWpf.RelayCommand vous pouvez utiliser
mycommand.RaiseCanExecuteChanged();
et pour moi, je l'ai créé une méthode d'extension:
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(); }));
}
}