2009-08-27 19 views
111

j'ai un Menu où chaque MenuItem dans la hiérarchie a sa Command propriété est définie sur un RoutedCommand que je l'ai défini. Le CommandBinding associé fournit un rappel pour l'évaluation de CanExecute qui contrôle l'état activé de chaque MenuItem.WPF - Comment forcer une commande à réévaluer « CanExecute » par ses CommandBindings

Cette presque fonctionne. Les éléments de menu arrivent initialement avec les bons états activés et désactivés. Toutefois, lorsque les données que mon rappel CanExecute utilise change, j'ai besoin de la commande pour re-demander un résultat de mon rappel afin que ce nouvel état soit reflété dans l'interface utilisateur. Il ne semble y avoir aucune méthode publique sur RoutedCommand ou CommandBinding pour cela.

Notez que le rappel est utilisé à nouveau lorsque je clique ou tape dans le contrôle (je suppose qu'il est déclenché en entrée car le survol ne provoque pas l'actualisation).

Répondre

149

Pas la plus jolie dans le livre, mais vous pouvez utiliser le CommandManager pour invalider tous CommandBinding:

CommandManager.InvalidateRequerySuggested(); 

Voir plus d'informations sur MSDN

+0

Merci cela a très bien fonctionné. Il y a un léger retard dans l'interface utilisateur, mais je ne suis pas trop inquiet à ce sujet.J'ai également voté votre réponse immédiatement, puis j'ai voté pour voir si cela fonctionnait. Maintenant que cela fonctionne, je ne peux plus réappliquer le vote. Je ne sais pas pourquoi SO a cette règle en place. –

+4

J'ai édité votre réponse afin de réappliquer mon vote. Je n'ai rien changé dans le montage. Merci encore. –

+0

haha ​​ok :) merci! – Arcturus

72

Pour toute personne qui vient à travers plus tard; Si vous utilisez MVVM et Prism, l'implémentation DelegateCommand de Prism de ICommand fournit une méthode .RaiseCanExecuteChanged() pour ce faire.

+10

Ce modèle se trouve également dans d'autres bibliothèques MVVM, par exemple MVVM Light. –

+1

Contrairement à Prism, le code source de MVVM Light v5 indique que 'RaiseCanExecuteChanged()' appelle simplement 'CommandManager.InvalidateRequerySuggested()'. – Peter

+3

une note de côté à MVVM Light dans WPF, vous devez utiliser l'espace de noms GalaSoft.MvvmLight.CommandWpf puisque GalaSoft.MvvmLight.Command va causer des problèmes http://www.mvvmlight.net/installing/changes#v5_0_2 – fuchs777

21

Je ne pouvais pas utiliser CommandManager.InvalidateRequerySuggested(); parce que je recevais un coup de performance.

J'ai utilisé la commande Delegating de MVVM Helper, qui ressemble à ci-dessous (je l'ai un peu modifié pour notre req). vous devez appeler command.RaiseCanExecuteChanged() de VM

public event EventHandler CanExecuteChanged 
{ 
    add 
    { 
     _internalCanExecuteChanged += value; 
     CommandManager.RequerySuggested += value; 
    } 
    remove 
    { 
     _internalCanExecuteChanged -= value; 
     CommandManager.RequerySuggested -= value; 
    } 
} 

/// <summary> 
/// This method can be used to raise the CanExecuteChanged handler. 
/// This will force WPF to re-query the status of this command directly. 
/// </summary> 
public void RaiseCanExecuteChanged() 
{ 
    if (canExecute != null) 
     OnCanExecuteChanged(); 
} 

/// <summary> 
/// This method is used to walk the delegate chain and well WPF that 
/// our command execution status has changed. 
/// </summary> 
protected virtual void OnCanExecuteChanged() 
{ 
    EventHandler eCanExecuteChanged = _internalCanExecuteChanged; 
    if (eCanExecuteChanged != null) 
     eCanExecuteChanged(this, EventArgs.Empty); 
} 
+0

Cela a mieux fonctionné pour moi. Merci. –

+2

Juste un FYI j'ai commenté CommandManager.RequerySuggested + = valeur; J'obtenais une évaluation quasi constante/en boucle de mon code CanExecute pour une raison quelconque. Sinon, la solution a fonctionné comme prévu. Merci! – robaudas

2

J'ai mis en place une solution pour gérer la dépendance de la propriété sur les commandes, voici le lien https://stackoverflow.com/a/30394333/1716620

grâce à vous finirez par avoir une commande comme ceci:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this, 
    //execute 
    () => { 
     Console.Write("EXECUTED"); 
    }, 
    //can execute 
    () => { 
     Console.Write("Checking Validity"); 
     return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5; 
    }, 
    //properties to watch 
    (p) => new { p.PropertyX, p.PropertyY } 
); 
4

Si vous avez roulé votre propre classe qui implémente ICommand vous pouvez perdre beaucoup de mises à jour automatiques de statut qui vous oblige à compter sur rafraîchissante manuel plus devrait être nécessaire. Il peut également casser InvalidateRequerySuggested(). Le problème est qu'une simple implémentation ICommand ne parvient pas à lier la nouvelle commande au CommandManager.

La solution est d'utiliser les éléments suivants:

public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    public void RaiseCanExecuteChanged() 
    { 
     CommandManager.InvalidateRequerySuggested(); 
    } 

Ce abonnés façon à joindre CommandManager plutôt que votre classe et peuvent bien participer à des changements d'état de la commande.

+0

Simple, direct, et permet aux gens d'avoir le contrôle sur leurs implémentations ICommand. –