MahApps.Metro in Verbindung mit der PRISM-Bibliothek (Teil 4: Event-Aggregator)

In den ersten Artikeln wurde der grundsätzliche Aufbau der Solution, das Regionenkonzept und die Erstellung von Modulen beschrieben. In diesem Artikel geht es um die Kommunikation der einzelnen Module untereinander. Für die Kommunikation der einzelnen Module untereinander gibt es unterschiedliche Möglichkeiten. Hier wird nun das EventAggregator-Pattern näher beschrieben. Das Pattern erlaubt es Publishern und Subsribern via Events miteinander zu kommunizieren ohne sich dabei direkt zu referenzieren bzw. Kenntnis voneinander zu haben. Das EventAggregator-Pattern wird häufig in größeren Enterprise-Applikationen eingesetzt und hat zum Ziel das Event-Handling zu zentralisieren bzw. zu vereinfachen.

Wie sich aus dem Namen des Patterns bereits schließen lässt kümmert sich der EventAggregator um die Verarbeitung von Events. Klären wir hier zunächst einmal den Begriff Event. Ein Event ist vereinfacht gesagt eine Nachricht, die von einem Objekt beim Eintreten ein bestimmten Aktion gesendet (raise) wird. Alle Applikationen (von klein bis groß) nutzen Events um anzuzeigen, dass eine bestimmte Aktion oder ein spezieller Programmstatus eingetreten ist. Diese Aktion kann zum einen durch eine Benutzeraktion (z.B. ein Buttonklick) oder durch die Programmlogik (z.B. Eigenschaftsänderung) selbst ausgelöst werden. Das Objekt, welches das Event auslöst wird auch als Event-Sender (Publisher) bezeichnet. Der Event-Sender (Publisher) hat im Normalfall keinerlei Kenntnis über die Objekte oder Methoden (Subscriber), die das jeweilig ausgelöste Event empfangen. Als einfaches Beispiel kann man z.B. den Button mit dem Click-Event oder das PropertyChanged-Event einer Klasse, die die Schnittstelle INotifiyPropertyChanged implementiert, nennen. Diese Art von traditionellem Event-Handling erlaubt also eine mehr oder weniger lose Kopplung zwischen zwei oder mehr Objekten, die auf diese Art und Weise miteinander kommunizieren. Der Prozess des traditionellen Event-Handlings sieht dabei wie folgt aus:

  1. Der Publisher stellt die jeweiligen Events nach außen hin zu Verfügung und entscheidet wann diese ausgelöst werden
  2. Ein oder mehrere Subscriber registrieren sich auf die jeweiligen Publisher-Events
  3. Der Publisher löst die jeweiligen Events aus und informiert somit die Subscriber

Dieses Konzept hat allerdings ein paar Nachteile:

  • Gibt es mehrere Publisher und Subscriber wird der Quellcode ziemlich schnell unübersichtlich und schwer zu debuggen
  • Der Subscriber eines Events muss den Publisher des Events kennen und bezieht sich direkt auf diesen (was also eine enge Kopplung zwischen diesen beiden Objekten nach sich zieht)
  • Diese enge Kopplung erlaubt keinen einfachen Austausch der Publisher- und Subscriber-Objekte

Bei genau diesen Nachteilen setzt nun das EventAggregator-Pattern an. Dabei versucht das Pattern die Einschränkungen bzw. Nachteile des traditionellen Event-Handlings damit zu eliminieren, in dem eine zentrale Stelle für die Veröffentlichung und Registrierung von Events zur Verfügung gestellt wird. Diese zentrale Stelle (wer hätte es gedacht ;-)) ist der EventAggregator. Dabei übernimmt der EventAggregator die Aufgaben der Registrierung (subsribe), Deregistrierung (unsubsribe) und das Veröffentlichen/Auslösen (publish) der Events. Die einzelnen Publisher und Subscriber kennen dabei nur den EventAggregator und haben somit keine Kenntnis voneinander. Dabei kann es mehrere Publisher sowie Subscriber geben (dabei können sich mehrere Subscriber auch auf das gleiche Event registrieren). Das folgende Schaubild soll diesen Zusammenhang verdeutlichen:

PrismMahAppsSample_17PRISM EventAggregator (Quelle: [1])

Das Pattern verfolgt also die folgenden Ziele:

  • Zentrales Handling von Events
  • Vereinfachung bei der Registrierung und Deregistrierung von Events
  • Lose Kopplung zwischen Publisher und Subscriber was eine einfache Austauschbarkeit dieser beiden Objekte ermöglicht
  • Einfaches Hinzufügen von Events

Erstellung eines Events

Um ein neues Event zu erstellen wird eine neue Klasse erzeugt, die von der Basisklasse PubSubEvent erbt. Die Payload gibt hier das Argument an, welches an die Subsciber weiter gereicht wird wenn das Event veröffentlicht wird. Für das Beispiel benötigen wird ein Event, das die StatusBar des Dialogs updatet. In der StatusBar soll lediglich ein Text angezeigt werden, somit benötigen wird ein Event mit einer Payload vom Typ string.

Hinweis: Da solche Events in größeren Applikationen oftmals von verschiedenen Modulen genutzt werden, werde diese an einer zentralen definiert. In dem gezeigten Beispiel ist dies das Projekt PrismMahAppsSample.Infrastructure:

PrismMahAppsSample_16

Veröffentlichung von Events

Um ein solches Event jetzt zu veröffentlichen (publish) muss es zunächst über den EventAggregator ermittelt und dann die Publish-Methode des Events aufgerufen werden. Innerhalb des Beispielprojekts ist der EventAggregator eine öffentliche Eigenschaft. Das Veröffentlichen des oben gezeigten Events sieht dann wie folgt aus:

Registrierung auf Events

Um sich auf ein Event zu registrieren muss der Subscriber lediglich die Subscribe-Methode des Events aufrufen. Das Event stellt dabei diverse Überladungen der Subscribe-Methode bereit, die es einem z.B. ermöglichen das Event innerhalb eines bestimmten Threads zu empfangen. Das ist vor allem dann hilfreich wenn man das UI innerhalb des EventHandlers updaten möchte. Dafür stellt der Parameter ThreadOptionen die folgenden Möglichkeiten bereit:

  • PublisherThread – dies ist die Standardeinstellung und hier wird das Event innerhalb des Publisher-Threads empfangen
  • BackgroundThread – diese Einstellung kann dazu verwendet werden das Event asynchron zu empfangen (dabei wird ein Thread aus dem .NET-Framework Thread-Pool verwendet
  • UIThread – mit dieser Einstellung wird das Event auf dem UI-Thread empfangen

Die Registrierung des Events für die Aktualisierung der StatusBar-Message ist wie folgt implementiert:

Damit wären die grundlegenden Schritte für die Veröffentlichung und Registrierung von PRISM-Events beschrieben. Der PRISM-EventAggregator bzw. PRISM-Events bieten darüber hinaus noch weitere Möglichkeiten wie z.B. Subscription-Filtering, Subscription using Strong References, usw. Für weitere Informationen hierzu siehe die Weblinks am Ende des Artikels.

Github-Repository

Der aktuellste Quellcode ist in folgendem Github-Repository verfügbar: https://github.com/steve600/PrismMahAppsSample

Literaturverzeichnis und Weblinks

Abk.Quelle
[1]Communicating Between Loosely Coupled Components Using the Prism Library 5.0 for WPF
[2]Martin Fowler - EventAggregator
Fork me on GitHub