21.5 Daten konvertieren 

Haben Sie sich schon einmal darüber Gedanken gemacht, wie die folgende Angabe umgesetzt wird?
<Button Background="Blue" />
Wir übergeben der Eigenschaft Background eine Zeichenfolge, obwohl die Eigenschaft vom Typ Brush ist. Die Zeichenfolge wird anscheinend in ein Objekt vom Typ Brush umgewandelt. Dabei spielen Klassen eine wichtige Rolle, die als Konvertierungsklassen oder Value Converter bezeichnet werden.
Konvertierungsklassen kommen zum Einsatz, wenn man gewisse Eigenschaften eines Objekts anders anzeigen möchte, als es die ToString-Methode vorgibt. Darüber hinaus kann man auch Konvertierungsklassen bereitstellen, die darzustellende Werte berechnen, beispielsweise
<TextBlock Text="{Binding Nettopreis*1.19}"
oder:
<TextBlock Text="{Binding Vorname} {Binding Zuname}" />
Wie schon die Bezeichnung zum Ausdruck bringt, wird eine Konvertierung durch eine Klassendefinition beschrieben. Die Klasse muss die Schnittstelle IValueConverter implementieren. Die Schnittstelle, die zum Namespace System.Windows.Data gehört, erzwingt die Implementierung der beiden Methoden Convert und ConvertBack. Convert beschreibt hierbei die Konvertierung der Quelle in Richtung Ziel, ConvertBack die entgegengesetzte Richtung.
Das Interface ist wie folgt definiert:
public interface IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture); public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) }
Der erste Parameter beschreibt den Wert der Bindungsquelle, der zweite den Typ der Bindungsziel-Eigenschaft. Benötigt der Konverter weitere Parameter, können diese dem dritten Parameter übergeben werden. Mit dem vierten Parameter vom Typ CultureInfo kann der Konverter für eine bestimmte Kultur spezifiziert werden.
Haben Sie die Konvertierungsklasse geschrieben (z. B. die Klasse MyConverter), müssen Sie den Namespace, zu dem die Klasse gehört, dem Window bekannt geben:
<Window x:Class="WpfApplication.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication" Title="MainWindow" Height="350" Width="525">
Danach erstellen Sie im Resources-Abschnitt des Window ein Objekt des Konverters:
<Window.Resources> <local:MyConverter x:Key="converter" /> </Window.Resources>
Und von nun an können Sie den Converter überall dort einsetzen, wo Datenbindung verwendet wird, z. B. hier:
<Button Content="{Binding SomeFunction, Converter={StaticResource converter}}" />
21.5.1 Beispielprogramm 

Sehen wir uns noch die Theorie anhand eines Beispiels an. Dem Programm liegt die Klasse Aktie zugrunde, die mit den beiden Eigenschaften Unternehmen und Wert die notdürftigsten Elemente aufweist.
// --------------------------------------------------------- // Beispiel: ...\Kapitel 21\ConverterSample // --------------------------------------------------------- class Aktie { public string Unternehmen { get; set; } public double Wert { get; set; } }
Der Wert der Aktie soll in einem WPF-Window als Zahl mit zwei Stellen hinter dem Komma dargestellt werden. Hierzu ist eine Konverterklasse notwendig, die wie folgt definiert ist:
using System.Globalization;
[ValueConversion(typeof(Double), typeof(String))]
class NumberConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
return ((double)value).ToString(parameter as string,
culture.NumberFormat);
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
return System.Convert.ToDouble(value as string, culture.NumberFormat);
}
}
Die Klasse implementiert die Schnittstelle IValueConverter mit den beiden Methoden Convert und ConvertBack. Auf die Beschreibung des Codes der beiden Methoden sei an dieser Stelle verzichtet. Darüber hinaus ist die Klasse mit dem Attribut ValueConversionAttribute verknüpft. Das Attribut ist generell nicht zwingend notwendig, es zu verwenden, ist aber guter Programmierstil, um den Entwicklungstools die Datentypen anzuzeigen.
Was zum Schluss noch bleibt, ist der XAML-Code.
<Window ... xmlns:local="clr-namespace:WpfApplication" > <Window.Resources> <local:NumberConverter x:Key="converter" /> <local:Aktie x:Key="aktie" Unternehmen="Tollsoft" Wert="211.73536" /> </Window.Resources> <StackPanel> <TextBlock Height="30" Text="{Binding Source={StaticResource aktie}, Path=Wert, Converter={StaticResource converter}, ConverterParameter='#.##'}" /> <TextBlock Height="30" Text="{Binding Source={StaticResource aktie}, Path=Unternehmen}" /> </StackPanel> </Window>
Im ersten Moment scheint die Angabe des oberen TextBocks schwer verständlich. Doch gehen wir der Reihe nach die Angaben zu der Eigenschaft Text durch. Zuerst wird die Text-Eigenschaft an die statische Ressource aktie gebunden. Dazu bedienen wir uns eines Binding-Objekts. Binding-Objekte haben aber nicht nur die schon bekannten Eigenschaft ElementName und Path, sondern weisen mit Converter, ConverterParameter und auch ConverterCulture weitere auf. Über Converter wird der Konverter als StaticResource genannt. Soll das Converter-Objekt auch nicht mit einem Parameter »gefüttert« werden oder mit zusätzlichen Informationen zur Kultur, dann werden die Eigenschaften ConverterParameter und ConverterCulture berücksichtigt.
Anmerkung |
Weiter unten in diesem Kapitel finden Sie das Beispielprogramm WPF_ADOSample2, in dem ebenfalls eine Converter-Klasse zum Einsatz kommt. |
21.5.2 Mehrfachbindungen und »Converter«-Klassen 

Bisher haben wir nur mit Datenbindungen gearbeitet, die eine Steuerelementeigenschaft mit genau einer Eigenschaft des Datenobjekts zuließen. Liegt der Datenbindung ein Objekt der Klasse Person zugrunde, müssen wir mit dem derzeitigen Kenntnisstand den Vor- und den Zunamen in zwei separaten Steuerelementen anzeigen lassen, beispielsweise:
<Window.Resources> <local:Person x:Key="pers" Vorname="Peter" Zuname="Holzschuh" /> </Window.Resources> <StackPanel> <TextBlock Text="{Binding Source={StaticResource pers}, Path=Vorname}" /> <TextBlock Text=", " /> <TextBlock Text="{Binding Source={StaticResource pers}, Path=Zuname}" /> </StackPanel>
Wir möchten aber die Angaben in einem TextBlock-Element zusammenfassen. Für solche Anwendungsfälle bietet die WPF mit dem Multiple Binding eine Lösung an. Die Klasse, die uns diese Möglichkeit eröffnet, heißt MultiBinding. Grundsätzlich könnte die Lösung wie folgt aussehen:
<TextBlock> <TextBlock.Text> <MultiBinding> <Binding Source="{StaticResource person}" Path="Zuname" /> <Binding Source="{StaticResource person}" Path="Vorname" /> </MultiBinding> </TextBlock.Text> </TextBlock>
Tippen Sie diesen Code in den Code-Editor, wird eine Fehlermeldung auftauchen. Der Grund ist sehr einfach: Das MultiBinding-Objekt weiß nicht, wie es die Daten verarbeiten soll. Dazu ist ein Konverter notwendig. Da mehrere Daten an den Konverter übertragen werden müssen, eignet sich ein Konverter vom Typ IValueConverter nicht mehr. Stattdessen kommt jetzt die Schnittstelle IMultiValueConverter ins Spiel.
Die Schnittstelle IMultiValueConverter ist der Schnittstelle IValueConverter sehr ähnlich. Der Unterschied bei der Methode Convert ist im ersten Übergabeparameter zu finden, der vom Typ object[] ist, um mehrere Daten empfangen zu können. Bei ConvertBack ist es dementsprechend der zweite Parameter, der vom Typ Type[] ist.
[ValueConversion(typeof(String), typeof(String))]
class PersonConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter,
CultureInfo culture) {
return (string)values[0] + ", " + (string)values[1];
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, CultureInfo culture) {
throw new NotSupportedException();
}
}
Hinweis |
Es ist nicht unüblich, dass die Methode ConvertBack nicht implementiert wird. Sie sollten in einem solchen Fall eine Ausnahme vom Typ NotSupportedException auslösen. |
Zum Schluss müssen wir noch das MultiBinding-Element um den entsprechenden Konverter ergänzen.
<Window ... xmlns:local="clr-namespace:WpfApplication"> <Window.Resources> <local:PersonConverter x:Key="converter" /> <local:Person x:Key="pers" Vorname="Peter" Zuname="Holzschuh" /> </Window.Resources> <StackPanel> <TextBlock> <TextBlock.Text> <MultiBinding Converter="{StaticResource converter}"> <Binding Source="{StaticResource pers}" Path="Zuname" /> <Binding Source="{StaticResource pers}" Path="Vorname" /> </MultiBinding> </TextBlock.Text> </TextBlock> </StackPanel> </Window>