Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Der MVVM Light Messenger im Detail
Diese Reihe über das Muster Model-View-ViewModel (MVVM) und das MVVM Light Toolkit hat seit ihrem Start vor über einem Jahr schon einiges abgedeckt, vom Einsatz von IoC-Containern in MVVM-Anwendungen bis zu Möglichkeiten, wie man mit dem threadübergreifenden Zugriff und der Komponente DispatcherHelper von MVVM Light umgeht. Ich habe auch über befehlen (mit RelayCommand und EventToCommand), Anzeigedienste wie Navigation und Dialogdienste gesprochen und kurz die Messenger-Komponente diskutiert.
Die Messenger-Komponente ist wirklich ein ziemlich leistungsfähiges Element des MVVM Light Toolkit, das wegen seiner einfachen Bedienbarkeit Entwickler häufig verführt. Aber es hat auch zu einigen Kontroversen geführt wegen der Risiken, die es bei falschem Gebrauch mit sich bringt. Diese Komponente verdient einen eigenen Artikel, der erläutert, wie sie funktioniert, welche Risiken es gibt und in welchen Szenarien sie den meisten Sinn macht.
In diesem Artikel bespreche ich die allgemeinen Prinzipien hinter der Implementierung von Messenger und wir sehen uns an, weshalb diese Implementierung einfach einsetzbar ist als traditionellere Ansätze. Ich beleuchte auch, wie dieser Ansatz sich auf den Speicher auswirken kann, wenn nicht bestimmte Vorkehrungen getroffen werden. Schließlich diskutiere ich den MVVM Light Messenger selbst detaillierter, insbesondere einige der integrierten Nachrichten und wozu sie dienen.
Ereignisaggregation und Messenger-Vereinfachungen
Systeme wie Messenger werden manchmal als Ereignisbus oder Ereignisaggregator bezeichnet. Solche Komponenten verbinden einen Absender mit einem Empfänger (manchmal „Verleger” und „Abonnent” genannt). Als MVVM Light geschaffen wurde, erforderten viele Messagingsysteme, dass der Empfänger oder Absender spezifische Methoden implementiert. Da könnte es beispielsweise eine IReceiver-Schnittstelle gegeben haben, die eine Empfangsmethode festlegt, und für die Registrierung mit dem Messagingsystem musste ein Objekt in diese Schnittstelle implementiert werden. Diese Art von Einschränkung war ärgerlich, denn damit war limitiert, wer das Messagingsystem tatsächlich verwenden kann. Wenn man beispielsweise das Assembly eines Drittanbieters einsetzte, dann konnte man eine Instanz dieser Bibliothek nicht mit dem Messagingsystem registrieren, denn man hatte keinen Zugang zum Code und konnte die Drittanbieter-Klasse nicht für die Implementierung von IReceiver modifizieren.
Der MVVM Light Messenger wurde mit der Absicht geschaffen, dieses Szenario mit einer klaren Prämisse zu vereinfachen: Jedes Objekt kann Empfänger sein; jedes Objekt kann Sender sein; jedes Objekt kann eine Nachricht sein.
Das Vokabular war ebenfalls vereinfacht. Statt Worte wie „Ereignisaggregation” zu verwenden, die schwer zu definieren sind, ist von Messaging die Rede, was relativ leicht verständlich ist. Der Abonnent wird der Empfänger und der Verleger wird der Sender. Statt Ereignissen gibt es Nachrichten. Diese Vereinfachung der Sprache macht es zusammen mit der einfacheren Implementierung leichter, mit dem Messenger in die Gänge zu kommen und zu verstehen, wie er funktioniert.
Betrachten Sie beispielsweise den Code in Abbildung 1. Wie Sie sehen können, wird MVVM Light Messenger in zwei separaten Objekten verwendet. Das Registrierungsobjekt sendet eine Nachricht an alle RegisteredUser-Instanzen. Diese Art von Szenario kann auf unterschiedliche Weise implementiert werden und der Messenger ist dabei nicht immer die beste Lösung. Doch je nach vorhandener Architektur könnte es ein sehr gutes Tool für die Implementierung dieser Funktion sein, besonders wenn Sender und Empfänger sich in Teilen der Anwendung befinden, die entkoppelt sein sollen. Beachten Sie, dass die Registrierungsinstanz nicht direkt an die RegisteredUser-Instanzen sendet. Sie überträgt die Nachricht vielmehr durch den Messenger. Jede Instanz kann sich für diese Art von Nachricht registrieren und wird benachrichtigt, wenn sie versandt wird. In diesem Beispiel ist die gesandte Nachricht eine RegistrationInfo-Instanz. Es kann aber jede Art von Nachricht an dedizierte Meldungsobjekte gesendet werden, zum Beispiel einfache Werte (int, bool usw.). Später spreche ich über den Einsatz von Nachrichten und wir sehen uns einige der integrierten Nachrichtentypen in MVVM Light an.
Abbildung 1: Eine Nachricht senden und empfangen
public class Registration { public void SendUpdate() { var info = new RegistrationInfo { // ... Irgendwelche Eigenschaften }; Messenger.Default.Send(info); } } public class RegisteredUser { public RegisteredUser() { Messenger.Default.Register<RegistrationInfo>( this, HandleRegistrationInfo); } private void HandleRegistrationInfo(RegistrationInfo info) { // Infos für registrierte Benutzer aktualisieren } } public class RegistrationInfo { // ... Irgendwelche Eigenschaften }
Der Code in Abbildung 1 zeigt, dass die Registrierung für einen Nachrichtentyp (RegistrationInfo) durch einen Delegat (HandleRegistrationInfo) erfolgt. Das geschieht häufig im Microsoft .NET Framework. Die Registrierung eines Ereignishandlers in C# wird beispielsweise ebenfalls durch das Übergeben eines Delegaten an das Ereignis erledigt, entweder eine benannte Methode oder ein anonymer lambda-Ausdruck. In gleicher Weise können Sie benannte Methoden oder anonyme lambdas einsetzen, um einen Empfänger mit dem Messenger zu registrieren, wie in Abbildung 2 dargestellt.
Abbildung 2: Registrieren mit benannten Methoden oder lambdas
public UserControl() { InitializeComponent(); // Registrieren mit benannten Methoden ---- Loaded += Figure2ControlLoaded; Messenger.Default.Register<AnyMessage>( this, HandleAnyMessage); // Registrieren mit anonymen lambdas ---- Loaded += (s, e) => { // Mache etwas }; Messenger.Default.Register<AnyMessage>( this, message => { // Mache etwas }); } private void HandleAnyMessage(AnyMessage message) { // Mache etwas } private void Figure2ControlLoaded (object sender, RoutedEventArgs e) { // Mache etwas }
Threadübergreifender Zugriff
Eine Sache, die der Messenger nicht macht, ist zu überwachen, auf welchem Thread eine Nachricht gesendet wird. Wenn Sie meinem vorherigen Artikel „Multithreading und Verteilung in MVVM-Anwendungen” (https://msdn.microsoft.com/de-de/magazine/dn630646.aspx) gelesen haben, dann wissen Sie, dass man einige Vorkehrungen treffen sollte, wenn ein auf einem Thread ausgeführtes Objekt Zugriff auf ein Objekt haben soll, das zu einem anderen Thread gehört. Das Problem entsteht häufig zwischen einem Hintergrundthread und einer Steuerung, die dem UI-Thread gehört. In diesem Artikel konnten Sie sehen, wie sich der MVVM Light DispatcherHelper einsetzen lässt, um den Vorgang auf dem UI-Thread zu „abzuschicken” und den Fehler durch threadübergreifenden Zugriff zu verhindern.
Mit einigen Ereignisaggregatoren kann man Nachrichten automatisch abschicken, die an den UI-Thread gesandt werden. Der MVVM Light Messenger macht das allerdings wegen des Konzepts einer vereinfachten Messenger-API niemals. Eine Option für das automatische Abschicken der Nachrichten an den UI-Thread würde heißen, dass es mehr Parameter bei den Registrierungsmethoden gibt. Mehr noch, damit würde das Abschicken weniger explizit und weniger erfahrene Entwickler könnten möglicherweise schwieriger verstehen, was unter der Haube passiert.
Stattdessen sollte man Nachrichten explizit an den UI-Thread abschicken, wenn nötig. Das geht am besten mithilfe de MVVM Light DispatcherHelpers. Wie im vorigen Artikel dargestellt, schickt die Methode CheckBeginInvokeOnUI den Vorgang nur, falls dies nötig ist. Wenn der Messenger bereits auf dem UI-Thread ausgeführt wird, kann die Nachricht sofort ohne Abschicken verteilt werden:
public void RunOnBackgroundThread() { // Führe irgendeine Hintergrundaufgabe aus DispatcherHelper.CheckBeginInvokeOnUI( () => { Messenger.Default.Send(new ConfirmationMessage()); }); }
Speicherhandhabung
Jedes System, das es Objekten ermöglicht zu kommunizieren, ohne dass sie sich kennen, steht vor der schwierigen Aufgabe, eine Referenz für die Empfänger der Nachricht speichern zu müssen. Denken Sie beispielsweise daran, dass der .NET-Ereignishandlersystem starke Referenzen zwischen einem Objekt erstellen kann, das ein Ereignis auslöst, und einem solchen, das dieses Ereignis abonniert. Der Code in Abbildung 3 schafft eine starke Verknüpfung zwischen _first und _second. Das bedeutet: Wenn die Methode CleanUp aufgerufen wird und _second auf Null gesetzt ist, der Garbage Collector sie nicht aus dem Speicher entfernen kann, das _first immer noch eine Referenz darauf hat. Der Garbage Collector stützt sich auf die Anzahl der Referenzen auf ein Objekt und findet anhand dieser Zahl heraus, ob sie aus dem Speicher entfernt werden kann. Das geschieht nicht für die Instanz Second, weshalb ein Speicherverlust erzeugt wird. Im Laufe der Zeit kann das zu einer Menge Problemen führen; die Anwendung kann deutlich langsamer werden und schließlich sogar abstürzen.
Abbildung 3: Starke Referenz zwischen Instanzen
public class Setup { private First _first = new First(); private Second _second = new Second(); public void InitializeObjects() { _first.AddRelationTo(_second); } public void Cleanup() { _second = null; // Auch wenn sie auf Null gesetzt ist, bleibt die Instanz Second // doch im Speicher, denn die Verweisanzahl ist nicht // null (es gibt immer noch eine Referenz in _first). } } public class First { private object _another; public void AddRelationTo(object another) { _another = another; } } public class Second { }
Damit das ausgeglichen wird, haben die .NET-Entwickler das Objekt WeakReference geschaffen. Mit dieser Klasse lässt sich eine Objektreferenz auf „schwache” Weise speichern. Wenn alle anderen Referenzen auf dieses Objekt auf Null stehen, kann der Garbage Collector das Objekt abräumen, auch wenn es eine WeakReference gibt, die es verwendet. Das ist sehr praktisch, und wenn weise genutzt, kann es das Problem mit Speicherverlust verringern, auch wenn es nicht sämtliche Schwierigkeiten aus der Welt räumt. Zur Illustration dieses Punkts zeigt Abbildung 4 ein einfaches Kommunikationssystem, in dem das Objekt SimpleMessenger die Referenz zum Empfänger in einer WeakReference speichert. Beachten Sie, wie die Eigenschaft IsAlive geprüft wird, ehe die Nachricht verarbeitet wird. Wenn der Empfänger nicht gelöscht und vom Garbage Collector entfernt wurde, dann ist die Eigenschaft IsAlive falsch. Das ist ein Zeichen dafür, dass die WeakReference nicht mehr gültig ist und deshalb entfernt werden sollte.
Abbildung 4: Verwendung von WeakReference-Instanzen
public class SuperSimpleMessenger { private readonly List<WeakReference> _receivers = new List<WeakReference>(); public void Register(IReceiver receiver) { _receivers.Add(new WeakReference(receiver)); } public void Send(object message) { // Empfänger sperren, damit Multithreadprobleme verhindert werden. lock (_receivers) { var toRemove = new List<WeakReference>(); foreach (var reference in _receivers.ToList()) { if (reference.IsAlive) { ((IReceiver)reference.Target).Receive(message); } else { toRemove.Add(reference); } } // Tote Referenzen entfernen. // Tun Sie das in einer anderen Schleife, um einen Fehler zu verhindern // wenn Sie eine Auflistung ändern, die gerade durchlaufen wird. foreach (var dead in toRemove) { _receivers.Remove(dead); } } } }
Der MVVM Light Messenger setzt ungefähr auf dasselbe Prinzip, auch wenn er natürlich ein ganzes Stück komplexer ist! Besonders weil der Messenger es nicht erforderlich macht, dass der Empfänger irgendeine Schnittstelle implementiert, muss er eine Referenz zur Methode speichern (der Rückruf), die für die Übertragung der Nachricht verwendet wird. In Windows Presentation Foundation (WPF) und Windows-Runtime ist das kein Problem. In Silverlight und Windows Phone ist der Framework jedoch sicherer und die APIs verhindern, dass bestimmte Vorgänge ablaufen. Eine dieser Einschränkungen trifft das Messengersystem in bestimmten Fällen.
Um das zu verstehen, muss man wissen, welche Art von Methoden für den Umgang mit Nachrichten registriert werden können. Zusammenfassend kann gesagt werden, dass eine Empfangsmethode statisch sein kann, was nie ein Problem ist; oder es kann eine Instanzmethode sein, wobei man dann zwischen öffentlich, intern und privat unterscheiden muss. In vielen Fällen ist eine Empfangsmethode ein anonymer lambda-Ausdruck, was dasselbe wie eine private Methode ist.
Wenn eine Methode statisch oder öffentlich ist, besteht kein Risiko, dass ein Speicherverlust entsteht. Ist die Behandlungsmethode intern oder privat (oder ein anonymer lambda), kann das in Silverlight und Windows Phone ein Risiko darstellen. Leider gibt es in diesen Fällen keine Möglichkeit für den Messenger, eine WeakReference zu verwenden. Und noch einmal: Das ist kein Problem in WPF oder der Windows-Runtime. Abbildung 5 fasst diese Informationen zusammen.
Abbildung 5: Das Risiko von Speicherverlusten ohne Aufheben der Registrierung
Sichtbarkeit | WPF | Silverlight | Windows Phone 8 | Windows-Runtime |
Statisch | Kein Risiko | Kein Risiko | Kein Risiko | Kein Risiko |
Öffentlich | Kein Risiko | Kein Risiko | Kein Risiko | Kein Risiko |
Intern | Kein Risiko | Risiko | Risiko | Kein Risiko |
Privat | Kein Risiko | Risiko | Risiko | Kein Risiko |
Anonymer lambda | Kein Risiko | Risiko | Risiko | Kein Risiko |
Es ist wichtig zu beachten, dass selbst wenn es, wie in Abbildung 5 dargestellt, ein Risiko gibt, führt das Aufheben der Registrierung nicht immer zu einem Speicherverlust. Das gesagt: Damit sichergestellt ist, dass kein Speicherverlust auftritt, ist es eine gute Angewohnheit, die Registrierung der Empfänger vom Messenger explizit aufzuheben, wenn sie nicht mehr gebraucht werden. Dies kann mithilfe der Methode Unregister erreicht werden. Beachten Sie, dass es mehrere Überladungen von Unregister gibt. Die Registrierung vom Messenger kann für einen Empfänger komplett aufgehoben werden, oder man kann nur die Registrierung für eine der vorhandenen Methoden aufheben und andere aktiv halten.
Andere Risiken beim Einsatz von Messenger
Wie bereits erwähnt, auch wenn der MVVM Light Messenger eine sehr leistungsfähige und vielseitige Komponente ist, sollte man daran denken, dass sein Einsatz einige Risiken birgt. Ich habe schon potenzielle Speicherverluste in Silverlight und Windows Phone erwähnt. Ein weiteres Risiko ist weniger technisch: Das Verwenden von Messenger entkoppelt die Objekte so weit, dass es schwer zu verstehen sein kann, was genau passiert, wenn eine Nachricht gesandt und empfangen wird. Für weniger erfahrene Entwickler, die einen Ereignisbus noch nie benutzt haben, kann es schwierig sein, dem Ablauf der Ereignisse zu folgen. Wenn man beispielsweise in den Aufruf einer Methode tritt und diese die Methode Messenger.Send aufruft, geht der Ablauf des Debuggings verloren, außer man weiß, dass man nach der korrespondierenden Methode Messenger.Receive suchen und dort einen Haltepunkt platzieren muss. Das gesagt: Die Messenger-Vorgänge sind synchron und wenn man versteht, wie der Messenger arbeitet, dann ist das Debugging dieses Ablaufs durchaus möglich.
Ich neige dazu, den Messenger als „letztes Mittel” zu verwenden, wenn konventionellere Programmiertechniken nicht möglich sind oder zu viele Abhängigkeiten zwischen Teilen der Anwendung verursachen, die ich so entkoppelt wie möglich halten möchte. Manchmal jedoch ist es vorzuziehen, andere Tools wie beispielsweise einen IOC-Container und Dienste zu verwenden und damit vergleichbare Ergebnisse auf explizitere Weise zu erreichen. Ich habe im ersten Artikel dieser Reihe über IOC und die Anzeigedienste gesprochen (bit.ly/1m9HTBX).
Einer oder mehrere Messenger
Einer der Vorzüge von Messagingsystemen wie dem MVVM Light Messenger besteht darin, dass sie sogar über Assemblys hinaus verwendet werden können – etwa in einem Plug-In-Szenario. Das ist eine gängige Architektur zur Entwicklung großer Anwendungen, besonders in WPF. Doch ein Plug-In-System kann auch für kleinere Anwendungen nützlich sein, wenn man etwa einfach neue Funktionen hinzufügen möchte, ohne die Hauptkomponente erneut kompilieren zu müssen. Sobald ein DLL in der AppDomain der Anwendung geladen wurde, können die darin enthalten Klassen den MVVM Light Messenger verwenden, um mit einer anderen Komponente in derselben Anwendung zu kommunizieren. Das ist sehr leistungsfähig, besonders wenn die Hauptanwendung nicht weiß, wie viele Subkomponenten geladen sind, was typischerweise bei einer Plug-In-Anwendung der Fall ist.
Normalerweise braucht eine Anwendung nur eine einzelne Messenger-Instanz und deckt damit die gesamte Kommunikation ab. Die statische Instanz, die in der Eigenschaft Messenger.Default gespeichert ist, ist wahrscheinlich alles, was man braucht. Man kann allerdings bei Bedarf neue Messenger-Instanzen erstellen. In derartigen Fällen agiert jeder Messenger als separater Kommunikationskanal. Das kann nützlich sein, wenn man sicherstellen möchte, dass ein bestimmtes Objekt niemals eine Nachricht erhält, die nicht für es gedacht ist. Im Code in Abbildung 6 beispielsweise registrieren zwei Klassen für denselben Nachrichtentyp. Wird die Nachricht empfangen, müssen beide Instanzen einige Prüfungen durchführen um zu sehen, was die Nachricht tut.
Abbildung 6: Den Standard-Messenger verwenden und den Absender prüfen
public class FirstViewModel { public FirstViewModel() { Messenger.Default.Register<NotificationMessage>( this, message => { if (message.Sender is MainViewModel) { // Diese Nachricht ist für mich. } }); } } public class SecondViewModel { public SecondViewModel() { Messenger.Default.Register<NotificationMessage>( this, message => { if (message.Sender is SettingsViewModel) { // Diese Nachricht ist für mich. } }); } }
Abbildung 7 zeigt eine Implementierung mit einer privaten Messenger-Instanz. In diesem Fall wird das SecondViewModel die Nachricht niemals erhalten, denn es hat eine andere Instanz des Messengers abonniert und wartet auf einem anderen Kanal.
Abbildung 7: Einen privaten Messenger verwenden
public class MainViewModel { private Messenger _privateMessenger; public MainViewModel() { _privateMessenger = new Messenger(); SimpleIoc.Default.Register(() => _privateMessenger, "PrivateMessenger"); } public void Update() { _privateMessenger.Send(new NotificationMessage("DoSomething")); } } public class FirstViewModel { public FirstViewModel() { var messenger = SimpleIoc.Default.GetInstance<Messenger>("PrivateMessenger"); messenger.Register<NotificationMessage>( this, message => { // Diese Nachricht ist für mich. }); } }
Eine andere Möglichkeit besteht darin, eine bestimmte Nachricht an einen speziellen Empfänger nicht zu senden, besteht in der Verwendung von Tokens wie in Abbildung 8 dargestellt. Das ist eine Art Vertrag zwischen Sender und Empfänger. Typischerweise ist ein Token ein ein eindeutiger Bezeichner wie etwa ein GUID; es könnte aber auch irgendein Objekt sein. Wenn ein Sender und Empfänger beide denselben Token verwenden, dann wird zwischen den beiden Objekten ein privater Kanal geöffnet. In diesem Szenario wird das SecondViewModel, das den Token nicht verwendet, niemals darüber informiert, dass eine Nachricht gesendet wird. Der wichtigste Vorteil liegt darin, dass der Empfänger keine Logik schreiben muss, die sicherstellt, dass die Nachricht wirklich für ihn gedacht war. Stattdessen filtern der Messenger Nachrichten auf der Grundlage von Tokens heraus.
Abbildung 8: Unterschiedliche Kommunikationskanäle mit Tokens
public class MainViewModel { public static readonly Guid Token = Guid.NewGuid(); public void Update() { Messenger.Default.Send(new NotificationMessage("DoSomething"), Token); } } public class FirstViewModel { public FirstViewModel() { Messenger.Default.Register<NotificationMessage>( this, MainViewModel.Token, message => { // Diese Nachricht ist für mich. }); } }
Nachrichten verwenden
Tokens sind eine hübsche Möglichkeit, Nachrichten zu filtern. Das ändert allerdings nichts an der Tatsache, dass eine Nachricht etwas Kontext beinhalten sollte, damit sie verstanden wird. Beispielsweise kann man die Methoden Send und Receive mit Booleschem Inhalt verwenden wie in Abbildung 9 dargestellt. Doch wenn mehrere Sender Boolesche Nachrichten senden, wie soll ein Empfänger dann wissen, für wen die Nachricht bestimmt ist und was er damit tun soll? Deshalb ist es besser, einen dedizierten Nachrichtentyp zu verwenden und so den Kontext klar zu machen.
Abbildung 9: Einen Nachrichtentyp zur Definition des Kontextes verwenden
public class Sender { public void SendBoolean() { Messenger.Default.Send(true); } public void SendNotification() { Messenger.Default.Send( new NotificationMessage<bool>(true, Notifications.PlayPause)); } } public class Receiver { public Receiver() { Messenger.Default.Register<bool>( this, b => { // Weiß nicht genau, was ich mit diesem Booleschen anfangen soll. }); Messenger.Default.Register<NotificationMessage<bool>>( this, message => { if (message.Notification == Notifications.PlayPause) { // Mache etwas mit dem Nachrichteninhalt. Debug.WriteLine(message.Notification + ":" + message.Content); } }); } }
Abbildung 9 zeigt auch, dass ein spezifischer Nachrichtentyp verwendet wird. NotificationMessage<T> ist einer der am häufigsten verwendeten Nachrichtentypen, die in das MVVM Light Toolkit integriert sind. Er ermöglicht es, jegliche Inhalte (in diesem Fall, ein Boolescher) zusammen mit einer Benachrichtigungszeichenfolge zu senden. Die Benachrichtigung ist typischerweise eine eindeutige Zeichenfolge in einer statischen Klasse namens Notifications. Damit ist es möglich, Anweisungen zusammen mit der Nachricht zu senden.
Natürlich ist es auch möglich, von NotificationMessage<T> abzuleiten, einen anderen integrierten Nachrichtentyp zu verwenden oder eigene Nachrichtentypen zu implementieren. Das MVVM Light Toolkit enthält die Klasse MessageBase, die für diesen Zweck abgeleitet werden kann, auch wenn das absolut nicht zwingend ist, wenn man das im eigenen Code verwenden möchte.
Eine weiterer integrierter Nachrichtentyp ist PropertyChangedMessage<T>. Das ist insbesondere hilfreich in Bezug auf ein ObservableObjekt und die Klasse ViewModelBase, die typischerweise als Basisklasse für Objekte verwendet wird, die in Bindungsvorgängen vorkommen. Diese Klassen sind Implementierungen der INotifyPropertyChanged-Schnittstelle, die entscheidend in MVVM-Anwendungen mit Datenbindung ist. Beispielsweise definiert im Code in Abbildung 10 das BankAccountViewModel eine beobachtbare Eigenschaft namens Balance. Wenn sich diese Eigenschaft verändert, dann nimmt die Methode RaisePropertyChanged einen Booleschen Parameter, der dazu führt, dass die Klasse ViewModelBase eine PropertyChangedMessage mit Informationen über diese Eigenschaft „überträgt”, wie etwa ihren Namen, den alten und neuen Wert. Ein anderes Objekt kann diesen Nachrichtentyp abonnieren und entsprechend reagieren.
Abbildung 10: Eine PropertyChangedMessage senden
public class BankViewModel : ViewModelBase { public const string BalancePropertyName = "Balance"; private double _balance; public double Balance { get { return _balance; } set { if (Math.Abs(_balance - value) < 0.001) { return; } var oldValue = _balance; _balance = value; RaisePropertyChanged(BalancePropertyName, oldValue, value, true); } } } public class Receiver { public Receiver() { Messenger.Default.Register<PropertyChangedMessage<double>>( this, message => { if (message.PropertyName == BankViewModel.BalancePropertyName) { Debug.WriteLine( message.OldValue + " --> " + message.NewValue); } }); } }
Es gibt andere integrierte Nachrichten in MVVM Light, die in verschiedenen Szenarien nützlich sind. Darüber hinaus ist die Infrastruktur zum Entwickeln eigener benutzerdefinierter Nachrichten verfügbar. Im Wesentlich steht dahinter die Idee, das Leben des Empfängers zu leichter zu machen, indem man einen hinreichenden Kontext für ihn liefert, sodass er weiß, was er mit dem Inhalt der Nachricht anfangen soll.
Zusammenfassung
Der Messenger hat sich in vielen Szenarien als recht nützlich erwiesen, die ohne eine komplett entkoppelte Messaging-Lösung schwer zu implementieren wären. Er ist jedoch ein erweitertes Werkzeug und sollte mit Vorsicht eingesetzt werden, damit kein verwirrender Code entsteht, der später schwer zu debuggen und warten ist.
Dieser Artikel schließt die Präsentation der MVVM Light Toolkit-Komponenten ab. Das ist eine spannende Zeit für .NET-Entwickler, die jetzt dieselben Tools und Techniken für mehrere XAML-basierte Plattformen verwenden können. Mit MVVM Light können Sie Code über WPF, Windows Runtime, Windows Phone, Silverlight hinweg verwenden – ja sogar auf den Xamarin-Plattformen für Android und iOS. Ich hoffe, Sie fanden diese Artikelreihe nützlich für ein besseres Verständnis dazu, wie Sie mithilfe von MVVM Light Anwendungen effizient entwickeln und gleichzeitig Design, Tests und Wartung dieser Anwendungen einfach halten.
Laurent Bugnionist Senior Director beim Microsoft-Partner IdentityMine Inc., der Technologien wie Windows Presentation Foundation, Silverlight, Pixelsense, Kinect, Windows 8, Windows Phone und UX einsetzt. Er lebt in Zürich in der Schweiz. Er ist außerdem Microsoft MVP und Microsoft Regional Director.
Unser Dank gilt dem folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels: Jeffrey Ferman
Jeffrey Ferman arbeitet derzeit als Projektleiter an Visual Studio. Seit über vier Jahren konzentriert sich Jeff auf XAML-Tools sowohl in Visual Studio als auch Blend. Er entwickelt gerne Branchen-Apps und experimentiert mit unterschiedlichen Designmustern und -praktiken. Er begeistert sich leidenschaftlich für Erweiterbarkeit und genießt die Arbeit mit Kunden, wenn es um die Entwicklung von Erlebnissen zur Entwurfszeit bei der Steuerung geht.