Das Observer Pattern – Wenn Code kommuniziert…

In Verbindung mit MVVM sollen der Vollständigkeit halber die Arbeiter hinter den Kulissen etwas erläutert werden, um einfach auch zu zeigen, was dem Entwickler durch das .NET Framework alles an Arbeit abgenommen wird. – Nun zum Thema

SnapFinalApplication

Wer sich an das oben gezeigte Bild aus dem MVVM Beispielen erinnert, ist nun sprichwörtlich alles Bestens. Leider wird es dies immer der Fall sein. Wenn sich nun der Status des hier nur bildlich vorgestellten Servers ändern sollte, würde dies in der View nie dargestellt werden, weil sie über Änderungen im Model vom View Model bis jetzt nicht informiert wird. Darum kümmert sich eine Implementierung des Observer Patterns in .NET.

Mit Hilfe des Observer Patterns können Änderungen eines einzelnen Objektes allen dazu in Beziehung stehenden Objekten bekannt gemacht werden. Dabei wird von Subject und Obeservern (Beobachtern) gesprochen. Ein Subject kann mehrere Observer besitzen. Sobald eine Änderung im Subject auftritt, können alle Observer benachrichtigt werden und darauf reagieren.

ObserverPatternDiagram

Vereinfachtes Schema des Observer Patterns

Bei der Übertragung des Schemas auf das MVVM ergibt sich für die View die Rolle des Observers. Sobald im Datenmodell Änderungen auftreten, soll die Benutzeroberfläche entsprechend informiert werden. Das View Model muss diese Änderungen als Subject publizieren.

Ab jetzt wird es für den Entwickler sehr einfach. Die Implementierung des Observer Patterns findet nicht statt, da es von dem .NET Framework geliefert wird. Wenn das View Model das Interface INotifyPropertyChanged implementiert, registriert sich die View automatisch als Observer an dem View Model.

Das nachstehende Beispiel knüpft an das Projekt aus den vorangegangenen Kapiteln an und demonstriert den Einsatz von INotifyPropertyChanged.

[sourcecode language=”csharp”]
using System.ComponentModel;

namespace T_SimpleDataBinding
{
public class StatusMessageViewModel : INotifyPropertyChanged
{
private string _statusMessage;
public string StatusMessage
{
get { return _statusMessage; }
set
{
_statusMessage = value;
OnPropertyChanged("StatusMessage");
}
}

public void ModelStatusRequestCompleted
(object sender, StatusEventArgs e)
{
StatusMessage = e.Result;
}

public event PropertyChangedEventHandler PropertyChanged;

private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged
(this, new PropertyChangedEventArgs(property));
}
}
}
}
[/sourcecode]

Beginnen wir in Zeile 05. Das Implementieren von INotifyPropertyChanged erzwingt die Deklaration des eines Events, z u sehen in Zeile 24. Dieses Event wird automatisch von der View abonniert. Nun muss dieses Event immer dann gerufen werden, wenn sich eine Eigenschaft im View Model ändert. Dafür wurde im set der StatusMessage Property in Zeile 14 ein Methodenaufruf gesetzt. Das heißt, immer wenn der Eigenschaft ein Wert zugewiesen wird, wird diese Methode gerufen. Die Methode macht nichts anderes, als zu prüfen, ob es einen Abonnenten für das Event gibt. Wenn das der Fall ist wird das PropertyChanged Event „gefeuert“. Dabei wird der Name der geänderten Eigenschaft in der string – Variable property übergeben. So kann die View entscheiden, welches Oberflächenelement aktualisieren muss.

Hinweis: Damit dieser Ansatz in der Umsetzung problemlos funktioniert, sollten eindeutige Bezeichnungen für die Eigenschaften vergeben werden. Bei gleicher Namensgebung werden mehrere Benutzerelemente fälschlicherweise aktualisiert.

Neben dem Interface INotifyPropertyChanged gibt es noch weitere Implementierungshilfen für das Observer Pattern, die als zusätzliche Information hier genannt werden.

Mit Hilfe des INotifyCollectionChanged können Observer über die Änderungen einer ganzen Liste von Eigenschaften informiert werden. Die Klasse ObservableCollection<T> implementiert diese Schnittstelle bereits. In einem späteren Szenario wird deren Einsatz demonstriert.

Stay tuned!

Im Rahmen der Bachelorarbeit werden noch weiterführende Themen besprochen und hoffentlich rege diskutiert.
Seit gespannt, was die nächsten Tage hier passiert! Zwinkerndes Smiley
Hier ein paar Key Notes:

  • Pattern in MVVM
    • Command Pattern
    • Observer Pattern
  • Was ist Prism?
    • Das Ziel von Composite UI
    • Grundkenntnisse
    • Der Unity Container
    • Der Bootstrapper
    • Lego in .NET ?
    • Die Bausteine
    • Das Steckbrett

    Ist das nicht genug? Kontaktiert mich, wenn euch noch weitere Dinge zu diesem Thema interessieren!

    Beste Grüße
    Gregor

  • Der Klebstoff

    Ein Bindeglied zwischen der View und dem Model wird nun benötigt. Das View Model ist nicht nur die sprachliche Komposition der zwei erwähnten Bestandteile des MVVM, sondern auch die technische Lösung. Das View Model stellt die Daten des Models für die View zur Verfügung. Dies geschieht über das Data Binding. Wie der Begriff vermuten lässt, wird hierdurch ein Prozess beschrieben, wie Daten von einer bestimmten Quelle an die Oberfläche gebunden werden können. Die Quelle stellt das View Model dar. Es stellt eine für das User Interface abrufbare Eigenschaft zur Verfügung. Die Eigenschaft wird vom View Model mit Hilfe des Models verwaltet.

    [sourcecode language=”csharp”]
    namespace T_SimpleDataBinding
    {
    public class StatusMessageViewModel
    {
    public string StatusMessage { get; set; }

    public StatusMessageViewModel()
    {
    StatusMessageModel model = new StatusMessageModel();
    model.StatusRequestCompleted += ModelStatusRequestCompleted;

    model.BeginStatusRequest();
    }

    public void ModelStatusRequestCompleted
    (object sender, StatusEventArgs e)
    {
    StatusMessage = e.Result;
    }
    }
    }
    [/sourcecode]
    View Model für Beispielanwendung “Einfaches Data Binding”

    Nun sehen wir in Zeile 5 die Eigenschaft, an die sich die View zukünftig binden wird. Im Konstruktor des StatusMessageViewModel wird das Model instanziert. Das View Model schließt in Zeile 10 das erwähnte Abonnement ab. Wir erinnern uns: Sobald das Model die Daten geladen hat soll das ViewModel dies Daten empfangen und dann über die Property der View zur Verfügung stellen. Sobald das Event durch das Model „gefeuert“ wurde wird in der Methode ModelStatusRequestCompleted in Zeile 15, die die Statusnachricht in der Eigenschaft setzt.

    Die View und das ViewModel werden mit einander verheiratet. Dazu muss das ViewModel der View bekannt gemacht werden. Dies kann auf unterschiedlichen Wegen erfolgen. Zum einen direkt im XAML- Code:

    [sourcecode language=”html”]
    <UserControl.Resources>
    <Lokal:StatusMessageViewModel x:Key="BindingViewModel"/>
    </UserControl.Resources>
    <UserControl.DataContext>
    <Binding Source="{StaticResource BindingViewModel}"/>
    </UserControl.DataContext>
    [/sourcecode]
    Einbetten des View Models in die View

    In der StatusMessageView werden diese Tags hinzugefügt. Innerhalb von UserControl.Resources wird das View Model (grau unterlegt) bekannt gemacht. Dazu wird ein Namesraum benötigt, damit die Klasse in dem XAML- File serialisiert werden kann:

    [sourcecode language=”html”]
    xmlns:Lokal="clr-namespace:T_SimpleDataBinding"
    [/sourcecode]
    Deklaration eines View Models in der View.

    Außerdem wird das View Model dem DataContext des UserControls hinzugefügt. Dies stellt einen Pfad dar. Die View kann nun alle öffentlichen Properties auslesen, später sogar noch mehr.

    Eine weitere Möglichkeit ist die Instanzierung in der Code Behind Datei der StatusMessageView.xaml:

    [sourcecode language=”csharp”]
    using System.Windows.Controls;

    namespace T_SimpleDataBinding
    {
    public partial class StatusMessageView : UserControl
    {
    private StatusMessageViewModel viewModel;

    public StatusMessageView()
    {
    InitializeComponent();

    viewModel = new StatusMessageViewModel();

    this.DataContext = viewModel;
    }
    }
    }
    [/sourcecode]
    Instanzierung eines View Models in der Code Behind Datei der View

    Was jetzt noch bleibt ist lediglich unserem Textelement in der Benutzeroberfläche die zu bindende Ressource bekannt zu machen. Dazu leitet man das Data Binding mit geschweiften Klammern und dem darauf folgenden Schlüsselwort Binding ein. Das Attribut Path erhält den Wert der gebunden Property, deren Wert dann in dem TextBlock erscheint.

    [sourcecode language=”html”]
    <UserControl x:Class="T_SimpleDataBinding.StatusMessageView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-
    compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:Lokal="clr-namespace:T_SimpleDataBinding">
    <UserControl.Resources>
    <Lokal:StatusMessageViewModel x:Key="BindingViewModel"/>
    </UserControl.Resources>
    <UserControl.DataContext>
    <Binding Source="{StaticResource BindingViewModel}"/>
    </UserControl.DataContext>
    <Grid>
    <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
    Text="{Binding Path=StatusMessage}"/>
    </Grid>
    </UserControl>
    [/sourcecode]
    vollständige View mit Data Binding

    Sobald die Anwendung kompiliert und gestartet wurde erhalten wir ein so genanntes „Hallo Welt“- Beispiel für MVVM.

    clip_image002
    “Hallo MVVM”

    Die View

    Mit Hilfe der Extensible Application Markup Language (XAML) kann die View beschrieben werden. Jeder der HTML kennt, wird sich schnell in XAML einarbeiten können. Einige Beispiele werden in dieser Arbeit beschrieben. Die View hat die Aufgabe die Daten aus dem Model für den Benutzer lesbar in der Anwendung darzustellen. Hierbei ist Kreativität gefragt. Um nun einen ersten Einblick in XAML zu ermöglichen ist nachstehend die Oberfläche für unser Beispielprojekt in der StatusMessageView.xaml beschrieben.

    [sourcecode language=”html”]
    <UserControl x:Class="T_SimpleDataBinding.StatusMessageView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-
    compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
    <Grid>
    <TextBlock Text="[Hier soll der Serverstatus angezeigt werden!]"/>
    </Grid>
    </UserControl>

    [/sourcecode]

    Listing 2 View für Beispielanwendung “Einfaches Data Binding”

    Das erste Tag (UserControl) leitet die Oberflächenbeschreibung ein. Die Attribute dieses Tags sind Namespace Deklarationen und werden vom Visual Studio selbst generiert, was sehr nützlich ist. Da XAML nichts weiter eine Repressentation der WPF- oder Silverlight- Klassen ist müssen bestimmte Namenskonventionen eingehalten werden, damit die Oberfläche fehlerfrei implementiert werden kann. Mit Hilfe der Namespaces, kann das XAML validiert werden, um Fehler präventiv entgegenzuwirken.

    In dem UserControl befindet sich das Grid, welches ein Container ist, in dem Oberflächenelemente arrangiert werden können. In unserem Fall liegt in dem Grid zunächst ein simpler TextBlock, der in naher Zukunft die Statusnachricht aus dem Model anzeigen soll. Diese Oberfläche hat aber noch nichts mit Grafikgestaltung zu tun. Für eine ansprechende Programmoberfläche sollte stets ein Designer für das Team arbeiten.

    Aus dem oberen Absatz ergeben sich nun bereits zwei Fragen. Als erstes muss geklärt werden, wie die Daten vom Model in die View geladen werden. Eine ebenso interessante Frage ist, warum in dieser Arbeit über Softwarearchitektur der Grafikdesigner erwähnt wird. Beide Fragen lassen sich sehr leicht beantworten. Aufgrund der Struktur des MVVM ist es möglich Designer ohne jedwede Programmierkenntnis in das Softwareprojekt zu integrieren. Dazu muss man sich an eine wichtige Regel halten: Die View darf keine Geschäftslogik enthalten. Die View kann also komplett von Grafikern entworfen werden, ohne dass eine Zeile Code geschrieben werden muss. Dies wird durch Microsofts Expressen Blend ermöglicht. Die Funktionalität dieses Werkzeugs wird im späteren Verlauf der Arbeit erläutert.

    Nun ist das Chaos komplett. Der Programmierer soll also die Geschäftslogik implementieren und der Grafiker die Oberfläche gestalten und animieren. Wie kommen die Arbeitsergebnisse nun zusammen? Im nächsten Kapitel erfahren Sie wie diese getrennten Welten miteinander verbunden werden.

    MVVM – Das Model- View- ViewModel

    Das MVVM- Pattern setzt sich aus drei wesentlichen Bestandteilen zusammen. Der Repräsentant der Datenschicht ist hier das Model. In einem Model werden alle Daten, die für eine Anwendung zur Verfügung gestellt werden gespeichert. Je nach Szenario können mehrere Models existieren. Dem Model sind Methoden bekannt Daten zu laden und diese zu verwalten. Beispielsweise kann das Model an einen Webservice gebunden sein um Daten zu sammeln, diese zu speichern, oder zu manipulieren. In der Praxis werden für große Anwendungen meistens Datenbanksysteme verwendet um große Datenmengen zu verwalten. Um den Funktionsrahmen des Models zu verdeutlichen, ist nachstehend ein Beispielprojekt angeführt, was sich zu Gunsten der Erläuterung durch dieses Kapitel wie ein roter Faden ziehen wird.

    clip_image002

    Bild 1 Projekt für ein einfaches Data Binding

    Im Visual Studio 2010 wurde eine einfache WPF Anwendung angelegt. Diese ist für die Demonstration ganz simpel gehalten und besteht aus den eben beschriebenen Elementen des MVVM. In allen folgenden Erläuterungen sind an jeder Klasse die Bezeichnungen der Bestandteile (Model, ViewModel, View) angefügt, damit es jederzeit möglich ist diese zu identifizieren.

    In diesem Beispiel soll lediglich eine Statusnachricht in einem Fenster angezeigt werden. Die Nachricht könnte von einem Server abgefragt werden, um sicher zu stellen, dass dieser erreichbar ist.

    Dafür wird ein Model angelegt, welches die Fähigkeit besitzen muss den Status abzufragen und die Information zu speichern. In diesem Fall wird der Datenbankzugriff nur simuliert, um die Komplexität des Quellcodes so gering wie möglich zu halten.

    Nachstehend ist der Quellcode des StatusMessageModels abgebildet:
    [sourcecode language=”csharp”]
    namespace T_SimpleDataBinding
    {
    public class StatusMessageModel
    {
    public event EventHandler&lt;StatusEventArgs&gt;
    StatusRequestCompleted;

    public void BeginStatusRequest()
    {
    // Abarbeitung um 5 Sekunden verzögern.
    // Dies dient der Simulation einer Datenbankabfrage.
    Thread.Sleep(5000);

    if (StatusRequestCompleted != null)
    {
    const string message = "Alles ist in Bestens!";

    StatusRequestCompleted
    (this, new StatusEventArgs(message));
    }
    }
    }

    public class StatusEventArgs : EventArgs
    {
    public string Result { get; set; }

    public StatusEventArgs(string result)
    {
    Result = result;
    }
    }
    }
    [/sourcecode]
    Listing 1 Model für Beispielanwendung „Einfaches Data Binding”

    Hier wird in der Klasse StatusMessageModel als aller erstes ein Event angelegt. Dies kann von einer anderen Klasse abonniert werden. Wenn das Event oder auch Ereignis eintritt, kann der Abonnent sofort darauf reagieren. In unserem Fall nutzen wir das Event, um darüber zu informieren, dass die Statusabfrage erfolgreich geladen wurde und nun bereit steht. Die zu übertragene Information, in unserem Fall die Statusnachricht, wird mittels Ereignisvariablen an den Abonnementen gesendet. Dafür wurde eine Klasse StatusEventArgs geschrieben, die von EventArgs erbt. Das ist auch schon alles. Unser erstes Model ist fertig!

    Das Model nutzt einem Anwender zunächst nicht viel, da es keine Kenntnis darüber hat, wie es sich auf einem Bildschirm präsentieren soll. Dafür wird eine Oberfläche benötigt, die in WPF oder Silverlight mit einer bestimmten Beschreibungssprache erstellt werden kann.

    Frisch ans Werk!

    Nun ist es soweit.- Das Schreiben der Bachelorarbeit steht an.
    Diese Gelegenheit möchte ich nutzen um hier meine Arbeitsergebnisse zur Verfügung zu stellen und diskutieren.

    Viel Spaß beim Lesen!

    Beste Grüße
    Gregor