21.3 Verschiedene Datenbindungsquellen 

21.3.1 Bindung an einfache Objekte 

Im letzten Abschnitt hatten wir bereits die Klasse Person bereitgestellt und mit dieser Datenbindung betrieben. Fassen wir an dieser Stelle alle wesentlichen Gesichtspunkte zusammen, die beim Entwurf einer bindungsfähigen Klasse beachtet werden sollten:
- Die Klasse muss die Schnittstelle INotifyPropertyChanged implementieren. Diese ist im Namespace System.ComponentModel enthalten.
- Die Klasse sollte einen parameterlosen Konstruktor haben.
- Als Datenquelle kommen nur Eigenschaften in Betracht, die über eine Eigenschaftsmethode veröffentlicht werden, da öffentliche Felder nicht gebunden werden können.
In den Beispielen zuvor hatten wir mit
this.DataContext = person;
ein Objekt vom Typ Person an den DataContext des Window gebunden. Hier ist der parameterlose Konstruktor nicht unbedingt notwendig. Person-Objekte lassen sich aber auch direkt im XAML-Code angeben. Dazu ist zum Beispiel der Resources-Abschnitt im Window gut geeignet. Im XAML-Code wird zuvor die Angabe des Namespaces benötigt, in dem Person definiert ist. Deshalb muss der Namespace des Typs Person in der XAML-Datei bekannt gegeben werden:
xmlns:src="clr-namespace:NamespaceOfPerson"
Nun kann die Klasse Person auch im Resources-Abschnitt der XAML-Datei verwendet werden. Dabei wird der parameterlose Konstruktor aufgerufen. Optional können den Eigenschaften sofort Werte zugewiesen werden:
<Window.Resources> <src:Person x:Key="pers1" Name="Schmidt" Alter="43" /> <src:Person x:Key="pers2" Name="Müller" Alter="21" /> </Window.Resources>
Anschließend können Sie die Möglichkeiten der Datenbindung benutzen.
Möchten Sie mit C#-Code auf das im XAML-Code erzeugts Objekt zugreifen, müssen Sie sich zuerst die Referenz auf das im XAML-Code erzeugte Objekt besorgen. Hier hilft Ihnen die Methode FindResource der Klasse FrameworkElement weiter, einer Klasse, von der auch Window abgeleitet ist. Der Methode wird als Argument das im XAML-Code erzeugte Objekt übergeben. Der Rückgabewert muss noch in den richtigen Typ konvertiert werden.
private void button1_Click(object sender, RoutedEventArgs e) {
Person pers = (Person)FindResource("pers1");
MessageBox.Show("Name und Alter: " + pers.Name + ", " + pers.Alter);
pers.Zuname = "Fischer";
}
21.3.2 Auflistungen binden 

Im Abschnitt zuvor haben Sie gesehen, dass an eine Klasse besondere Anforderungen gestellt werden müssen, damit sie XAML-bindungsfähig wird. Das gilt in gleichem Maße auch für eine Auflistung. Hier lautet die Forderung, dass die Auflistung neben INotifyPropertyChanged eine weitere Schnittstelle implementieren muss, nämlich INotifyCollectionChanged. Mit der generischen Klasse ObservableCollection<> stellt .NET Ihnen bereits eine solche Klasse zur Verfügung. Sie gehört zum Namespace System.Collections.ObjectModel. Sie kann ähnlich benutzt werden wie eine herkömmliche Auflistung – mit dem Unterschied, dass sie unter WPF bindungsfähig ist.
Damit wird es sehr einfach, eine Liste mehrerer Objekte vom Typ Person zu beschreiben:
ObservableCollection<Person> personen = new ObservableCollection<Person>();
Damit steht uns ein Auflistungsobjekt zur Verfügung, das wir zum Füllen von Steuerelementen wie der ComboBox oder der ListBox einsetzen können.
Im Kapitel habe ich beschrieben, wie Sie mit Elementen vom Typ ListBoxItem und ComboBoxItem Einträge erstellen können. Diese Elemente spielen bei der Datenbindung keine Rolle mehr. Stattdessen betritt nun die Eigenschaft ItemSource die Bühne. Diese Eigenschaft bietet die Möglichkeit der Anbindung von Objektmengen. Grundsätzlich lässt sich jede Objektmenge anbinden, vorausgesetzt, diese implementiert die Schnittstelle IEnumerable. Das gilt nicht nur für ObservableCollection<T>, sondern auch gleichermaßen für ArrayList, List<T> und jedes klassische Array.
Ein komplettes Beispiel, in dem eine ListBox an eine Collection gebunden wird, wollen wir uns nun ansehen.
// --------------------------------------------------------- // Beispiel: ...\Kapitel 21\ListBoxBinding // --------------------------------------------------------- <Window ... > <Grid> <Grid.RowDefinitions> <RowDefinition Height="120" /> <RowDefinition Height="30"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <ListBox Grid.Column="0" Name="lstPersonen" ItemsSource="{Binding}" DisplayMemberPath="Name"/> <Button Grid.Row="1" Height="30" Name="btnAddPerson" Click="btnAddPerson_Click">Hinzufügen </Button> <Button Grid.Row="2" Height="30" Name="btnShowPerson" Click="btnShowPerson_Click">Abrufen </Button> </Grid> </Window>
Hier ist noch der C#-Code dazu:
public partial class MainWindow : Window { private ObservableCollection<Person> personen = new ObservableCollection<Person>(); public MainWindow() { personen.Add(new Person("Meier", 67)); personen.Add(new Person("Fischer", 23)); personen.Add(new Person("Schmidt", 45)); personen.Add(new Person("Serna", 26)); personen.Add(new Person("Müller", 39)); InitializeComponent(); this.DataContext = personen; lstPersonen.SelectedIndex = 3; } private void btnAddPerson_Click(object sender, RoutedEventArgs e){ personen.Add(new Person("Walter", 54)); } private void btnShowPerson_Click(object sender, RoutedEventArgs e){ Person p = (Person)lstPersonen.SelectedItem; MessageBox.Show(p.Name + " / " + p.Alter); } }
Abbildung 21.3 Ausgabe des Beispielprogramms »ListBoxBinding«
Im Konstruktor des Window wird eine Liste vom Typ der generischen Collection ObservableCollection mit Person-Objekten gefüllt. Die Bindung der Collection erfolgt an die Eigenschaft DataContext des Window. Mit SelectedIndex wird das Element mit dem Index 3 in der Liste vorselektiert. Des Weiteren enthält der Code zwei Ereignishandler für zwei sich im Window befindliche Buttons. Der erste Button gestattet das Hinzufügen eines weiteren Person-Objekts, der zweite wertet mit SelectedItem den aktuell selektierten Eintrag in der ListBox aus. Bei dem zurückgelieferten Element handelt es sich um das entsprechende Person-Objekt. Nach vorhergehender Konvertierung können die Eigenschaften des Objekts ausgewertet werden.
Im XAML-Codes wird die ListBox mit
ItemsSource="{Binding}"
an die Datenquelle gebunden. Mit der Eigenschaft DisplayMemberPath geben wir eine zusätzliche Information, was aus dem Datenobjekt angezeigt werden soll.