Freigeben über


Grundlagen der Datenbindung

Beispiel durchsuchen.Durchsuchen Sie das Beispiel

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.

  1. Die BindingContext-Eigenschaft des Zielobjekts muss auf das Quellobjekt gesetzt werden,
  2. Die SetBinding-Methode (oft in Verbindung mit der Binding-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 sich BindingContext ä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:

Umgekehrte Bindungen.

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:

Binden an eine Auflistung.

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:

Binden an eine Auflistung mit einer DataTemplate.

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:

Binden an eine Auflistung mit einer DataTemplate und einem Konverter.

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.