Laden...

Wieso wird Eventhandler mehrfach aufgerufen obwohl ein Property des Objektes ein mal geändert wird?

Erstellt von wciibb vor 3 Jahren Letzter Beitrag vor 3 Jahren 624 Views
W
wciibb Themenstarter:in
5 Beiträge seit 2020
vor 3 Jahren
Wieso wird Eventhandler mehrfach aufgerufen obwohl ein Property des Objektes ein mal geändert wird?

Zu viele Ereignisse im MVVM?

(Code bzw. Projekt im Anhang zu besseren analyse 😉

Nach jahrelanger WinForms-Programmierung beschäftige ich mich mit WPF und natürlich mit
dem MVVM-Pattern.

Zur Entwicklung eines "passenden" MVVM-Models habe ich ein Proof-of-Concept erstellt bei
dem "scheinbar" zu viele Ereignisse auftreten.

Ich habe zwei Klassen im Model. Task und Todolist. Die Todolist enthält eine
ObservableCollection<Task>. Das ViewModel besteht aus den Klassen TaskViewModel und
TodolistViewModel. Diese werden in der GUI an ein Listview gebunden.

Mein Ziel ist es dass Änderungen die am Model durchgeführt werden, über das ViewModel an
die GUI(View) weitergereicht werden. Aus diesem Grund habe ich das Ereignis
INotifyPropertyChanged implementiert. Und zwar sowohl in den Model-Klassen als auch in
den ViewModel-Klassen. Das Ziel ist dass eine Änderung am Task-Objekt, zB. der Name, in
der Listview angezeigt werden. Dazu abonniert die TaskViewModel-Klasse im Konstruktor das
PropertyChanged-Event des zugrunde liegenden Task-Objektes und löst seinerseits das
PropertyChangedEvent aus.

Auf Grund von Faulheit hatte ich im ursprünglichen Entwurf im OnPropertyChange-Eventhandler
der TaskViewModel-Klasse nicht(!) auf PropertyChanged == null geprüft. Prompt erhielt ich bei
der Ausführung eine null-Execption, was mich wunderte. Ich kann mir momentan nicht erklären
warum.

Also implementierte ich den Test auf null und baute noch einen Debug.WriteLine ein. Nun
funktioniert die PoC-App wie sie soll. Aber jetzt kommt das seltsame:

Der OnPropertyChange-Eventhandler wird scheinbar öfter aufgerufen als ich es mir erklären kann.
Ich kann nicht erkennen weshalb, deshalb hoffe ich dass hier jemand den Zusammenhang erkennt und
mir dazu einen Tip geben kann.

Wenn man die App startet dann wird ein Modell mit drei Tasks erstellt. Klickt man nun auf den Button
"Change 3rd item" wird die Name-Property des Task-Models geändert. Im Debug-Output kann man sehen das
der Eventhandler des TaskViewModels genau einmal aufgerufen wird. Wenn man die App neu startet, oder das
Model neu generieren läßt mit Hilfe des entsprechenden Buttons und nun nicht sofort das dritte Item
ändert, sondern zunächst mit dem Button Add item zwei neue Task in der Liste erzeugt. Nun ändert man
die Name-Property des dritten Elements und auf einmal wird der Eventhandler dreimal aufgerufen.
Scheinbar hat die Anzahl der Eventhandleraufrufe irgendwas mit der Anzahl der Elemente in der Observable
List zu tun, aber ich weiß nicht was. Zumal die Gesamtzahl der Items keine Rolle zu spielen scheint,
da ja noch zwei Items darüber stehen.

Noch interessanter scheint es zu werden wenn man mit Hilfe von Add item und Delete item ein wenig
herum spielt sodass am Ende wieder nur drei Items in der Liste sind - wie beim Start - und dann wieder
auf Change 3rd item klickt so wird der Handler wieder dreimal aufgerufen und nicht nur einmal wie am
Anfang.

Ich verstehen momentan absolut nicht was dahinter steckt. Hoffentlich kann mir da jemand einen Tip geben
oder den Grund nennen? Interessant ist auch dass ich dieses "Verhalten" ohne die "vergessene" null Prüfung
nie bemerkt hätte.

Ich zähl auf Euch 😃

H
48 Beiträge seit 2020
vor 3 Jahren

was ist deine frage??

W
wciibb Themenstarter:in
5 Beiträge seit 2020
vor 3 Jahren

Die Frage ist, wieso wird der Eventhandler mehrfach aufgerufen obwohl nur eine Property eines Objektes ein einziges mal geändert wird. Er sollte dann doch auch nur einmal aufgerufen werden, oder?

F
10.010 Beiträge seit 2004
vor 3 Jahren

Was machst Du denn in OnCollectionChanged??

W
wciibb Themenstarter:in
5 Beiträge seit 2020
vor 3 Jahren

In OnCollectionChange wird die Collection des TodolistViewModels, an welche die View bindet, neu erstellt, sobald sich in der Collection des TodolistModels etwas ändert.

Das ist erst mal quick and dirty mit .clear() und komplett neuem Aufbau realisiert anstatt nur die Änderungen an sich zu berücksichtigen.

F
10.010 Beiträge seit 2004
vor 3 Jahren

Schon klar, aber Du erzeugst hier haufenweise neue TaskVm für DEN SELBEN Task (unglückliche Namenswahl) und registrierst jedesmal ein neues VM.

Da du beim Clear die EventHandler nicht entfernst, bleiben die VMs am leben und auch wenn sie nicht mehr in der OC sind, feuern sie das event.

W
wciibb Themenstarter:in
5 Beiträge seit 2020
vor 3 Jahren

Ja ich gebe zu die Namenswahl war nicht so glücklich.

Kannst Du mir genau sagen wo ich die erzeuge? Im CollectionChanged-Handler wo ich erst .clear() aufrufe und dann den ganzen Collectioninhalt neu erzeuge?

F
10.010 Beiträge seit 2004
vor 3 Jahren

Genau, hier legst du neue TaskViewModel an, und registrierst die events.

Vorher schmeißt du die VMs aus der collection, aber da sie über den EventHandler weiter verbunden sind, werden sie nicht weggeräumt.
Also hast du mit jedem Aufruf ein VM mehr am Eventhandler hängen.

Eventhandler sind die "beliebteste" stelle für MemmoryLeaks.

Ich "missbrauche" bei classen die Eventhandler registrieren das IDisposable pattern, so das ich angehalten bleibe die Events zu deregistrieren.

hab das mal eben reingehackt.

W
wciibb Themenstarter:in
5 Beiträge seit 2020
vor 3 Jahren

Ah, super danke. Gut der Mann. 👍

Da hatte ich das mit dem Handling der geänderten Collection also, weil ich es quick and dirty gemacht habe verschlimmbessert.

Ich hatte mir dabei gedacht, werfe ich die Items alle einfach weg und erstelle die ganze Collection neu anstatt mit e.NewItems und e.OldItems "ordentlich" den Inhalt der beiden Collections zu synchronisieren.

Super, vielen Dank nochmal.

F
10.010 Beiträge seit 2004
vor 3 Jahren

Du hättest es auch viel einfacher haben können wenn du MVVM richtig implementiert hättest.

In MainWindow.xaml.cs hat der Code ausser evtl dem zuweisen des TodolistViewModel eigentlich nichts zu suchen.

Das TodolistViewModel hat das Add und Remove zu machen, und dann muss da auch nichts synchronisiert werden.

5.658 Beiträge seit 2006
vor 3 Jahren

Siehe dazu auch: [Artikel] MVVM und DataBinding

Und bitte halte dich zukünftig an [Hinweis] Wie poste ich richtig?, besonders Punkt 4.1 und 5

Weeks of programming can save you hours of planning