Data Templates sind eine feine Sache. Sie erlauben es, die Präsentation von Daten sehr einfach und präzise zu definieren. Hierzu vorab ein einfaches Beispiel in Form einer Silverlight Listbox, die die 16 deutschen Bundesländer auflistet. Standardmäßig ist das Data Template für die Items einer Listbox einfach ein ContentPresenter. Dieser ruft an den Items die Funktion ToString auf und stellt den resultierenden Text dar. Durch das Zuweisen eines Data Templates wird aus dem simplen String eine viel informativere Darstellung.
Hier kann man das Beispiel ausprobieren
In diesem Beispiel wird beim Drücken von „Apply Template“ ein Data Template geladen und an die Property ItemTemplate der Listbox zugewiesen. Dies hat zur Folge, dass die Listbox zur Darstellung ihrer Items nicht mehr einfach nur den Default Content Presenter verwendet, sondern ein individuell auf die Daten zugeschnittenes Template. Diese Funktionalität ist in WPF und Silverlight nahezu identisch.
WPF bietet nun zusätzlich noch die Möglichkeit, das Template abhängig von Daten Items individuell auszuwählen. Dazu muss man eine eigene Klasse MyTemplateSelector von DataTemplateSelector ableiten und darin die Funktion SelectTemplate überschreiben. Eine Listbox hat wie jedes Items Control in WPF die Property ItemTemplateSelector. Dieser Property muss man MyTemplateSelector zuweisen. Die Listbox ruft nun für jedes Daten Item was sie in Ihrer Items Source findet die Funktion SelectTemplate in MyTemplateSelector auf. So hat man die Möglichkeit, im Code abhängig von den Daten jedes einzelnen Items unterschiedliche Templates auszuwählen.
Lösung in Silverlight
Silverlight 3 kennt diese WPF Funktionalität nicht, aber man kann sich eine vergleichbare Funktionalität einfach selber bauen. Da Silverlight für alle Daten Items immer das gleiche Template verwendet, muss man einen Trick anwenden: Man baut sich ein spezielles Data Template, welches genau nur ein Content Control beinhaltet. In diesem Content Control redefiniert man die Funktion OnContentChanged und wechselt hier einfach das Data Template des Content Controls abhängig vom aktuellen Content. Diese zusätzliche Indirektion liefert im Ergebnis das gleiche wie in WPF.
Ein Beispiel soll zeigen, wie es im Einzelnen funktioniert. Um es einfach zu halten, bauen wir das obige Beispiel mit den Bundesländern so um, dass alle Bundesländer, die Stadtstaaten sind, mit einem anderen Template dargestellt werden sollen.
Zunächst die Klasse DataTemplateSelector für Silverlight:
/// <summary>
/// Base class for Silverlight DataTemplateSelector.
/// </summary>
public class DataTemplateSelector : ContentControl
{
/// <summary>
/// Called when the value of the <see cref="System.Windows.Controls.ContentControl.Content"/> property changes.
/// </summary>
protected override void OnContentChanged(object oldContent, object newContent)
{
base.OnContentChanged(oldContent, newContent);
var template = SelectTemplate(newContent, null);
if (template != null)
ContentTemplate = template;
}
/// <summary>
/// When overridden in a derived class, returns a DataTemplate based on custom logic.
/// </summary>
/// <param name="item">The data object for which to select the template. </param>
/// <param name="notUsedInSilverlight">The not used in silverlight. Exists for WPF compatibility only.</param>
/// <returns>Returns a DataTemplate or a null reference. The default value is a null reference.</returns>
public virtual DataTemplate SelectTemplate(object item, DependencyObject notUsedInSilverlight)
{
return null;
}
}
Wie man sieht, ist diese Klasse von ContentControl abgeleitet. Ändert sich der Content, wird über die Funktion OnContentChanged die Funktion SelectTemplate aufgerufen. Diese Funktion hat die gleiche Signatur wie ihr WPF Gegenstück.
Um unser Beispiel umzusetzen wird eine neue Klasse MyTemplateSelector von DataTemplateSelector abgeleitet:
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var stateInfo = item as States.StateInfo;
if (stateInfo != null)
{
if (stateInfo.Capital != "–") // the state is not one of the three German city-states
{
// This variation demonstrates how to read the template from a resource file
var info = Application.GetResourceStream(new Uri("/DataTemplateSelectorSample;component/Assets/RegularStateTemplate.xaml", UriKind.Relative));
using (var reader = new StreamReader(info.Stream))
{
return XamlReader.Load(reader.ReadToEnd()) as DataTemplate;
}
}
// This variation demonstrates how to read the template from the Application resources
return Application.Current.Resources["CityStateTemplate"] as DataTemplate;
}
return null;
}
}
Anhand der Property Capital wird unterschieden, ob es sich bei einem Daten Item um ein Stadtstaat oder ein normales Bundesland handelt. Je nachdem was es ist, werden unterschiedliche Templates geladen.
Zuletzt muss noch die Listvox mit der Klasse MyTemplateSelector verknüpft werden:
<ListBox x:Name="lbxStatesOfGermany" Margin="10 10 10 10">
<ListBox.ItemTemplate>
<DataTemplate>
<tmpl:MyTemplateSelector Content="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Wie bereits gesagt, wird MyTemplateSelector zum Item Template der Listbox. Die Listbox erzeugt nun für jedes ihrer Daten Items eine Instanz von MyTemplateSelector und bindet das jeweilige Item daran. Abhängig vom Item setzt nun jede Instanz von MyTemplateSelector wiederum ihr eigenes Content Template.
Hier das Ergebnis zum ausprobieren
Fazit
Der Silverlight Data Template Selector lässt sich genau so einfach verwenden wie in WPF. Er funktioniert natürlich nicht nur mit Listboxen, sondern mit alle Items Controls. Dieses Beispiel zeigt wieder einmal, wie einfach und elegant man Silverlight erweitern kann.
Hier der gesamte Quellcode der beiden Beispiele zum Downloaden:
SilverlightDataTemplateSelector.zip (202,00 kb)