2009-08-27 3 views
111

ho un Menu dove ogni MenuItem nella gerarchia ha la proprietà Command impostata su un RoutedCommand ho definito. L'associato CommandBinding fornisce un callback per la valutazione di CanExecute che controlla lo stato abilitato di ogni MenuItem.WPF - Come forzare un comando per rivalutare 'CanExecute' attraverso i suoi CommandBindings

Questo quasi funziona. Le voci di menu inizialmente presentano gli stati abilitati e disabilitati corretti. Tuttavia, quando i dati che la mia callback CanExecute utilizza modifiche, ho bisogno del comando per ri-richiedere un risultato dal mio callback affinché questo nuovo stato si rifletta nell'interfaccia utente.

Non sembrano esserci metodi pubblici su RoutedCommand o CommandBinding per questo.

Si noti che la richiamata viene utilizzata di nuovo quando si fa clic o si digita nel controllo (suppongo che sia attivato in input perché il passaggio del mouse non causa l'aggiornamento).

risposta

149
Non

la più bella del libro, ma è possibile utilizzare il CommandManager di invalidare tutte CommandBinding:

CommandManager.InvalidateRequerySuggested(); 

Tutte le informazioni MSDN

+0

Grazie a questo ha funzionato bene. C'è un leggero ritardo nell'interfaccia utente, ma non ne sono troppo preoccupato.Inoltre, ho votato subito la tua risposta, poi ho ripreso il voto per vedere se ha funzionato. Ora che funziona, non posso ri-applicare nuovamente il voto. Non sono sicuro del perché SO abbia questa regola. –

+4

Ho modificato la tua risposta per riapplicare il mio voto. Non ho cambiato nulla nella modifica. Grazie ancora. –

+0

haha ​​ok :) grazie! – Arcturus

72

Per chi si imbatte in questo più tardi; Se si utilizza MVVM e Prism, l'implementazione di DelegateCommand di Prism ICommand fornisce un metodo .RaiseCanExecuteChanged() per farlo.

+10

Questo modello si trova anche in altre librerie MVVM, per esempio MVVM Light. –

+1

A differenza di Prism, il codice sorgente di MVVM Light v5 indica che 'RaiseCanExecuteChanged()' chiama semplicemente 'CommandManager.InvalidateRequerySuggested()'. – Peter

+3

una nota a margine di MVVM Light in WPF, è necessario utilizzare lo spazio dei nomi GalaSoft.MvvmLight.CommandWpf poiché GalaSoft.MvvmLight.Command causerà problemi http://www.mvvmlight.net/installing/changes#v5_0_2 – fuchs777

21

Non ho potuto utilizzare CommandManager.InvalidateRequerySuggested(); perché stavo ottenendo un successo in termini di prestazioni.

Ho usato il comando di Delegazione MVVM Helper, che appare come di seguito (l'ho ottimizzato un po 'per il nostro req). devi chiamare command.RaiseCanExecuteChanged() da 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

Questo ha funzionato meglio per me. Grazie. –

+2

Solo una FYI ho commentato CommandManager.RequerySuggested + = value; Per qualche ragione stavo ottenendo una valutazione quasi costante/ciclica del mio codice CanExecute. Altrimenti la soluzione ha funzionato come previsto. Grazie! – robaudas

2

Ho implementato una soluzione per gestire le proprietà di dipendenza sui comandi, qui il link https://stackoverflow.com/a/30394333/1716620

grazie a che si finirà per avere un comando come questo:

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

Se si è rotolato la propria classe che implementa ICommand si può perdere un sacco di aggiornamenti automatici di stato ti costringe a fare affidamento su rinfrescante manuale più che dovrebbe essere necessario. Può anche rompere InvalidateRequerySuggested(). Il problema è che una semplice implementazione ICommand non riesce a collegare il nuovo comando allo CommandManager.

La soluzione è quella di utilizzare il seguente: gli abbonati

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

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

questo modo attribuiscono alla CommandManager piuttosto che la vostra classe e possono adeguatamente partecipare a cambiamenti di stato di comando.

+0

Direttamente, al punto, e consente alle persone di avere il controllo sulle loro implementazioni ICommand. –