Datenbindung in WPF/XAML

Werbung
PPM – Schwerpunktfragen
Fragebogendesigner
Stefan Wurzinger
Datenbindung in WPF/XAML
Verwendung im Maturaprojekt: Für die Realisierung des MVVM Entwurfsmusters benötigt
WPF erlaubt es vom XAML Code auf Eigenschaften des Programmcodes zu verweisen, diese
also zu binden. Dabei steht es einem frei, ob man die Bindung nur einseitig – d.h. z.B.
ausschließlich mit den Daten einer Eigenschaft im Code ein Textfeld zu befüllen bzw. mit
Daten im Textfeld eine Eigenschaft setzen – oder beidseitig (Änderungen auf beiden Seiten
haben Auswirkungen auf das jeweilige gegenüber) durchführen will. Hierbei wird der
Datenkontext auf die entsprechende Klasse gesetzt, auf die sich folgend alle Datenbindungen
des jeweiligen Elements und dessen untergeordnete Elemente beziehen.
Da die klassischen Eigenschaften nicht mehr ausreichten, um z.B. Änderungen festzustellen
und zu publizieren, wurden so genannte Abhängigkeitseigenschaften (aus dem englischen:
Dependency Property)eingeführt. Dabei werden die Daten nicht mehr herkömmlich in einer
privaten Variable der Klasse gespeichert, sondern in einem globalen gemeinsamen
Speicherbereich verwaltet. Des Weiteren bieten sie Unterstützung für das Propagieren von
Nachrichten über Änderungen der entsprechenden Variablen und können Vererbt werden.
Beispiel zweier Datenbindungen:
<TextBox Text="{Binding Path=Text, ValidatesOnDataErrors=True, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}" />

Path
Im Path wird der Name der Eigenschaft angegeben, die gebunden werden soll. Es
kann auch zusätzlich eine oder mehrere Unter-Eigenschaften oder ein Array-Indizes
angegeben werden. Mit den Eigenschaften „RelativeSource“ und „ElementName“
kann zusätzlich angegeben werden, auf welches Objekt sich die Datenbindung bezieht
(standardmäßig ist dies der dem Element zugewiesene bzw. vererbte Datenkontext).

Mode
Bestimmt die Art der Datenbindung. Hierbei kann zwischen folgenden Arten
unterschieden werden (u.a.):
o OneWay
Die Daten werden nur bei Änderungen im ViewModel auf der
Benutzeroberfläche aktualisiert.
o TwoWay
Änderungen
im
ViewModel
bewirken
Änderungen
auf
der
Benutzeroberfläche, aber auch umgekehrt.
o OneWayToSource
Änderungen auf der Benutzeroberfläche bewirken Änderungen im
ViewModel, aber nicht umgekehrt.
o OneTime
Die Daten werden nur ein einziges Mal vom ViewModel abgerufen und auf
der Benutzeroberfläche aktualisiert.

UpdateSourceTrigger
Hier kann angegeben werden, wann Änderungen einer Seite der Datenbindung an das
jeweilige Gegenüber übermittelt werden sollen. Dies kann z.B. sofort bei jeder
Rev. 1.0
Seite 1 von 6
23.06.2011
PPM – Schwerpunktfragen
Fragebogendesigner
Stefan Wurzinger
Änderung, nach Verlust des Eingabefokus oder allein Programmtechnisch durch
zusätzlichen Programmcode geschehen.
Model-View-ViewModel
Verwendung im Maturaprojekt: Grundstruktur der Anwendung
MVVM bezeichnet ein für WPF angepasstes Entwurfsmuster, um eine saubere Trennung von
Oberfläche und Programmcode zu gewährleisten. Es basiert großteils auf jahrelang
verwendeten und gut etablierten Entwurfsmustern, wie etwa das in C++ häufig verwendete
MVC Entwurfsmuster. WPF bietet nun einige erweiterte Möglichkeiten im Gegensatz zu
C++, wie etwa Datenbindung, durch welche einige Aufgaben dieses Modells überflüssig bzw.
umständlich werden ließen.
Der Begriff MVVM ist eine Kurzbezeichnung für die einzelnen Kernkomponenten des
Entwurfsmusters:
 Model
 View
 ViewModel
 DataAccess
Letztere ist zwar nicht zwingend nötig (und auch nicht in der Bezeichnung enthalten), wird
aber in den allermeisten Fällen eingesetzt.
Ziele des MVVM Entwurfsmusters:
 Schließen der zwischen XAML und Programmcode entstandenen Lücke
 Programmcode in der Code-Behind Datei minimieren
 Unit-Test-taugliche Programmstruktur gewährleisten
 Einfach wartbare Programmstruktur gewährleisten
Model
Die Model Klasse abstrahiert das Datenmodell des darzustellenden Objekts. Wenn bei
klassischer Programmierung die Daten jeweils als private Variable einer Klasse implementiert
werden, werden diese bei MVVM in eine eigene Model Klasse ausgegliedert. Die Model
Schicht stellt die Daten auf unterster Ebene dar. Das bedeutet, dass beim Einlesen eines
Objektes aus z.B. einer Datenbank oder einer XML-Datei die Datenstruktur unverändert
bleiben sollte.
In der Model Klasse kann weiters Serialisierung implementiert werden. Da die Daten bereits
in derselben Form vorliegen, wie sie aus der Datenbank bzw. XML-Datei kommen, sollten
keine größeren Umwandlungen zum Serialisieren der Daten erforderlich sein.
Die Model Klasse sorgt normalerweise weiters für die Überprüfung der Daten mittels
Validierung. Hierzu werden die einzelnen Eigenschaften separat auf Gültigkeit überprüft. Als
Beispiel sei ein Datenbankfeld erwähnt, das nicht NULL sein darf. Bei der Validierung kann
nun genau diese Bedingung überprüfen.
Rev. 1.0
Seite 2 von 6
23.06.2011
PPM – Schwerpunktfragen
Fragebogendesigner
Stefan Wurzinger
DataAccess
Die DataAccess Schicht des MVVM Entwurfsmusters ist grundsätzlich für die Verwaltung
der Datenmodelle zuständig, wenn – wie häufig –mehrere Objekte existieren. Die DataAccess
Schicht ist ebenso zum Einlesen von Datensätzen aus der Datenbank, einer XML-Datei oder
einem anderen Datenspeicher zuständig, genauso wie zum Speichern der Änderungen bzw.
neuen Datensätze im Datenspeicher. Dabei soll die Klasse den Datenzugriff soweit
abstrahieren, dass es für die restlichen Schichten unsichtbar bleibt, ob nun eine Datenbank,
eine XML-Datei oder eine interne Liste im Arbeitsspeicher als Datenspeicher dient. Die
Datenmodelle werden dabei meist in einer privaten Liste zwischengespeichert.
Da die Klasse für die Verwaltung der Datenmodelle zuständig ist wird sie meist „Repository“
genannt. Sie stellt Funktionen zum Hinzufügen und Löschen von Datenmodellen aus der Liste
bereit. Für die übergeordneten Schichten des MVVM Entwurfsmusters wird eine Liste der
Datenmodelle bereitgestellt, die zumeist eine Kopie der Intern verwendeten Liste darstellt.
Die DataAccess Schicht ist kein zwingend nötiger Teil des MVVM Entwurfsmusters, wird
aber zumeist verwendet. Solang z.B. nur ein Objekt eines bestimmten Datenmodells
gleichzeitig bestehen kann, dann ist die DataAccess Schicht um die Verwaltungsfunktion für
die Liste von Objekten ärmer, wodurch nur noch die Funktionen zum Speichern und Laden
der Daten von der Datenquelle übrig bleibt.
ViewModel
Die ViewModel Schicht des MVVM Entwurfsmusters stellt die Daten aus der Model Klasse
für die Programmoberfläche zur Verfügung. Hierbei werden die einzelnen Werte ein weiteres
Mal lokal im ViewModel zwischengespeichert. Im ViewModel befinden sich zusätzlich zu
den Eigenschaften, die direkt vom Model stammen, auch zumeist Hilfsvariablen für die
Datenbindung an die Programmoberfläche. So kann z.B. im Modell eine
Identifikationsnummer gespeichert werden und im ViewModel eine Liste mit der
Übersetzung von einer Identifikationsnummer zu einer für den Benutzer verständlichen
Bezeichnung. Intern wird im genannten Beispiel folglich die Identifikationsnummer
verwendet während der Benutzer nur die Bezeichnungen der einzelnen Optionen zu Gesicht
bekommt.
Das ViewModel sollte seine Daten unabhängig vom Model zwischenspeichern. Das bedeutet,
dass die im ViewModel befindlichen Daten nicht zu jedem Zeitpunkt mit denen im Model
übereinstimmen müssen. Wenn die Daten nicht zwischengespeichert werden und Änderungen
sofort auf die Daten im Model übernommen werden kann dies zu Problemen bei manchen
Anwendungsfällen führen. Das beste Beispiel hierfür ist wahrscheinlich der „Abbrechen“
bzw. „Änderungen Verwerfen“ Schalter in einem Dialog. Dieser wird normalerweise dazu
verwendet die vom Benutzer getätigten Änderungen nicht zu speichern. Wenn vom
ViewModel direkt die Daten im Model geändert werden ist es nur umständlich bzw. nicht
mehr möglich eine solche „Abbrechen“ Funktion zu implementieren.
Wenn ein Datenobjekt untergeordnete Elemente besitzt, wird der zugehörige Datenspeicher
(DataAccess Schicht; Repository) ebenfalls im ViewModel verwaltet. Dabei wird der
Datenspeicher auf Änderungen überwacht und bei entsprechendem einfügen bzw. entfernen
von Datenobjekten aus dem Datenspeicher können wiederum ViewModel Objekte für das
jeweilige Datenmodell angelegt bzw. gelöscht werden.
Rev. 1.0
Seite 3 von 6
23.06.2011
PPM – Schwerpunktfragen
Fragebogendesigner
Stefan Wurzinger
Im ViewModel kann auch für zusätzliche Funktionen verwendet werden, wie z.B. Selektieren
von bestimmten Datenobjekten. Oft wird auch ein Anzeigename definiert. Für die
Programmoberfläche können auch Commands zur Verfügung gestellt werden, die wiederum
mittels Datenbindung mit der Oberfläche verknüpft sind. Gängige Funktionen sind hierbei
Befehle zum Speichern oder Verwerfen von Formulardaten.
Das ViewModel ist auch für die Erkennung und Behandlung von Eingabefehlern zuständig.
Während Formatfehler (z.B. ungültige Zeichen für ein numerisches Eingabefeld) bereits
durch eine fehlgeschlagene Datenbindung erkannt werden, können inhaltliche Fehler, die aus
dem Zusammenhang mehrerer Felder ausgelöst wurden, nur im ViewModel erkannt werden.
Datenbindung wurde in den obigen Absätzen bereits mehrfach genannt und ist bei der
Separation von ViewModel und View die grundlegend notwendige Funktion von WPF, mit
der einerseits diese zwei Schichten verknüpft werden aber andererseits doch relativ
unabhängig voneinander bleiben.
View
Die View Schicht des MVVM Entwurfsmusters stellt den dem Benutzer sichtbaren Teil des
Programms dar. Hier werden die in den Schichten unterhalb aufbereiteten und gespeicherten
bzw. abgerufenen Daten dem Benutzer präsentiert.
Die View Schicht wird in XAML realisiert und sollte im Idealfall keine einzige Zeile manuell
geschriebenen Programmcode enthalten. Oft wird diese Forderung jedoch aufgrund
komplexer Situationen und umständlicher Lösungswege als Ausweg schlichtweg ignoriert.
Um die Daten vom ViewModel in die View Schicht zu transferieren (und umgekehrt) wird
eine mit WPF eingeführte neue Technik mit der Bezeichnung „Datenbindung“ verwendet.
Dabei wird im XAML Code der View Schicht auf bestimmte Eigenschaften über dessen
Namen verwiesen. Damit dies funktioniert, muss für die View Schicht der Datenkontext
entsprechend auf die Instanz eines ViewModel gesetzt werden, wozu oft eine Zeile
Programmcode in der View Schicht nötig ist.
LINQ
Verwendung im Maturaprojekt: u.a. Datenabfrage und -filterung zwischen DataAccess und ViewModel Schicht
LINQ stellt SQL-ähnliche Abfragen für u.a. C#-Collections direkt in C# bereit. Neben der
Verwendung für C#-Collections gibt es auch dutzende andere Zielsysteme für LINQ. Genannt
seien LINQ to SQL, LINQ to XML oder LINQ to DataSets. Man kann auch selbst LINQ um
Provider erweitern, wodurch z.B. Projekte wie LINQ to Google entstanden sind.
private static List<string> people = new List<string>()
{
"Granville", "John", "Rachel", "Betty",
"Chandler", "Ross", "Monica"
};
public static void Example1()
{
Rev. 1.0
Seite 4 von 6
23.06.2011
PPM – Schwerpunktfragen
Fragebogendesigner
Stefan Wurzinger
IEnumerable<string> query = from p in people select p;
foreach (string person in query)
{
Console.WriteLine(person);
}
}
Die Abfrage gibt an, welche Informationen aus der Datenquelle oder den Datenquellen
abgerufen werden sollen. Optional kann eine Abfrage auch angeben, wie diese Informationen
vor der Rückgabe sortiert, gruppiert und strukturiert werden sollen. Eine Abfrage wird in
einer Abfragevariablen gespeichert und mit einem Abfrageausdruck initialisiert. Um das
Schreiben von Abfragen zu erleichtern, hat C# eine neue Abfragesyntax eingeführt.
Wie bereits erwähnt, speichert die Abfragevariable selbst nur die Abfragebefehle. Die
tatsächliche Ausführung der Abfrage wird so lange verzögert, bis Sie die Abfragevariable in
einer foreach-Anweisung durchlaufen. Dieses Konzept wird als verzögerte Ausführung
bezeichnet.
Da die Abfragevariable selbst nie die Abfrageergebnisse enthält, kann man sie beliebig oft
ausführen. Wenn man beispielsweise über eine Datenbank verfügt, die ständig durch eine
separate Anwendung aktualisiert wird kann man in der Anwendung eine Abfrage erstellen,
die die neuesten Daten abruft, und diese Abfrage in bestimmten Abständen wiederholt
ausführen, um bei jeder Ausführung andere Ergebnisse abzurufen.
Um die unmittelbare Ausführung einer Abfrage zu erzwingen und ihre Ergebnisse
zwischenzuspeichern, kann man die ToList<TSource>-Methode oder die ToArray<TSource>Methode aufrufen.
LINQ-Abfragevariablen werden als IEnumerable<T> typisiert oder als abgeleiteter Typ, wie
zum Beispiel IQueryable<T>. Oft werden auch generische Typen zur Speicherung der
Abfrage verwendet:
var query = from p in products
where p.Name.StartsWith("A")
orderby p.ID
select p;
foreach ( var p in query ) {
Console.WriteLine ( p.Name );
}
Das var-Schlüsselwort bietet sich besonders an, wenn der Variablentyp offensichtlich ist oder
wenn die ausdrückliche Angabe von geschachtelten, generischen Typen, z. B. solche, die bei
Gruppenabfragen erstellt werden, nicht so wichtig ist.
Alternativ zur neuen C# Abfragesyntax können auch so genannte Erweiterungsmethoden mit
Lambda-Ausdrücken verwendet werden um dieselbe Funktionalität zu erreichen.
var query = products
.Where(p => p.Name.StartsWith("A"))
.OrderBy(p => p.ID);
foreach ( var product in query ) {
Console.WriteLine ( product.Name );
}
Rev. 1.0
Seite 5 von 6
23.06.2011
PPM – Schwerpunktfragen
Rev. 1.0
Fragebogendesigner
Seite 6 von 6
Stefan Wurzinger
23.06.2011
Herunterladen