WPF - jak wymusić na poleceniu ponowną ocenę „CanExecute” za pośrednictwem jego CommandBindings


130

Mam Menugdzie każdy MenuItemw hierarchii ma swoją Commandwłaściwość ustawioną na RoutedCommandzdefiniowaną przeze mnie. Skojarzony CommandBindingzapewnia wywołanie zwrotne do oceny, CanExecutektóre kontroluje stan włączenia każdego MenuItem.

To prawie działa. Pozycje menu początkowo pojawiają się z prawidłowymi stanami włączenia i wyłączenia. Jednak gdy dane, których CanExecuteużywa moje wywołanie zwrotne, ulegną zmianie, potrzebuję polecenia, aby ponownie zażądać wyniku z mojego wywołania zwrotnego, aby ten nowy stan został odzwierciedlony w interfejsie użytkownika.

Tam nie wydają się być wszelkie metody publiczne na RoutedCommandlub CommandBindingza to.

Zwróć uwagę, że wywołanie zwrotne jest używane ponownie, gdy klikam lub piszę w kontrolce (myślę, że jest wyzwalane przy wprowadzaniu danych, ponieważ najechanie kursorem myszy nie powoduje odświeżenia).

Odpowiedzi:


172

Nie jest to najładniejsze w książce, ale możesz użyć CommandManager do unieważnienia wszystkich poleceń:

CommandManager.InvalidateRequerySuggested();

Zobacz więcej informacji w witrynie MSDN


1
Dzięki, to działało dobrze. Występuje niewielkie opóźnienie w interfejsie użytkownika, ale nie martwię się o to. Ponadto natychmiast zagłosowałem pozytywnie na twoją odpowiedź, a potem cofnąłem głosowanie, aby sprawdzić, czy zadziałało. Teraz, gdy to działa, nie mogę ponownie zagłosować. Nie jestem pewien, dlaczego SO stosuje tę zasadę.
Drew Noakes

5
Zredagowałem Twoją odpowiedź, aby ponownie oddać mój głos. Nic nie zmieniłem w edycji. Dzięki jeszcze raz.
Drew Noakes

Miałem ten sam problem, gdy zmieniałem zawartość Texboksa zza kodu. Jeśli edytujesz to ręcznie, zadziała. W tej aplikacji mieli edytowaną skrzynkę tekstową przez kontrolkę, która wyskakuje, a po zapisaniu wyskakującego okienka zmieniło to właściwość Texbox.Text. To rozwiązało problem! Dzięki @Arcturus
Dzyann

10
Zwróć uwagę na inną odpowiedź ( stackoverflow.com/questions/783104/refresh-wpf-command ) „należy ją wywołać w wątku interfejsu użytkownika”
Samvel Siradeghyan

83

Dla każdego, kto zetknie się z tym później; Jeśli używasz MVVM i Prism, DelegateCommandimplementacja Prism ICommandzapewnia .RaiseCanExecuteChanged()metodę, aby to zrobić.


12
Ten wzorzec można również znaleźć w innych bibliotekach MVVM, np. MVVM Light.
Peter Lillevold

2
W przeciwieństwie do Prism, kod źródłowy MVVM Light v5 wskazuje na jego RaiseCanExecuteChanged() proste wywołania CommandManager.InvalidateRequerySuggested().
Peter

4
uwaga dodatkowa do MVVM Light w WPF, musisz użyć przestrzeni nazw GalaSoft.MvvmLight.CommandWpf, ponieważ GalaSoft.MvvmLight.Command spowoduje problemy mvvmlight.net/installing/changes#v5_0_2
fuchs777

((RelayCommand)MyCommand).RaiseCanExecuteChanged();działało dla mnie, używając GalaSoft.MvvmLight.Command - ALE po zmianie na CommandWPFdziałało bez potrzeby dzwonienia do czegokolwiek. Dzięki @ fuchs777
Robin Bennett

1
A co jeśli nie korzystasz z biblioteki innej firmy?
Vidar

28

Nie mogłem użyć, CommandManager.InvalidateRequerySuggested();ponieważ dostałem hit wydajności.

Użyłem polecenia delegowania MVVM Helpera , które wygląda jak poniżej (trochę go poprawiłem dla naszego wymagania). musisz zadzwonić command.RaiseCanExecuteChanged()z 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
Tylko wiadomość do Twojej wiadomości Skomentowałem CommandManager.RequerySuggested + = wartość; Z jakiegoś powodu uzyskiwałem niemal stałą / zapętloną ocenę mojego kodu CanExecute. W przeciwnym razie rozwiązanie działało zgodnie z oczekiwaniami. Dzięki!
robaudas

16

Jeśli wyrzuciłeś swoją własną klasę, która implementuje ICommand, możesz stracić wiele automatycznych aktualizacji statusu, zmuszając cię do polegania na ręcznym odświeżaniu bardziej niż powinno. Może też pęknąć InvalidateRequerySuggested(). Problem polega na tym, że prosta ICommandimplementacja nie łączy nowego polecenia z plikiem CommandManager.

Rozwiązaniem jest użycie:

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

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

W ten sposób subskrybenci przyłączają się do CommandManagerTwojej klasy, a nie do Twojej klasy i mogą odpowiednio uczestniczyć w zmianach statusu poleceń.


2
Prosty, na temat i pozwala ludziom mieć kontrolę nad ich implementacjami ICommand.
Akoi Meexx

2

Zaimplementowałem rozwiązanie do obsługi zależności właściwości od poleceń, tutaj link https://stackoverflow.com/a/30394333/1716620

dzięki temu otrzymasz takie polecenie:

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

Oto, co zadziałało dla mnie: umieść CanExecute przed poleceniem w kodzie XAML.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.