Teil 4. Grundlagen der Datenbindung

Beispiel herunterladen Das Beispiel herunterladen

Datenbindungen ermöglichen es, Eigenschaften von zwei Objekten zu verknüpfen, sodass eine Änderung in einem Objekt zu einer Änderung im anderen führt. Dies ist ein sehr wertvolles Tool, und während Datenbindungen vollständig im Code definiert werden können, bietet XAML Verknüpfungen und Benutzerfreundlichkeit. Folglich ist eine der wichtigsten Markuperweiterungen in Xamarin.Forms Bindung.

Datenbindungen

Datenbindungen verbinden Eigenschaften von zwei Objekten, die als Quelle und Ziel bezeichnet werden. Im Code sind zwei Schritte erforderlich: Die BindingContext -Eigenschaft des Zielobjekts muss auf das Quellobjekt festgelegt werden, und die SetBinding -Methode (häufig in Verbindung mit der Binding -Klasse verwendet) muss für das Zielobjekt aufgerufen werden, um eine Eigenschaft dieses Objekts an eine Eigenschaft des Quellobjekts zu binden.

Die Zieleigenschaft muss eine bindbare Eigenschaft sein, was bedeutet, dass das Zielobjekt von BindableObjectabgeleitet werden muss. Die Onlinedokumentation Xamarin.Forms gibt an, welche Eigenschaften bindbare Eigenschaften sind. Eine -Eigenschaft von Label wie Text ist der bindbaren Eigenschaft TextPropertyzugeordnet.

Im Markup müssen Sie auch die gleichen zwei Schritte ausführen, die im Code erforderlich sind, mit der Ausnahme, dass die Binding Markuperweiterung den -Aufruf und die Binding -Klasse übernimmtSetBinding.

Wenn Sie Jedoch Datenbindungen in XAML definieren, gibt es mehrere Möglichkeiten, die des BindingContext Zielobjekts festzulegen. Manchmal wird es aus der CodeBehind-Datei festgelegt, manchmal mit einer StaticResource Markuperweiterung oder x:Static und manchmal als Inhalt von BindingContext Eigenschaftselementtags.

Bindungen werden am häufigsten verwendet, um die Visuals eines Programms mit einem zugrunde liegenden Datenmodell zu verbinden, in der Regel in einer Umsetzung der MVVM-Anwendungsarchitektur (Model-View-ViewModel), wie in Teil 5 erläutert. Von Datenbindungen zu MVVM, aber es sind andere Szenarien möglich.

View-to-View-Bindungen

Sie können Datenbindungen definieren, um Eigenschaften von zwei Ansichten auf derselben Seite zu verknüpfen. In diesem Fall legen Sie die BindingContext des Zielobjekts mithilfe der x:Reference Markuperweiterung fest.

Hier sehen Sie eine XAML-Datei, die eine Slider und zwei Label Ansichten enthält, von denen eine durch den Slider Wert gedreht wird und eine andere, die den Slider Wert anzeigt:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="XamlSamples.SliderBindingsPage"
             Title="Slider Bindings Page">

    <StackLayout>
        <Label Text="ROTATION"
               BindingContext="{x:Reference Name=slider}"
               Rotation="{Binding Path=Value}"
               FontAttributes="Bold"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />

        <Slider x:Name="slider"
                Maximum="360"
                VerticalOptions="CenterAndExpand" />

        <Label BindingContext="{x:Reference slider}"
               Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"
               FontAttributes="Bold"
               FontSize="Large"
               HorizontalOptions="Center"
               VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>

Slider enthält ein x:Name Attribut, auf das die beiden Label Sichten mithilfe der x:Reference Markuperweiterung verweisen.

Die x:Reference Bindungserweiterung definiert eine Eigenschaft namens Name , die auf den Namen des Elements festgelegt wird, auf das verwiesen wird, in diesem Fall slider. Die Klasse, die ReferenceExtension die x:Reference Markuperweiterung definiert, definiert jedoch auch ein ContentProperty Attribut für Name, was bedeutet, dass es nicht explizit erforderlich ist. Nur für Die Sorte enthält die erste x:Reference "Name=", aber die zweite nicht:

BindingContext="{x:Reference Name=slider}"
…
BindingContext="{x:Reference slider}"

Die Binding Markuperweiterung selbst kann mehrere Eigenschaften aufweisen, genau wie die - und Binding -BindingBaseKlasse. Der ContentProperty für Binding ist Path, aber der "Path="-Teil der Markuperweiterung kann weggelassen werden, wenn der Pfad das erste Element in der Binding Markuperweiterung ist. Das erste Beispiel hat "Path=", aber im zweiten Beispiel wird es weggelassen:

Rotation="{Binding Path=Value}"
…
Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

Die Eigenschaften können alle in einer Zeile oder in mehrere Zeilen unterteilt werden:

Text="{Binding Value,
               StringFormat='The angle is {0:F0} degrees'}"

Tun Sie, was bequem ist.

Beachten Sie die StringFormat -Eigenschaft in der zweiten Binding Markuperweiterung. In Xamarin.Formsführen Bindungen keine impliziten Typkonvertierungen durch, und wenn Sie ein Nicht-Zeichenfolgenobjekt als Zeichenfolge anzeigen müssen, müssen Sie einen Typkonverter bereitstellen oder verwenden StringFormat. Im Hintergrund wird die statische String.Format Methode verwendet, um zu implementieren StringFormat. Dies ist möglicherweise ein Problem, da .NET-Formatierungsspezifikationen geschweifte Klammern enthalten, die auch zum Trennen von Markuperweiterungen verwendet werden. Dies führt zu einer Verwechslung des XAML-Parsers. Um dies zu vermeiden, setzen Sie die gesamte Formatierungszeichenfolge in einfache Anführungszeichen:

Text="{Binding Value, StringFormat='The angle is {0:F0} degrees'}"

Hier sehen Sie das ausgeführte Programm:

View-to-View-Bindungen

Der Bindungsmodus

Eine einzelne Ansicht kann Datenbindungen für mehrere eigenschaften aufweisen. Jede Ansicht kann jedoch nur eine BindingContextenthalten, sodass mehrere Datenbindungen für diese Ansicht alle auf Eigenschaften desselben Objekts verweisen müssen.

Die Lösung für dieses und andere Probleme umfasst die Mode -Eigenschaft, die auf einen Member der BindingMode -Enumeration festgelegt ist:

  • Default
  • OneWay — Werte werden von der Quelle auf das Ziel übertragen.
  • OneWayToSource — Werte werden vom Ziel auf die Quelle übertragen.
  • TwoWay — Werte werden in beiden Richtungen zwischen Quelle und Ziel übertragen.
  • OneTime – Daten werden von Quelle zu Ziel, aber nur, wenn sich die BindingContext daten ändern.

Das folgende Programm veranschaulicht eine häufige Verwendung der OneWayToSource Bindungsmodi und TwoWay . Vier Slider Ansichten sollen die ScaleEigenschaften , Rotate, RotateXund RotateY eines Labelsteuern. Zunächst scheint es, als ob diese vier Eigenschaften von Label Datenbindungsziele sein sollten, da jede von einem Sliderfestgelegt wird. Der BindingContext von Label kann jedoch nur ein Objekt sein, und es gibt vier verschiedene Schieberegler.

Aus diesem Grund werden alle Bindungen scheinbar rückwärts festgelegt: Die BindingContext der vier Schieberegler wird auf Labelfestgelegt, und die Bindungen werden für die Value Eigenschaften der Schieberegler festgelegt. Mithilfe der OneWayToSource Modi und TwoWay können diese Value Eigenschaften die Quelleigenschaften festlegen, die die ScaleEigenschaften , Rotate, RotateXund RotateY von Labelsind:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             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>

Die Bindungen für drei der Slider Ansichten sind OneWayToSource, was bedeutet, dass der Slider Wert eine Änderung in der -Eigenschaft von BindingContextverursacht, die den Label Namen hat label. Diese drei Slider Ansichten verursachen Änderungen an den RotateEigenschaften , RotateXund RotateY von Label.

Die Bindung für die Scale -Eigenschaft ist TwoWayjedoch . Dies liegt daran, dass die Scale Eigenschaft den Standardwert 1 aufweist, und die Verwendung einer TwoWay Bindung bewirkt, dass der Slider Anfangswert auf 1 statt auf 0 festgelegt wird. Wenn diese Bindung wäre OneWayToSource, würde die Scale -Eigenschaft vom Standardwert auf 0 Slider festgelegt. Die Label wäre nicht sichtbar, und dies kann für den Benutzer einige Verwirrung verursachen.

Rückwärtsbindungen

Hinweis

Die VisualElement -Klasse verfügt auch über ScaleX eigenschaften und ScaleY , die die VisualElement auf der x-Achse bzw. y-Achse skalieren.

Bindungen und Auflistungen

Nichts veranschaulicht die Leistungsfähigkeit von XAML und Datenbindungen besser als ein vorlagenbasiertes ListView.

ListView definiert eine ItemsSource Eigenschaft vom Typ IEnumerableund zeigt die Elemente in dieser Auflistung an. Diese Elemente können Objekte eines beliebigen Typs sein. Standardmäßig verwendet die ToString -Methode jedes Elements, ListView um dieses Element anzuzeigen. Manchmal ist dies genau das, was Sie möchten, aber in vielen Fällen ToString gibt nur den vollqualifizierten Klassennamen des Objekts zurück.

Die Elemente in der ListView Auflistung können jedoch beliebig angezeigt werden, indem eine Vorlage verwendet wird, die eine klasse umfasst, die von abgeleitet wird Cell. Die Vorlage wird für jedes Element in geklont ListView, und Datenbindungen, die für die Vorlage festgelegt wurden, werden an die einzelnen Klone übertragen.

Häufig sollten Sie mithilfe der ViewCell -Klasse eine benutzerdefinierte Zelle für diese Elemente erstellen. Dieser Prozess ist im Code etwas unübersichtlich, aber in XAML wird er sehr einfach.

Im XamlSamples-Projekt ist eine Klasse namens NamedColorenthalten. Jedes NamedColor Objekt verfügt Name über eigenschaften und FriendlyName vom Typ stringsowie eine Color -Eigenschaft vom Typ Color. Verfügt darüber hinaus NamedColor über 141 statische schreibgeschützte Felder vom Typ Color , die den in der Xamarin.FormsColor -Klasse definierten Farben entsprechen. Ein statischer Konstruktor erstellt eine IEnumerable<NamedColor> Auflistung, die Objekte enthält NamedColor , die diesen statischen Feldern entsprechen, und weist sie der öffentlichen statischen All Eigenschaft zu.

Das Festlegen der statischen NamedColor.All Eigenschaft auf den ItemsSource eines ListView ist mit der x:Static Markuperweiterung einfach:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             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>

Die resultierende Anzeige stellt fest, dass die Elemente tatsächlich vom Typ XamlSamples.NamedColorsind:

Binden an eine Auflistung

Es sind nicht viele Informationen, aber die ListView ist scrollbar und auswählbar.

Um eine Vorlage für die Elemente zu definieren, sollten Sie die ItemTemplate -Eigenschaft als Eigenschaftselement aufteilen und auf einen DataTemplatefestlegen, der dann auf verweist ViewCell. Für die View -Eigenschaft von ViewCell können Sie ein Layout von einer oder mehreren Ansichten definieren, um jedes Element anzuzeigen. Hier ist ein einfaches Beispiel:

<ListView ItemsSource="{x:Static local:NamedColor.All}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <ViewCell.View>
                    <Label Text="{Binding FriendlyName}" />
                </ViewCell.View>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Hinweis

Die Bindungsquelle für Zellen und untergeordnete Zellen ist die ListView.ItemsSource Auflistung.

Das Label -Element wird auf die View -Eigenschaft von ViewCellfestgelegt. (Die ViewCell.View Tags werden nicht benötigt, da die View -Eigenschaft die Inhaltseigenschaft von ViewCellist.) Dieses Markup zeigt die FriendlyName -Eigenschaft jedes NamedColor Objekts an:

Binden an eine Sammlung mit einer DataTemplate

Viel besser. Jetzt müssen Sie nur noch die Elementvorlage mit weiteren Informationen und der tatsächlichen Farbe auffüllen. Zur Unterstützung dieser Vorlage wurden einige Werte und Objekte im Ressourcenverzeichnis der Seite definiert:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:XamlSamples"
             x:Class="XamlSamples.ListViewDemoPage"
             Title="ListView Demo Page">

    <ContentPage.Resources>
        <ResourceDictionary>
            <OnPlatform x:Key="boxSize"
                        x:TypeArguments="x:Double">
                <On Platform="iOS, Android, UWP" Value="50" />
            </OnPlatform>

            <OnPlatform x:Key="rowHeight"
                        x:TypeArguments="x:Int32">
                <On Platform="iOS, Android, UWP" Value="60" />
            </OnPlatform>

            <local:DoubleToIntConverter x:Key="intConverter" />

        </ResourceDictionary>
    </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="Medium" />

                            <StackLayout Orientation="Horizontal"
                                         Spacing="0">
                                <Label Text="{Binding Color.R,
                                       Converter={StaticResource intConverter},
                                       ConverterParameter=255,
                                       StringFormat='R={0:X2}'}" />

                                <Label Text="{Binding Color.G,
                                       Converter={StaticResource intConverter},
                                       ConverterParameter=255,
                                       StringFormat=', G={0:X2}'}" />

                                <Label Text="{Binding Color.B,
                                       Converter={StaticResource intConverter},
                                       ConverterParameter=255,
                                       StringFormat=', B={0:X2}'}" />
                            </StackLayout>
                        </StackLayout>
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

Beachten Sie die Verwendung von OnPlatform , um die Größe von und BoxView die Höhe der ListView Zeilen zu definieren. Obwohl die Werte für alle Plattformen identisch sind, kann das Markup problemlos für andere Werte angepasst werden, um die Anzeige zu optimieren.

Binden von Wertkonvertern

In der vorherigen ListView-Demo-XAML-Datei werden die einzelnen REigenschaften , Gund B der Xamarin.FormsColor -Struktur angezeigt. Diese Eigenschaften sind vom Typ double und reichen von 0 bis 1. Wenn Sie die Hexadezimalwerte anzeigen möchten, können Sie nicht einfach mit einer "X2"-Formatierungsspezifikation verwenden StringFormat . Dies funktioniert nur für ganze Zahlen, und außerdem müssen die double Werte mit 255 multipliziert werden.

Dieses kleine Problem wurde mit einem Wertkonverter, auch Bindungskonverter genannt, gelöst. Dies ist eine Klasse, die die IValueConverter -Schnittstelle implementiert, was bedeutet, dass sie über zwei Methoden mit dem Namen Convert und ConvertBackverfügt. Die Convert -Methode wird aufgerufen, wenn ein Wert von der Quelle zum Ziel übertragen wird. Die ConvertBack -Methode wird für Übertragungen vom Ziel zur Quelle in OneWayToSource oder TwoWay für Bindungen aufgerufen:

using System;
using System.Globalization;
using Xamarin.Forms;

namespace XamlSamples
{
    class DoubleToIntConverter : IValueConverter
    {
        public object Convert(object value, Type targetType,
                              object parameter, CultureInfo culture)
        {
            double multiplier;

            if (!Double.TryParse(parameter as string, out multiplier))
                multiplier = 1;

            return (int)Math.Round(multiplier * (double)value);
        }

        public object ConvertBack(object value, Type targetType,
                                  object parameter, CultureInfo culture)
        {
            double divider;

            if (!Double.TryParse(parameter as string, out divider))
                divider = 1;

            return ((double)(int)value) / divider;
        }
    }
}

Die ConvertBack -Methode spielt in diesem Programm keine Rolle, da die Bindungen nur eine Möglichkeit von der Quelle zum Ziel sind.

Eine Bindung verweist mit der Converter -Eigenschaft auf einen Bindungskonverter. Ein Bindungskonverter kann auch einen parameter akzeptieren, der mit der ConverterParameter -Eigenschaft angegeben ist. Für eine gewisse Vielseitigkeit wird der Multiplikator so angegeben. Der Bindungskonverter überprüft den Konverterparameter auf einen gültigen double Wert.

Der Konverter wird im Ressourcenwörterbuch instanziiert, sodass er für mehrere Bindungen freigegeben werden kann:

<local:DoubleToIntConverter x:Key="intConverter" />

Drei Datenbindungen verweisen auf diese einzelne instance. Beachten Sie, dass die Binding Markuperweiterung eine eingebettete StaticResource Markuperweiterung enthält:

<Label Text="{Binding Color.R,
                      Converter={StaticResource intConverter},
                      ConverterParameter=255,
                      StringFormat='R={0:X2}'}" />

Dies ist das Ergebnis:

Binden an eine Sammlung mit einer DataTemplate und Konvertern

Der ListView ist bei der Behandlung von Änderungen, die dynamisch in den zugrunde liegenden Daten auftreten können, recht ausgeklügelt, aber nur, wenn Sie bestimmte Schritte ausführen. Wenn die Auflistung von Elementen, die der ItemsSource -Eigenschaft der -Eigenschaft zugewiesen sind, während der ListView Laufzeit geändert wird, d. h., wenn Elemente der Auflistung hinzugefügt oder daraus entfernt werden können, verwenden Sie eine ObservableCollection Klasse für diese Elemente. ObservableCollection implementiert die INotifyCollectionChanged -Schnittstelle und ListView installiert einen Handler für das CollectionChanged -Ereignis.

Wenn sich die Eigenschaften der Elemente selbst während der Laufzeit ändern, sollten die Elemente in der Auflistung die INotifyPropertyChanged Schnittstelle implementieren und Änderungen an Eigenschaftswerten mithilfe des -Ereignisses PropertyChanged signalisieren. Dies wird im nächsten Teil dieser Reihe, Teil 5, veranschaulicht. Von der Datenbindung zu MVVM.

Zusammenfassung

Datenbindungen bieten einen leistungsstarken Mechanismus zum Verknüpfen von Eigenschaften zwischen zwei Objekten innerhalb einer Seite oder zwischen visuellen Objekten und zugrunde liegenden Daten. Wenn die Anwendung jedoch beginnt, mit Datenquellen zu arbeiten, entsteht ein gängiges Anwendungsarchitekturmuster als nützliches Paradigma. Dies wird in Teil 5 behandelt. Von Datenbindungen zu MVVM.