WPF - Come forzare un comando a rivalutare 'CanExecute' tramite i suoi CommandBindings


130

Ho un Menudove ognuno MenuItemnella gerarchia ha la sua Commandproprietà impostata su un RoutedCommandche ho definito. Il associato CommandBindingfornisce un callback per la valutazione di CanExecutequale controlla lo stato abilitato di ciascuno MenuItem.

Questo funziona quasi . Le voci di menu inizialmente presentano gli stati abilitati e disabilitati corretti. Tuttavia, quando i dati utilizzati dal mio CanExecutecallback cambiano, ho bisogno del comando per richiedere nuovamente un risultato dal mio callback affinché questo nuovo stato si rifletta nell'interfaccia utente.

Non sembra esserci alcun metodo pubblico su RoutedCommando CommandBindingper questo.

Si noti che il callback viene utilizzato di nuovo quando si fa clic o si digita il controllo (suppongo che sia attivato sull'input perché il passaggio del mouse non provoca l'aggiornamento).

Risposte:


172

Non il più grazioso del libro, ma puoi usare il CommandManager per invalidare tutti i comandi:

CommandManager.InvalidateRequerySuggested();

Vedi maggiori informazioni su MSDN


1
Grazie ha funzionato bene. C'è un leggero ritardo nell'interfaccia utente, ma non sono troppo preoccupato per questo. Inoltre, ho votato immediatamente la tua risposta, poi ho ripreso il voto per vedere se funzionava. Ora che funziona, non posso applicare nuovamente il voto. Non sono sicuro del perché SO abbia questa regola in atto.
Drew Noakes,

5
Ho modificato la tua risposta per applicare nuovamente il mio voto. Non ho cambiato nulla nella modifica. Grazie ancora.
Drew Noakes,

Ho avuto lo stesso problema quando stavo cambiando il contenuto di un Texbox dal code-behind. Se lo modifichi a mano funzionerebbe. In questa app, il texbox veniva modificato da un controllo che si apriva e, quando lo salvavi, cambiava la proprietà Texbox.Text. Questo ha risolto il problema! Grazie @Arcturus
Dzyann,

10
Basta notare da un'altra risposta ( stackoverflow.com/questions/783104/refresh-wpf-command ) "deve essere chiamato sul thread dell'interfaccia utente"
Samvel Siradeghyan,

84

Per chiunque lo incontri più tardi; Se ti capita di usare MVVM e Prism, l' DelegateCommandimplementazione di Prism di ICommandfornisce a.RaiseCanExecuteChanged() metodo per farlo.


12
Questo modello si trova anche in altre librerie MVVM, ad esempio MVVM Light.
Peter Lillevold,

2
A differenza di Prism, il codice sorgente di MVVM Light v5 indica RaiseCanExecuteChanged() semplicemente le sue chiamate CommandManager.InvalidateRequerySuggested().
Peter,

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

((RelayCommand)MyCommand).RaiseCanExecuteChanged();ha funzionato per me, usando GalaSoft.MvvmLight.Command - MA dopo essere passato a CommandWPF, ha funzionato senza la necessità di chiamare nulla. Grazie @ fuchs777
Robin Bennett,

1
Cosa succede se non si utilizza una libreria di terze parti?
Vidar,

28

Non ho potuto usare CommandManager.InvalidateRequerySuggested(); perché stavo ottenendo un grande successo.

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

3
Solo una FYI che ho commentato CommandManager.RequerySuggested + = value; Stavo ottenendo una valutazione pressoché costante / ciclica del mio codice CanExecute per qualche motivo. Altrimenti la soluzione ha funzionato come previsto. Grazie!
robaudas,

16

Se hai implementato la tua classe che implementa ICommand, puoi perdere molti degli aggiornamenti automatici dello stato costringendoti a fare affidamento sull'aggiornamento manuale più del necessario. Può anche rompersi InvalidateRequerySuggested(). Il problema è che una semplice ICommandimplementazione non riesce a collegare il nuovo comando aCommandManager .

La soluzione è utilizzare quanto segue:

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

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

In questo modo gli abbonati si collegano CommandManagerpiuttosto che alla tua classe e possono partecipare correttamente alle modifiche dello stato del comando.


2
Semplice, al punto, e consente alle persone di avere il controllo sulle implementazioni di ICommand.
Akoi Meexx,

2

Ho implementato una soluzione per gestire la dipendenza dalle proprietà dai comandi, qui il link https://stackoverflow.com/a/30394333/1716620

grazie a ciò finirai 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 }
 );

-3

Questo è ciò che ha funzionato per me: metti CanExecute prima del comando in XAML.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.