WPF Profiwissen benötigt - CanExecuteChanged

Dey

Banned
Registriert
Mai 2005
Beiträge
1.925
Ich habe schon etliche Male selbst implementierte RelayCommands benutzt, ohne dass ich mich der Frage bewusst wurde, wofür das ICommand-Interface das CanExecuteChanged-Event definiert.

In den üblichen Beispielen, die im Internet zu finden sind, wird stets ein spezieller Event-Accessor definiert, der den EventHandler-Delegate mit dem Command-System der WPF verknüpft (CommandManager.RequerySuggested += value).

Wenn das WPF-Command-System das RequerySuggested-Ereignis auslöst, wird in unserem RelayCommand der entsprechende EventHandler-Code ausgeführt.
Jetzt frage ich mich aber, wo und wann das CanExecuteChanged-Event ausgelöst wird? Es kann nur unsere RelayCommand-Klasse selbst auslösen, aber aus welchem Grund soll sie das tun?

Ich habe den Eindruck, dass die WPF das Event CanExecuteChanged aus dem ICommand-Interface „missbraucht“, um sich in das WPF-Command-System einzuhängen. In der Regel gehen Events von der Klasse aus, die sie definieren. Hier ist das andersherum.

Beim Debuggen ist mir aufgefallen, dass das Event niemals ausgelöst wird. Aber dadurch, dass wir (der Programmierer eines RelayCommands) einen Event Accessor schreiben, der einen Ereignishandler mit dem WPF-Command-System verknüpft, finden bei der Ausführung des Programmes interne Prozesse statt, die bewirken, dass das WPF-Command-System unsere Klasse kennt.

Ich habe zum Beispiel festgestellt, dass ein Button, der ein Binding an ein RelayCommand besitzt, beim Initialisieren einen EventHandler für eine Methode „OnCanExecuteChanged“ registriert. Die Methode stammt offensichtlich aus der Klasse ButtonBase. Wenn man sich in der Methode „CanExecute“ (im ViewModel) den Stack anzeigen lässt, liegt darüber hinaus einige Ebenen höher eine Methode „Update CanExecuteChanged“, die ebenfalls innerhalb von ButtonBase ausgelöst wird.

Registrieren wir (durch unsere Event Accessor-Definition) den Button als zu benachrichtigendes Objekt für das WPF-Command-System? Sodass der Button bei einer Zustandsänderung eine Benachrichtigung erhält, und er erst dann wiederum „CanExecute“ auf seinem RelayCommand aufruft?

Aber wieso registriert sich der Button dann nicht selbst beim WPF-Command-System? Um Ressourcen zu sparen?
 
Zuletzt bearbeitet:
Dey schrieb:
Ich habe schon etliche Male selbst implementierte RelayCommands benutzt, ohne dass ich mich der Frage bewusst wurde, wofür das ICommand-Interface das CanExecuteChanged-Event definiert.

In den üblichen Beispielen, die im Internet zu finden sind, wird stets ein spezieller Event-Accessor definiert, der den EventHandler-Delegate mit dem Command-System der WPF verknüpft (CommandManager.RequerySuggested += value).

WPF macht massiv gebrauch von RoutedCommands. Dabei weiß jeder Command, wann er ausgeführt werden kann und wann nicht. Das schöne dabei ist: kann ein Command nicht ausgeführt werden, also gibt das entsprechende CanExecute Predicate false zurück, wird die verknüpfte Control automatisch deaktiviert (Bsp. Speichern Button, wenn es nichts zu speichern gibt, ist der in WPF Apps deaktiviert). Umgesetzt wird das ganze intern über den CommandManager.

Wenn du nun mit einem DelegateCommand/RelayCommand daher kommst, weiß WPF nichts von deinem benutzerdefiniertem Command (DelegateCommand != RoutedCommand). Ein ziemlicher netter "Work-Around" ist nun einfach das CanExecuteChanged-Event (dafür ist es da) in den CommandManager zu "hooken". Daher verwendet man CommandManager.RequerySuggested += value bzw. -= value. Ohne würde dein Command auch funktionieren, du verlierst aber das Verhalten eines RoutedCommands.

Dey schrieb:
Ich habe den Eindruck, dass die WPF das Event CanExecuteChanged aus dem ICommand-Interface „missbraucht“, um sich in das WPF-Command-System einzuhängen. In der Regel gehen Events von der Klasse aus, die sie definieren. Hier ist das andersherum.

Siehe oben. Es ist nicht WPF was es missbraucht, sondern du, weil du einfach eine DelegateCommand Klasse kopiert hast, ohne deren Sinn zu verstehen :D

Dey schrieb:
Aber wieso registriert sich der Button dann nicht selbst beim WPF-Command-System? Um Ressourcen zu sparen?

Button : ButtonBase, HyperlinkButton : ButtonBase, RepeatButton : ButtonBase, ToggleButton : ButtonBase...

Noch Fragen? :)
 
Zuletzt bearbeitet:
Zurück
Oben