Bindungswertkonverter in Xamarin.Forms
Datenbindungen wandeln Daten in der Regel von einer Quelleigenschaft in eine Zieleigenschaft und in einigen Fällen von der Zieleigenschaft in die Quelleigenschaft um. Diese Umwandlung ist einfach, wenn die Quell- und Zieleigenschaften vom gleichen Typ sind oder wenn ein Typ über eine implizite Konvertierung in den anderen Typ konvertiert werden kann. Wenn dies nicht der Fall ist, muss eine Typkonvertierung durchgeführt werden.
Im Artikel Formatierung von Zeichenfolgen wurde erläutert, wie Sie die StringFormat
-Eigenschaft der Datenbindung zum Konvertieren von Typen in eine Zeichenfolge verwenden können. Für andere Konvertierungstypen müssen Sie speziellen Code in einer Klasse schreiben, die die Schnittstelle IValueConverter
implementiert. (Die Universelle Windows-Plattform enthält eine ähnliche Klasse IValueConverter
namens im Windows.UI.Xaml.Data
Namespace, aber dies IValueConverter
befindet sich im Xamarin.Forms
Namespace.) Klassen, die implementiert IValueConverter
werden, werden als Wertkonverter bezeichnet, werden aber auch häufig als Bindungskonverter oder Bindungswertkonverter bezeichnet.
Die IValueConverter-Schnittstelle
Angenommen, Sie möchten eine Datenbindung definieren, bei der die Quelleigenschaft vom Typ int
, die Zieleigenschaft jedoch vom Typ bool
ist. Sie möchten, dass diese Datenbindung einen false
-Wert erzeugt, wenn die Integerquelle gleich 0 ist und andernfalls true
.
Dies erreichen Sie mit einer Klasse, die die IValueConverter
-Schnittstelle implementiert:
public class IntToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value != 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? 1 : 0;
}
}
Sie legen eine Instanz dieser Klasse auf die Eigenschaft Converter
der Binding
-Klasse oder auf die Eigenschaft Converter
der Binding
-Markuperweiterung fest. Diese Klasse wird Teil der Datenbindung.
Die Convert
-Methode wird aufgerufen, wenn Daten von der Quelle zum Ziel in OneWay
- oder TwoWay
-Bindungen verschoben werden. Der Parameter value
ist das Objekt oder der Wert der Quelle der Datenbindung. Die Methode muss einen Wert des Datenbindungszieltyps zurückgeben. Die hier dargestellte Methode wandelt den value
-Parameter in eine int
-Eigenschaft um und vergleicht diese dann mit „0“ für einen bool
-Rückgabewert.
Die ConvertBack
-Methode wird aufgerufen, wenn Daten vom Ziel zur Quelle in TwoWay
- oder OneWayToSource
-Bindungen verschoben werden. ConvertBack
führt die umgekehrte Konvertierung aus: Diese Methode geht davon aus, dass der value
-Parameter ein boolescher Wert (bool
) aus dem Ziel ist und konvertiert diesen in einen int
-Rückgabewert für die Quelle.
Wenn in der Datenbindung auch eine StringFormat
-Einstellung enthalten ist, wird der Wertkonverter aufgerufen, bevor das Ergebnis als Zeichenfolge formatiert wird.
Auf der Seite "Schaltflächen aktivieren" im Beispiel wird die Verwendung dieses Wertkonverters in einer Datenbindung veranschaulicht. Der Konstruktor IntToBoolConverter
wird im Ressourcenverzeichnis der Seite instanziiert. Anschließend wird mit einer StaticResource
-Markuperweiterung auf diesen verwiesen, um die Eigenschaft Converter
in zwei Datenbindungen festzulegen. Es ist üblich, dass Datenkonverter zwischen mehreren Datenbindungen auf der Seite gemeinsam genutzt werden:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.EnableButtonsPage"
Title="Enable Buttons">
<ContentPage.Resources>
<ResourceDictionary>
<local:IntToBoolConverter x:Key="intToBool" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Padding="10, 0">
<Entry x:Name="entry1"
Text=""
Placeholder="enter search term"
VerticalOptions="CenterAndExpand" />
<Button Text="Search"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
IsEnabled="{Binding Source={x:Reference entry1},
Path=Text.Length,
Converter={StaticResource intToBool}}" />
<Entry x:Name="entry2"
Text=""
Placeholder="enter destination"
VerticalOptions="CenterAndExpand" />
<Button Text="Submit"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
IsEnabled="{Binding Source={x:Reference entry2},
Path=Text.Length,
Converter={StaticResource intToBool}}" />
</StackLayout>
</ContentPage>
Wenn ein Wertkonverter auf mehreren Seiten Ihrer Anwendung verwendet wird, können Sie ihn im Ressourcenverzeichnis in der Datei App.xaml instanziieren.
Auf der Seite Enable Buttons (Aktivieren von Schaltflächen) ist eine allgemeine Anforderung dargestellt, wenn eine Schaltfläche (Button
) einen Vorgang ausführt, die auf dem vom Benutzer in eine Entry
-Ansicht eingegebenen Text basiert. Wenn nichts in die Entry
-Ansicht eingegeben wurde, sollte die Schaltfläche (Button
) deaktiviert werden. Jede Button
-Klasse enthält eine Datenbindung für ihre IsEnabled
-Eigenschaft. Die Quelle der Datenbindung ist die Length
-Eigenschaft der Text
-Eigenschaft der entsprechenden Entry
-Ansicht. Wenn diese Length
-Eigenschaft nicht 0 (null) ist, gibt der Wertkonverter true
zurück, und die Schaltfläche (Button
) wird aktiviert:
Beachten Sie, dass die Text
-Eigenschaft in jeder Entry
mit einer leeren Zeichenfolge initialisiert wird. Die Text
-Eigenschaft ist standardmäßig null
, aber die Datenbindung funktioniert in diesem Fall nicht.
Einige Wertkonverter werden speziell für bestimmte Anwendungen geschrieben, andere werden wiederum verallgemeinert. Wenn Sie wissen, dass ein Wertkonverter nur in OneWay
-Bindungen verwendet wird, kann die ConvertBack
-Methode einfach null
zurückgeben.
Die oben dargestellte Convert
-Methode setzt voraus, dass das value
-Argument vom Typ int
ist und der Rückgabewert demnach vom Typ bool
sein muss. Gleichzeitig geht die ConvertBack
-Methode davon aus, dass das value
-Argument vom Typ bool
und der Rückgabewert int
ist. Sollte dies nicht der Fall sein, wird eine Laufzeitausnahme ausgelöst.
Sie können Wertkonverter so schreiben, dass sie verallgemeinert werden und mehrere unterschiedliche Datentypen akzeptieren. Die Methoden Convert
und ConvertBack
können die Operatoren as
oder is
mit dem value
-Parameter verwenden oder GetType
für diesen Parameter aufrufen, um dessen Typ zu bestimmen und dann einen entsprechenden Vorgang auszuführen. Der erwartete Typ des Rückgabewerts jeder Methode wird vom targetType
-Parameter bestimmt. In einigen Fällen werden Wertkonverter mit Datenbindungen verschiedener Zieltypen verwendet. Der Wertkonverter kann das Argument targetType
verwenden, um eine Konvertierung für den richtigen Typ durchzuführen.
Sollte sich die durchgeführte Konvertierung für unterschiedliche Kulturen unterscheiden, verwenden Sie zu diesem Zweck den culture
-Parameter. Das Argument parameter
für Convert
und ConvertBack
wird später in diesem Artikel behandelt.
Eigenschaften von Bindungskonvertern
Wertkonverterklassen können über Eigenschaften und generische Parameter verfügen. Dieser bestimmte Wertkonverter konvertiert einen booleschen Wert (bool
) aus der Quelle in ein Objekt des Typs T
für das Ziel:
public class BoolToObjectConverter<T> : IValueConverter
{
public T TrueObject { set; get; }
public T FalseObject { set; get; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? TrueObject : FalseObject;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((T)value).Equals(TrueObject);
}
}
Auf der Seite Switch Indicators (Umschalten von Indikatoren) ist dargestellt, wie so der Wert einer Switch
-Ansicht angezeigt werden kann. Obwohl es üblich ist, Wertkonverter als Ressourcen in einem Ressourcenverzeichnis zu instanziieren, wird auf dieser Seite eine Alternative vorgestellt: Jeder Wertkonverter wird zwischen Binding.Converter
-Eigenschaftselementtags instanziiert. Die Anweisung x:TypeArguments
gibt das generische Argument an, und TrueObject
und FalseObject
werden jeweils auf Objekte dieses Typs festgelegt:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.SwitchIndicatorsPage"
Title="Switch Indicators">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="FontSize" Value="18" />
<Setter Property="VerticalOptions" Value="Center" />
</Style>
<Style TargetType="Switch">
<Setter Property="VerticalOptions" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout Padding="10, 0">
<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Subscribe?" />
<Switch x:Name="switch1" />
<Label>
<Label.Text>
<Binding Source="{x:Reference switch1}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="x:String"
TrueObject="Of course!"
FalseObject="No way!" />
</Binding.Converter>
</Binding>
</Label.Text>
</Label>
</StackLayout>
<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Allow popups?" />
<Switch x:Name="switch2" />
<Label>
<Label.Text>
<Binding Source="{x:Reference switch2}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="x:String"
TrueObject="Yes"
FalseObject="No" />
</Binding.Converter>
</Binding>
</Label.Text>
<Label.TextColor>
<Binding Source="{x:Reference switch2}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="Color"
TrueObject="Green"
FalseObject="Red" />
</Binding.Converter>
</Binding>
</Label.TextColor>
</Label>
</StackLayout>
<StackLayout Orientation="Horizontal"
VerticalOptions="CenterAndExpand">
<Label Text="Learn more?" />
<Switch x:Name="switch3" />
<Label FontSize="18"
VerticalOptions="Center">
<Label.Style>
<Binding Source="{x:Reference switch3}"
Path="IsToggled">
<Binding.Converter>
<local:BoolToObjectConverter x:TypeArguments="Style">
<local:BoolToObjectConverter.TrueObject>
<Style TargetType="Label">
<Setter Property="Text" Value="Indubitably!" />
<Setter Property="FontAttributes" Value="Italic, Bold" />
<Setter Property="TextColor" Value="Green" />
</Style>
</local:BoolToObjectConverter.TrueObject>
<local:BoolToObjectConverter.FalseObject>
<Style TargetType="Label">
<Setter Property="Text" Value="Maybe later" />
<Setter Property="FontAttributes" Value="None" />
<Setter Property="TextColor" Value="Red" />
</Style>
</local:BoolToObjectConverter.FalseObject>
</local:BoolToObjectConverter>
</Binding.Converter>
</Binding>
</Label.Style>
</Label>
</StackLayout>
</StackLayout>
</ContentPage>
In den letzten drei Switch
- und Label
-Paaren wird das generische Argument auf Style
festgelegt, und die gesamten Style
-Objekte werden für die Werte TrueObject
und FalseObject
bereitgestellt. Diese überschreiben das impliziten Format für die im Ressourcenverzeichnis festgelegte Label
-Klasse. Deshalb werden die Eigenschaften in diesem Format explizit zur Label
-Klasse zugewiesen. Das Umschalten von Switch
bewirkt, dass die entsprechende Label
-Klasse die Änderung widerspiegelt:
Zum Implementieren von ähnlichen Änderungen auf der Benutzeroberfläche basierend auf anderen Ansichten können Sie ebenso Triggers
verwenden.
Parameter von Bindungskonvertern
Die Klasse Binding
definiert eine ConverterParameter
-Eigenschaft, und die Markuperweiterung Binding
definiert ebenfalls eine ConverterParameter
-Eigenschaft. Wenn diese Eigenschaft festgelegt ist, dann wird der Wert an die Methoden Convert
und ConvertBack
als parameter
-Argument übergeben. Auch wenn die Instanz des Wertkonverters auf mehrere Datenbindungen aufgeteilt wird, kann sich ConverterParameter
für andere Konvertierungen unterscheiden.
Die Verwendung von ConverterParameter
wird mit einem Farbauswahlprogramm veranschaulicht. In diesem Fall verfügt RgbColorViewModel
über drei Eigenschaften vom Typ double
namens Red
, Green
und Blue
, die verwendet werden, um einen Color
-Wert zu erstellen:
public class RgbColorViewModel : INotifyPropertyChanged
{
Color color;
string name;
public event PropertyChangedEventHandler PropertyChanged;
public double Red
{
set
{
if (color.R != value)
{
Color = new Color(value, color.G, color.B);
}
}
get
{
return color.R;
}
}
public double Green
{
set
{
if (color.G != value)
{
Color = new Color(color.R, value, color.B);
}
}
get
{
return color.G;
}
}
public double Blue
{
set
{
if (color.B != value)
{
Color = new Color(color.R, color.G, value);
}
}
get
{
return color.B;
}
}
public Color Color
{
set
{
if (color != value)
{
color = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));
Name = NamedColor.GetNearestColorName(color);
}
}
get
{
return color;
}
}
public string Name
{
private set
{
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
get
{
return name;
}
}
}
Der Bereich für die Eigenschaften Red
, Green
und Blue
liegt zwischen 0 und 1. Möglicherweise bevorzugen Sie, dass die Komponenten als zweistellige Hexadezimalwerte angezeigt werden.
Damit sie diese als Hexadezimalwerte in XAML anzeigen können, müssen sie mit 255 multipliziert und in einen Integer konvertiert werden. Anschließend müssen Sie mit der Spezifikation „X2“ in die StringFormat
-Eigenschaft formatiert werden. Die ersten beiden Aufgaben (Multiplikation mit 255 und Konvertieren in einen Integer) können vom Wertkonverter übernommen werden. Sie können den Wertkonverter so allgemein wie möglich gestalten, indem Sie den Multiplikationsfaktor mit der Eigenschaft ConverterParameter
angeben. Das heißt, dass dieser die Convert
- und ConvertBack
-Methode als parameter
-Argument angibt:
public class DoubleToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)Math.Round((double)value * GetParameter(parameter));
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value / GetParameter(parameter);
}
double GetParameter(object parameter)
{
if (parameter is double)
return (double)parameter;
else if (parameter is int)
return (int)parameter;
else if (parameter is string)
return double.Parse((string)parameter);
return 1;
}
}
Die Convert
-Methode wird vom Typ double
in int
konvertiert, während eine Multiplikation mit dem parameter
-Wert durchgeführt wird. ConvertBack
teilt das ganzzahlige value
-Argument durch parameter
und gibt ein double
-Ergebnis zurück. (Im unten gezeigten Programm wird der Wertkonverter nur in Verbindung mit der Zeichenfolgenformatierung verwendet, also wird ConvertBack
nicht verwendet.)
Der Typ des parameter
-Arguments kann leicht variieren, je nachdem, ob die Datenbindung in Code oder XAML definiert ist. Wenn die ConverterParameter
-Eigenschaft von Binding
in Code festgelegt ist, dann ist es wahrscheinlich, dass sie auf einen numerischen Wert festgelegt wird:
binding.ConverterParameter = 255;
Die ConverterParameter
-Eigenschaft ist vom Typ Object
, also interpretiert der C#-Compiler das Literal 255 als Integer und legt die Eigenschaft auf diesen Wert fest.
In XAML ist es jedoch wahrscheinlich, dass ConverterParameter
wie folgt festgelegt wird:
<Label Text="{Binding Red,
Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Red = {0:X2}'}" />
Das Literal 255 sieht zwar aus wie eine Zahl, da ConverterParameter
jedoch vom Typ Object
ist, behandelt der XAML-Parser 255 als Zeichenfolge.
Aus diesem Grund enthält der oben dargestellte Wertkonverter eine separate GetParameter
-Methode, Fälle behandelt, in denen der parameter
vom Typ double
, int
oder string
ist.
Die Seite RGB Color Selector (RGB-Farbselektor) instanziiert DoubleToIntConverter
im Ressourcenverzeichnis, gefolgt von der Definition von zwei impliziten Formaten:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DataBindingDemos"
x:Class="DataBindingDemos.RgbColorSelectorPage"
Title="RGB Color Selector">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Slider">
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>
<Style TargetType="Label">
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<local:DoubleToIntConverter x:Key="doubleToInt" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<StackLayout.BindingContext>
<local:RgbColorViewModel Color="Gray" />
</StackLayout.BindingContext>
<BoxView Color="{Binding Color}"
VerticalOptions="FillAndExpand" />
<StackLayout Margin="10, 0">
<Label Text="{Binding Name}" />
<Slider Value="{Binding Red}" />
<Label Text="{Binding Red,
Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Red = {0:X2}'}" />
<Slider Value="{Binding Green}" />
<Label Text="{Binding Green,
Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Green = {0:X2}'}" />
<Slider Value="{Binding Blue}" />
<Label>
<Label.Text>
<Binding Path="Blue"
StringFormat="Blue = {0:X2}"
Converter="{StaticResource doubleToInt}">
<Binding.ConverterParameter>
<x:Double>255</x:Double>
</Binding.ConverterParameter>
</Binding>
</Label.Text>
</Label>
</StackLayout>
</StackLayout>
</ContentPage>
Die Werte der Eigenschaften Red
und Green
werden mit einer Binding
-Markuperweiterung angezeigt. Die Blue
-Eigenschaft instanziiert jedoch die Binding
-Klasse, um zu veranschaulichen, wie ein expliziter double
-Wert auf die ConverterParameter
-Eigenschaft festgelegt werden kann.
Das Ergebnis lautet wie folgt: