Grundlagen der Datenbindung
NET Multi-Platform App UI(.NET MAUI)-Datenbindungen ermöglichen die Verknüpfung von Eigenschaften zweier Objekte, sodass eine Änderung des einen eine Änderung des anderen bewirkt. Dies ist ein sehr wertvolles Werkzeug, und während Datenbindungen vollständig im Code definiert werden können, bietet XAML Abkürzungen und Komfort.
Datenbindungen
Datenbindungen verbinden Eigenschaften von zwei Objekten, der Quelle und dem Ziel. Diese beiden Schritte sind erforderlich.
- Die
BindingContext
-Eigenschaft des Zielobjekts muss auf das Quellobjekt gesetzt werden, - Die
SetBinding
-Methode (oft in Verbindung mit derBinding
-Klasse verwendet) muss auf dem Zielobjekt aufgerufen werden, um eine Eigenschaft dieses Objekts an eine Eigenschaft des Quellobjekts zu binden.
Die Zieleigenschaft muss eine bindungsfähige Eigenschaft sein, was bedeutet, dass das Zielobjekt von BindableObject abgeleitet sein muss. Eine Eigenschaft von Label, wie etwa Text
, ist mit der bindungsfähigen Eigenschaft TextProperty
verbunden.
In XAML müssen Sie dieselben beiden Schritte ausführen, die auch im Code erforderlich sind, mit dem Unterschied, dass die Binding
-Markup-Erweiterung an die Stelle des SetBinding
-Aufrufs und der Binding
-Klasse tritt. Wenn Sie jedoch Datenbindungen in XAML definieren, gibt es mehrere Möglichkeiten, das BindingContext
des Zielobjekts festzulegen. Manchmal wird sie über die Code-Behind-Datei festgelegt, manchmal über eine StaticResource
- oder x:Static
-Markup-Erweiterung und manchmal als Inhalt von BindingContext
-Eigenschaftselement-Tags.
Ansicht-zu-Ansicht-Bindungen
Sie können Datenbindungen definieren, um Eigenschaften von zwei Ansichten auf derselben Seite zu verknüpfen. In diesem Fall setzen Sie das BindingContext
des Zielobjekts mit der Markuperweiterung x:Reference
.
Das folgende Beispiel enthält eine Slider- und zwei Label-Ansichten, von denen eine um den Slider-Wert gedreht ist und die andere den Slider-Wert anzeigt:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderBindingsPage"
Title="Slider Bindings Page">
<StackLayout>
<Label Text="ROTATION"
BindingContext="{x:Reference slider}"
Rotation="{Binding Path=Value}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Slider x:Name="slider"
Maximum="360"
VerticalOptions="Center" />
<Label BindingContext="{x:Reference slider}"
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
FontAttributes="Bold"
FontSize="18"
HorizontalOptions="Center"
VerticalOptions="Center" />
</StackLayout>
</ContentPage>
Die Slider enthält ein x:Name
-Attribut, auf das die beiden Label-Ansichten mit der x:Reference
-Auszeichnungserweiterung verweisen. Die x:Reference
-Bindungserweiterung definiert eine Eigenschaft namens Name
, die auf den Namen des referenzierten Elements gesetzt wird, in diesem Fall slider
. Die ReferenceExtension
-Klasse, die die x:Reference
-Auszeichnungserweiterung definiert, legt jedoch auch ein ContentProperty
-Attribut für Name
fest, was bedeutet, dass es nicht ausdrücklich erforderlich ist.
Die Auszeichnungserweiterung Binding
selbst kann mehrere Eigenschaften haben, genau wie die Klassen BindingBase
und Binding
. Das ContentProperty
für Binding
ist Path
, aber der „Path=“-Teil der Markup-Erweiterung kann weggelassen werden, wenn der Pfad das erste Element in der Binding
Markup-Erweiterung ist.
Die zweite Markuperweiterung Binding
setzt die Eigenschaft StringFormat
. In .NET MAUI führen Bindungen keine impliziten Typkonvertierungen durch, und wenn Sie ein Nicht-String-Objekt als String anzeigen müssen, müssen Sie einen Typkonverter bereitstellen oder StringFormat
verwenden.
Wichtig
Formatierungszeichenfolgen müssen in einfache Anführungszeichen gesetzt werden.
Bindungsmodus
Eine einzelne Ansicht kann Datenbindungen für mehrere seiner Eigenschaften aufweisen. Jede Ansicht kann jedoch nur eine BindingContext
haben, sodass mehrere Datenbindungen in dieser Ansicht alle auf Eigenschaften desselben Objekts verweisen müssen.
Die Lösung für dieses und andere Probleme ist die Eigenschaft Mode
, die auf ein Mitglied der Enumeration BindingMode
gesetzt wird:
Default
OneWay
— Werte werden von der Quelle an das Ziel übertragen.OneWayToSource
— Werte werden vom Ziel an die Quelle übertragen.TwoWay
— Werte werden auf beide Arten zwischen Quelle und Ziel übertragen.OneTime
– Daten gehen von der Quelle zum Ziel, aber nur wenn sichBindingContext
ändert
Das folgende Beispiel veranschaulicht eine häufige Verwendung der Bindungsmodi OneWayToSource
und TwoWay
:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamlSamples.SliderTransformsPage"
Padding="5"
Title="Slider Transforms Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Scaled and rotated Label -->
<Label x:Name="label"
Text="TEXT"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<!-- Slider and identifying Label for Scale -->
<Slider x:Name="scaleSlider"
BindingContext="{x:Reference label}"
Grid.Row="1" Grid.Column="0"
Maximum="10"
Value="{Binding Scale, Mode=TwoWay}" />
<Label BindingContext="{x:Reference scaleSlider}"
Text="{Binding Value, StringFormat='Scale = {0:F1}'}"
Grid.Row="1" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for Rotation -->
<Slider x:Name="rotationSlider"
BindingContext="{x:Reference label}"
Grid.Row="2" Grid.Column="0"
Maximum="360"
Value="{Binding Rotation, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationSlider}"
Text="{Binding Value, StringFormat='Rotation = {0:F0}'}"
Grid.Row="2" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationX -->
<Slider x:Name="rotationXSlider"
BindingContext="{x:Reference label}"
Grid.Row="3" Grid.Column="0"
Maximum="360"
Value="{Binding RotationX, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationXSlider}"
Text="{Binding Value, StringFormat='RotationX = {0:F0}'}"
Grid.Row="3" Grid.Column="1"
VerticalTextAlignment="Center" />
<!-- Slider and identifying Label for RotationY -->
<Slider x:Name="rotationYSlider"
BindingContext="{x:Reference label}"
Grid.Row="4" Grid.Column="0"
Maximum="360"
Value="{Binding RotationY, Mode=OneWayToSource}" />
<Label BindingContext="{x:Reference rotationYSlider}"
Text="{Binding Value, StringFormat='RotationY = {0:F0}'}"
Grid.Row="4" Grid.Column="1"
VerticalTextAlignment="Center" />
</Grid>
</ContentPage>
In diesem Beispiel sollen 4 Slider-Ansichten die Eigenschaften Scale
, Rotate
, RotateX
und RotateY
eines Label steuern. Auf den ersten Blick scheint es, als ob diese 4 Eigenschaften des Label datenbindende Ziele sein sollten, da jede von ihnen von einem Slider festgelegt wird. Die BindingContext
von Label kann jedoch nur ein Objekt sein, und es gibt 4 verschiedene Schieberegler. Aus diesem Grund wird der BindingContext
jedes der 4 Schieberegler auf den Label gesetzt, und die Bindungen werden auf die Value
Eigenschaften der Schieberegler gesetzt. Durch die Verwendung der Modi OneWayToSource
und TwoWay
können diese Value
-Eigenschaften die Quelleigenschaften festlegen, d. h. die Scale
-, Rotate
-, RotateX
- und RotateY
-Eigenschaften des Label.
Die Bindungen von 3 der Slider-Ansichten sind OneWayToSource
, was bedeutet, dass der Slider-Wert eine Änderung in der Eigenschaft seiner BindingContext
verursacht, die Label mit dem Namen label
ist. Diese 3 Slider-Ansichten bewirken Änderungen an den Rotate
-, RotateX
- und RotateY
-Eigenschaften des Label:
Die Bindung für die Eigenschaft Scale
ist jedoch TwoWay
. Das liegt daran, dass die Scale
-Eigenschaft einen Standardwert von 1 hat und die Verwendung einer TwoWay
-Bindung dazu führt, dass der Slider-Ausgangswert auf 1 statt auf 0 gesetzt wird. Wäre diese Bindung OneWayToSource
, würde die Eigenschaft Scale
vom Standardwert Slider zunächst auf 0 gesetzt werden. Dies Label wäre nicht sichtbar.
Hinweis
Die Klasse VisualElement hat auch die Eigenschaften ScaleX
und ScaleY
, die die VisualElement auf der x-Achse bzw. y-Achse skalieren.
Bindungen und Sammlungen
ListView definiert eine ItemsSource
-Eigenschaft vom Typ IEnumerable
und zeigt die Elemente in dieser Sammlung an. Diese Elemente können Objekte eines beliebigen Typs sein. Standardmäßig verwendet ListView die ToString
-Methode für jedes Element, um dieses Element anzuzeigen. Manchmal ist dies genau das, was Sie wollen, aber in vielen Fällen gibt ToString
nur den vollqualifizierten Klassennamen des Objekts zurück.
Die Elemente in der ListView-Sammlung können jedoch durch die Verwendung einer Vorlage, bei der es sich um eine Klasse handelt, die von Cell abgeleitet ist, auf beliebige Weise angezeigt werden. Die Vorlage wird für jedes Element in der ListView geklont, und Datenbindungen, die auf der Vorlage festgelegt wurden, werden auf die einzelnen Klone übertragen. Benutzerdefinierte Zellen können für Elemente mit der Klasse ViewCell erstellt werden.
ListView kann mithilfe der Klasse NamedColor
eine Liste aller benannten Farben anzeigen, die in .NET MAUI verfügbar sind:
using System.Reflection;
using System.Text;
namespace XamlSamples
{
public class NamedColor
{
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public Color Color { get; private set; }
// Expose the Color fields as properties
public float Red => Color.Red;
public float Green => Color.Green;
public float Blue => Color.Blue;
public static IEnumerable<NamedColor> All { get; private set; }
static NamedColor()
{
List<NamedColor> all = new List<NamedColor>();
StringBuilder stringBuilder = new StringBuilder();
// Loop through the public static fields of the Color structure.
foreach (FieldInfo fieldInfo in typeof(Colors).GetRuntimeFields())
{
if (fieldInfo.IsPublic &&
fieldInfo.IsStatic &&
fieldInfo.FieldType == typeof(Color))
{
// Convert the name to a friendly name.
string name = fieldInfo.Name;
stringBuilder.Clear();
int index = 0;
foreach (char ch in name)
{
if (index != 0 && Char.IsUpper(ch))
{
stringBuilder.Append(' ');
}
stringBuilder.Append(ch);
index++;
}
// Instantiate a NamedColor object.
NamedColor namedColor = new NamedColor
{
Name = name,
FriendlyName = stringBuilder.ToString(),
Color = (Color)fieldInfo.GetValue(null)
};
// Add it to the collection.
all.Add(namedColor);
}
}
all.TrimExcess();
All = all;
}
}
}
Jedes NamedColor
-Objekt hat Name
und FriendlyName
-Eigenschaften vom Typ string
, eine Color
-Eigenschaft vom Typ Color sowie Red
-, Green
- und Blue
-Eigenschaften. Außerdem erstellt der NamedColor
statische Konstruktor eine IEnumerable<NamedColor>
Sammlung, die NamedColor
Objekte enthält, die den Feldern vom Typ Color in der Klasse Colors entsprechen, und weist sie ihrer öffentlichen statischen All
Eigenschaft zu.
Das Festlegen der statischen NamedColor.All
-Eigenschaft auf die ItemsSource
eines ListView kann mit der Auszeichnungserweiterung x:Static
erreicht werden:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples;assembly=XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ListView ItemsSource="{x:Static local:NamedColor.All}" />
</ContentPage>
Das Ergebnis legt fest, dass die Elemente vom Typ XamlSamples.NamedColor
sind:
Um eine Vorlage für die Elemente zu definieren, sollte das ItemTemplate
auf ein DataTemplate gesetzt werden, das auf ein ViewCell verweist. Die ViewCell sollte ein Layout mit einer oder mehreren Ansichten definieren, um jedes Element anzuzeigen:
<ListView ItemsSource="{x:Static local:NamedColor.All}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding FriendlyName}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Hinweis
Die Bindungsquelle für Zellen und untergeordnete Zellen ist die ListView.ItemsSource
-Sammlung.
In diesem Beispiel wird das Element Label auf die Eigenschaft View des Elements ViewCell gesetzt. Die ViewCell.View
-Tags werden nicht benötigt, da die View-Eigenschaft die Inhaltseigenschaft von ViewCell ist. Diese XAML zeigt die FriendlyName
-Eigenschaft jedes NamedColor
-Objekts an:
Die Artikelvorlage kann erweitert werden, um weitere Informationen und die aktuelle Farbe anzuzeigen:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamlSamples"
x:Class="XamlSamples.ListViewDemoPage"
Title="ListView Demo Page">
<ContentPage.Resources>
<x:Double x:Key="boxSize">50</x:Double>
<x:Int32 x:Key="rowHeight">60</x:Int32>
<local:FloatToIntConverter x:Key="intConverter" />
</ContentPage.Resources>
<ListView ItemsSource="{x:Static local:NamedColor.All}"
RowHeight="{StaticResource rowHeight}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="5, 5, 0, 5"
Orientation="Horizontal"
Spacing="15">
<BoxView WidthRequest="{StaticResource boxSize}"
HeightRequest="{StaticResource boxSize}"
Color="{Binding Color}" />
<StackLayout Padding="5, 0, 0, 0"
VerticalOptions="Center">
<Label Text="{Binding FriendlyName}"
FontAttributes="Bold"
FontSize="14" />
<StackLayout Orientation="Horizontal"
Spacing="0">
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
<Label Text="{Binding Green,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', G={0:X2}'}" />
<Label Text="{Binding Blue,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat=', B={0:X2}'}" />
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
Bindungswertkonverter
Das vorherige XAML-Beispiel zeigt die einzelnen Red
-, Green
- und Blue
-Eigenschaften der einzelnen NamedColor
. Diese Eigenschaften sind vom Typ float
und reichen von 0 bis 1. Wenn Sie die hexadezimalen Werte anzeigen wollen, können Sie nicht einfach StringFormat
mit einer „X2“-Formatierungsangabe verwenden. Das funktioniert nur für ganze Zahlen und außerdem müssen die float
Werte mit 255 multipliziert werden.
Dieses Problem kann mit einem Wertkonverter, auch Bindungskonverter genannt, gelöst werden. Dies ist eine Klasse, die die Schnittstelle IValueConverter implementiert, was bedeutet, dass sie zwei Methoden namens Convert
und ConvertBack
hat. Die Methode Convert
wird aufgerufen, wenn ein Wert von der Quelle zum Ziel übertragen wird. Die Methode ConvertBack
wird für Übertragungen von Ziel zu Quelle in OneWayToSource
oder TwoWay
Bindungen aufgerufen:
using System.Globalization;
namespace XamlSamples
{
public class FloatToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
float multiplier;
if (!float.TryParse(parameter as string, out multiplier))
multiplier = 1;
return (int)Math.Round(multiplier * (float)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
float divider;
if (!float.TryParse(parameter as string, out divider))
divider = 1;
return ((float)(int)value) / divider;
}
}
}
Hinweis
Die ConvertBack
-Methode spielt in diesem Beispiel keine Rolle, da die Bindungen nur in einer Richtung von der Quelle zum Ziel erfolgen.
Eine Bindung verweist auf einen Bindungskonverter mit der Converter
-Eigenschaft. Ein Bindungskonverter kann auch einen mit der Eigenschaft ConverterParameter
angegebenen Parameter akzeptieren. Für eine gewisse Vielseitigkeit wird der Multiplikator auf diese Weise festgelegt. Der Bindungskonverter überprüft den Konverterparameter auf einen gültigen float
-Wert.
Der Konverter wird im Ressourcenverzeichnis der Seite instanziiert, sodass er für mehrere Bindungen freigegeben werden kann:
<local:FloatToIntConverter x:Key="intConverter" />
Drei Datenbindungen verweisen auf diese einzelne Instanz:
<Label Text="{Binding Red,
Converter={StaticResource intConverter},
ConverterParameter=255,
StringFormat='R={0:X2}'}" />
Die Elementvorlage gibt die Farbe, den Anzeigenamen und die RGB-Werte wieder:
Die ListView kann Änderungen, die dynamisch in den zugrunde liegenden Daten auftreten, verarbeiten, aber nur, wenn Sie bestimmte Schritte unternehmen. Wenn sich die Sammlung von Elementen, die der ItemsSource
-Eigenschaft des ListView zugewiesen sind, während der Laufzeit ändert, verwenden Sie eine ObservableCollection<T>-Klasse für diese Elemente. ObservableCollection<T> implementiert die Schnittstelle INotifyCollectionChanged
, und ListView installiert einen Handler für das Ereignis CollectionChanged
.
Wenn sich die Eigenschaften der Elemente selbst während der Laufzeit ändern, dann sollten die Elemente in der Sammlung die INotifyPropertyChanged
-Schnittstelle implementieren und Änderungen der Eigenschaftswerte mit dem PropertyChanged
-Ereignis signalisieren.
Nächste Schritte
Datenbindungen bieten einen leistungsstarken Mechanismus zur Verknüpfung von Eigenschaften zwischen zwei Objekten innerhalb einer Seite oder zwischen visuellen Objekten und zugrunde liegenden Daten. Wenn die App jedoch mit Datenquellen arbeitet, beginnt sich ein beliebtes Architekturmuster für Apps als nützliches Paradigma zu erweisen.