21.4 DataTemplates festlegen 

In der ListBox des vorhergehenden Beispiels wird nur der Name angezeigt. Im Grunde genommen erstaunt das nicht, denn woher soll auch die ListBox wissen, welche Darstellungsform der Anzeige wir uns wünschen. Die ListBox reagiert nur auf die Angabe, die wir unter der Eigenschaft DisplayMemberPath gemacht haben. Die Eigenschaft selbst erlaubt keine komplexeren Darstellungsmöglichkeiten.
DataTemplates schaffen hier Abhilfe. Sie gestatten, die Inhalte und deren Darstellung exakt festzulegen. Damit wird es uns möglich, nicht nur mehrere Inhalte in einer Zeile auszugeben. Wir können darüber hinaus auch in jeglicher anderer Weise die Darstellung manipulieren, beispielsweise durch alternierende Farben.
DataTemplates ähneln den ControlTemplates. Sie können sie im Window.Resources-Abschnitt festlegen und die Darstellung der Daten ähnlich beschreiben, wie Sie auch einem Steuerelement mit einem ControlTemplate zu einer anderen Darstellung verhelfen. Der folgende XAML-Code zeigt, wie Sie das Beispiel ListBoxBinding ergänzen können, um in der ListBox neben dem Namen der Person auch deren Alter auszugeben. Beachten Sie die Angabe des Datenobjekts mit DataType als Eigenschaft des DataTemplate-Elements. Damit wird zum Ausdruck gebracht, dass Person-Objekte durch diese Schablone ersetzt werden.
<Window.Resources> <DataTemplate x:Key="PersonTemplate" DataType="{x:Type local:Person}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Width="100" Grid.Column="0" Text="{Binding Path=Name}" /> <TextBlock Width="30" Grid.Column="1" Text="{Binding Path=Alter}" /> </Grid> </DataTemplate> </Window.Resources>
Eine Anpassung des ListBox-Elements ist ebenfalls notwendig. Es benötigt nun nicht mehr die Angabe der Eigenschaft DisplayMemberPath. Dafür geben Sie der Eigenschaft ItemTemplate den Key der Ressource an.
<ListBox Grid.Column="0" Name="lstPersonen" ItemsSource="{Binding}" ItemTemplate="{StaticResource PersonTemplate}" />
In Abbildung 21.4 sehen Sie, wie die ListBox unter Zuhilfenahme der DataTemplate die Elemente anzeigt.
Die Schablone lässt sich auch direkt im Steuerelement angeben. Hierfür für die Eigenschaft ItemTemplate bereitgestellt, in die das DataTemplate eingebettet wird.
<ListBox Grid.Column="0" Name="lstPersonen" ItemsSource="{Binding}"> <ListBox.ItemTemplate> <DataTemplate DataType="{x:Type local:Person}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition /><ColumnDefinition /> </Grid.ColumnDefinitions> <TextBlock Width="100" Grid.Column="0" Text="{Binding Path=Name}" /> <TextBlock Width="30" Grid.Column="1" Text="{Binding Path=Alter}" /> </Grid> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
Der Nachteil ist, dass dieses DataTemplate nicht von anderen Steuerelementen genutzt werden kann.
Abbildung 21.4 ListBox mit DataTemplate
21.4.1 Trigger 

Vorlagen können mit Triggern ausgestattet werden. Das gilt nicht nur für ContentTemplate, sondern auch für DataTemplate. Trigger gestatten es einem Template, auf Veränderungen von Daten zu reagieren und die Darstellung anzupassen. Wenn Sie Trigger wie in Kapitel 20, »Konzepte der WPF«, beschrieben einsetzen, kann der Trigger nur auf die Veränderung des Steuerelements selbst reagieren. Um auf eine Veränderung der Datenquelle reagieren zu können, gibt es mit DataTrigger und MultiDataTrigger zwei weitere Klassen.
DataTrigger arbeiten mit der Datenverbindung. Sie können eine beliebige Eigenschaft des Objekts abfragen und dann entsprechend darauf reagieren. Das sei am Beispiel der Klasse Person gezeigt. Dabei sollen die Person-Objekte, die das Rentenalter erreicht oder überschritten haben, in der ListBox mit rotem Hintergrund dargestellt werden. Aus diesem Grund erweitern wir die Klasse um die Eigenschaft Rente.
public class Person : INotifyPropertyChanged {
private bool _Rente;
public bool Rente {
get {
return _Rente;
}
private set {
_Rente = value;
OnPropertyChanged(new PropertyChangedEventArgs("Rente"));
}
}
...
}
Eigentlich würde uns hier eine schreibgeschützte Eigenschaft ausreichen. Da wir aber erwarten, dass datenbindende Objekte bei einer Änderung der Eigenschaft Notiz von der Änderung nehmen, muss das Ereignis PropertyChanged ausgelöst werden. Das kann nur im Setter passieren. Um dennoch nach außen eine schreibgeschützte Eigenschaft vorzugaukeln, ist der Setter private.
Das Window soll gegenüber dem vorhergehenden Beispiel ein ansehnlicheres Layout erfahren. Darüber hinaus soll gezeigt werden, wie eine Person gelöscht werden kann (was im Grunde genommen kein großes Problem sein sollte). Zum Testen der datengebundenen Eigenschaft Rente enthält das Fenster eine Schaltfläche, mit der das Alter der aktuell selektierten Person auf einen statischen Wert vergrößert wird, der dem definierten Rentenalter entspricht (siehe Abbildung 21.5).
Abbildung 21.5 Fenster des Beispiels »DataTriggerSample«
Der DataTrigger ähnelt einem Eigenschaftstrigger. Er wird an die Eigenschaft Rente der Datenbindung gebunden. Ist die Eigenschaft True, wird der Hintergrund des Grid-Elements auf die Farbe Rot gesetzt.
<DataTemplate x:Key="PersonTemplate" DataType="{x:Type local:Person}"> <Grid Name="personzeile"> ... </Grid> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=Rente}" Value="True"> <Setter Property="Background" Value="Red" TargetName="personzeile" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>
Aus Platzgründen soll an dieser Stelle der XAML-Code nicht vollständig wiedergegeben werden. Einzig und allein der C#-Code der vier Click-Ereignisse sei an dieser Stelle gezeigt:
// ---------------------------------------------------------
// Beispiel: ...\Kapitel 21\DataTriggerSample
// ---------------------------------------------------------
private void btnAddPerson_Click(object sender, RoutedEventArgs e) {
personen.Add(new Person("Walter", 54));
}
private void btnSelectedPerson_Click(object sender, RoutedEventArgs e) {
Person p = (Person)lstPersonen.SelectedItem;
string str = "Folgende Person wurde ausgewählt:\n";
MessageBox.Show(str + p.Name + " / " + p.Alter);
}
private void btnDeletePerson_Click(object sender, RoutedEventArgs e) {
personen.Remove((Person)lstPersonen.SelectedItem);
}
private void btnEditAlter_Click(object sender, RoutedEventArgs e) {
((Person)lstPersonen.SelectedItem).Alter = 69;
}