Partager via


Partie 4. Notions de base sur la liaison de données

Les liaisons de données permettent aux propriétés de deux objets d’être liés afin qu’une modification d’un objet entraîne une modification dans l’autre. Il s’agit d’un outil très précieux et, bien que les liaisons de données puissent être définies entièrement dans le code, XAML fournit des raccourcis et des commodités. Par conséquent, l’une des extensions de balisage les plus importantes est Xamarin.Forms Binding.

Liaisons de données

Les liaisons de données connectent les propriétés de deux objets, appelées source et cible. Dans le code, deux étapes sont requises : la BindingContext propriété de l’objet cible doit être définie sur l’objet source, et la SetBinding méthode (souvent utilisée conjointement avec la Binding classe) doit être appelée sur l’objet cible pour lier une propriété de cet objet à une propriété de l’objet source.

La propriété cible doit être une propriété pouvant être liée, ce qui signifie que l’objet cible doit dériver de BindableObject. La documentation en ligne Xamarin.Forms indique les propriétés pouvant être liées. Propriété de telle sorte qu’elle Label Text est associée à la propriété TextPropertypouvant être liée.

Dans le balisage, vous devez également effectuer les deux étapes requises dans le code, sauf que l’extension Binding de balisage prend la place de l’appel SetBinding et de la Binding classe.

Toutefois, lorsque vous définissez des liaisons de données en XAML, il existe plusieurs façons de définir l’objet BindingContext cible. Parfois, il est défini à partir du fichier code-behind, parfois à l’aide d’une extension ou x:Static d’une StaticResource extension de balisage, et parfois comme contenu des balises d’élément BindingContext de propriété.

Les liaisons sont utilisées le plus souvent pour connecter les visuels d’un programme à un modèle de données sous-jacent, généralement dans une réalisation de l’architecture d’application MVVM (Model-View-ViewModel), comme indiqué dans la partie 5. Des liaisons de données à MVVM, mais d’autres scénarios sont possibles.

Liaisons d’affichage à vue

Vous pouvez définir des liaisons de données pour lier des propriétés de deux vues sur la même page. Dans ce cas, vous définissez l’objet BindingContext cible à l’aide de l’extension de x:Reference balisage.

Voici un fichier XAML qui contient une Slider et deux Label vues, dont l’une est pivotée par la Slider valeur et une autre qui affiche la Slider valeur :

<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>

Contient Slider un x:Name attribut référencé par les deux Label vues à l’aide de l’extension de x:Reference balisage.

L’extension x:Reference de liaison définit une propriété nommée Name pour définir le nom de l’élément référencé, dans ce cas slider. Toutefois, la ReferenceExtension classe qui définit l’extension de x:Reference balisage définit également un ContentProperty attribut pour Name, ce qui signifie qu’elle n’est pas explicitement requise. Pour la variété, le premier x:Reference inclut « Name= », mais le second ne le fait pas :

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

L’extension Binding de balisage elle-même peut avoir plusieurs propriétés, comme la classe et Binding la BindingBase classe. Pour ContentProperty celaPath, mais la partie « Path= » de l’extension de balisage peut être omise si le chemin est le premier élément de l’extension de Binding balisage.Binding Le premier exemple a « Path= », mais le deuxième exemple l’omet :

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

Les propriétés peuvent toutes se trouver sur une seule ligne ou séparées en plusieurs lignes :

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

Faites tout ce qui est pratique.

Notez la StringFormat propriété dans la deuxième Binding extension de balisage. Dans Xamarin.Forms, les liaisons n’effectuent aucune conversion de type implicite et si vous devez afficher un objet non-chaîne sous forme de chaîne, vous devez fournir un convertisseur de type ou utiliser StringFormat. En arrière-plan, la méthode statique String.Format est utilisée pour implémenter StringFormat. C’est potentiellement un problème, car les spécifications de mise en forme .NET impliquent des accolades, qui sont également utilisées pour délimiter les extensions de balisage. Cela crée un risque de confusion de l’analyseur XAML. Pour éviter cela, placez la chaîne de mise en forme entière entre guillemets simples :

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

Voici le programme en cours d’exécution :

Liaisons d’affichage à vue

Mode de liaison

Une vue unique peut avoir des liaisons de données sur plusieurs de ses propriétés. Toutefois, chaque vue ne peut avoir qu’un seul BindingContext, donc plusieurs liaisons de données sur cette vue doivent toutes les propriétés de référence du même objet.

La solution à ceci et à d’autres problèmes implique la Mode propriété, qui est définie sur un membre de l’énumération BindingMode :

  • Default
  • OneWay — les valeurs sont transférées de la source vers la cible
  • OneWayToSource — les valeurs sont transférées de la cible à la source
  • TwoWay — les valeurs sont transférées de deux façons entre la source et la cible
  • OneTime — les données vont de la source à la cible, mais uniquement lorsque les BindingContext modifications

Le programme suivant illustre une utilisation courante des modes de liaison et TwoWay de OneWayToSource liaison. Quatre Slider vues sont destinées à contrôler les RotateRotateY ScaleRotateXpropriétés d’un .Label Au début, il semble que ces quatre propriétés de la Label liaison de données doivent être des cibles de liaison de données, car chacune est définie par un Slider. Toutefois, il BindingContext ne peut s’agir que d’un Label seul objet et il existe quatre curseurs différents.

Pour cette raison, toutes les liaisons sont définies de manière apparemment descendante : l’ensemble BindingContext des quatre curseurs est défini sur le Labelcurseur et les liaisons sont définies sur les Value propriétés des curseurs. À l’aide des modes et des OneWayToSource modes, ces Value propriétés peuvent définir les propriétés sources, qui sont les Scalepropriétés , et RotateY RotateRotateXles propriétés des Labeléléments suivants :TwoWay

<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>

Les liaisons sur trois des Slider vues sont OneWayToSource, ce qui signifie que la Slider valeur provoque une modification dans la propriété de son BindingContext, qui est le nom .labelLabel Ces trois Slider vues entraînent des modifications apportées aux propriétés et RotateY aux RotateRotateXpropriétés du Label.

Toutefois, la liaison de la Scale propriété est TwoWay. Cela est dû au fait que la propriété a une valeur par défaut de 1 et que l’utilisation Scale d’une TwoWay liaison entraîne la définition de la Slider valeur initiale à 1 au lieu de 0. Si cette liaison était OneWayToSource, la Scale propriété est initialement définie sur 0 à partir de la Slider valeur par défaut. Ce Label n’est pas visible et cela peut entraîner une certaine confusion pour l’utilisateur.

Liaisons descendantes

Remarque

La VisualElement classe a ScaleX également et ScaleY les propriétés, qui mettez à l’échelle respectivement l’axe VisualElement x et l’axe y.

Liaisons et collections

Rien n’illustre la puissance des liaisons XAML et des liaisons de données mieux qu’un modèle ListView.

ListView définit une ItemsSource propriété de type IEnumerableet affiche les éléments de cette collection. Ces éléments peuvent être des objets de n’importe quel type. Par défaut, ListView utilise la ToString méthode de chaque élément pour afficher cet élément. Parfois, c’est juste ce que vous voulez, mais dans de nombreux cas, ToString retourne uniquement le nom de classe complet de l’objet.

Toutefois, les éléments de la ListView collection peuvent être affichés comme vous le souhaitez par le biais de l’utilisation d’un modèle, ce qui implique une classe qui dérive de Cell. Le modèle est cloné pour chaque élément dans les ListViewliaisons de données qui ont été définies sur le modèle sont transférées vers les clones individuels.

Très souvent, vous souhaiterez créer une cellule personnalisée pour ces éléments à l’aide de la ViewCell classe. Ce processus est quelque peu désordonné dans le code, mais en XAML, il devient très simple.

Inclus dans le projet XamlSamples est une classe appelée NamedColor. Chaque NamedColor objet a Name et FriendlyName propriétés de type string, et une Color propriété de type Color. De plus, NamedColor 141 champs statiques en Color lecture seule correspondent aux couleurs définies dans la Xamarin.FormsColor classe. Un constructeur statique crée une collection qui contient NamedColor des IEnumerable<NamedColor> objets correspondant à ces champs statiques et l’affecte à sa propriété statique All publique.

La définition de la propriété statique NamedColor.All sur l’une ItemsSource d’entre elle ListView est facile à l’aide de l’extension de x:Static balisage :

<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>

L’affichage résultant établit que les éléments sont vraiment de type XamlSamples.NamedColor:

Liaison à une collection

Ce n’est pas beaucoup d’informations, mais le défilement et la ListView sélection sont accessibles.

Pour définir un modèle pour les éléments, vous souhaiterez décomposer la ItemTemplate propriété en tant qu’élément de propriété et la définir sur un DataTemplate, qui fait ensuite référence à un ViewCell. Pour la View propriété du ViewCell fichier, vous pouvez définir une disposition d’une ou plusieurs vues pour afficher chaque élément. Voici un exemple simple :

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

Remarque

La source de liaison pour les cellules et les enfants de cellules est la ListView.ItemsSource collection.

L’élément Label est défini sur la View propriété du ViewCell. (Les ViewCell.View balises ne sont pas nécessaires, car la View propriété est la propriété de contenu de ViewCell.) Ce balisage affiche la FriendlyName propriété de chaque NamedColor objet :

Liaison à une collection avec un DataTemplate

Beaucoup mieux. Maintenant, tout ce qui est nécessaire est d’épilèver le modèle d’élément avec plus d’informations et la couleur réelle. Pour prendre en charge ce modèle, certaines valeurs et objets ont été définis dans le dictionnaire de ressources de la page :

<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>

Notez l’utilisation de OnPlatform définir la taille d’un BoxView et la hauteur des ListView lignes. Bien que les valeurs de toutes les plateformes soient identiques, le balisage peut facilement être adapté à d’autres valeurs pour affiner l’affichage.

Convertisseurs de valeur de liaison

Le fichier XAML de démonstration ListView précédent affiche les propriétés et les propriétés individuelles GRB de laColor Xamarin.Formsstructure. Ces propriétés sont de type double et vont de 0 à 1. Si vous souhaitez afficher les valeurs hexadécimales, vous ne pouvez pas simplement utiliser StringFormat avec une spécification de mise en forme « X2 ». Cela ne fonctionne que pour les entiers et en plus, les double valeurs doivent être multipliées par 255.

Ce petit problème a été résolu avec un convertisseur de valeur, également appelé convertisseur de liaison. Il s’agit d’une classe qui implémente l’interface IValueConverter , ce qui signifie qu’elle a deux méthodes nommées Convert et ConvertBack. La Convert méthode est appelée lorsqu’une valeur est transférée de la source à la cible ; la ConvertBack méthode est appelée pour les transferts de la cible à la source dans ou TwoWay des OneWayToSource liaisons :

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;
        }
    }
}

La ConvertBack méthode ne joue pas de rôle dans ce programme, car les liaisons ne sont qu’une seule façon de passer de la source à la cible.

Une liaison fait référence à un convertisseur de liaison avec la Converter propriété. Un convertisseur de liaison peut également accepter un paramètre spécifié avec la ConverterParameter propriété. Pour une certaine polyvalence, il s’agit de la façon dont le multiplicateur est spécifié. Le convertisseur de liaison vérifie le paramètre de convertisseur pour une valeur valide double .

Le convertisseur est instancié dans le dictionnaire de ressources afin qu’il puisse être partagé entre plusieurs liaisons :

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

Trois liaisons de données font référence à cette instance unique. Notez que l’extension de Binding balisage contient une extension de balisage incorporée StaticResource :

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

Voici le résultat :

Liaison à une collection avec un DataTemplate et des convertisseurs

Il ListView est assez sophistiqué dans la gestion des modifications qui peuvent se produire dynamiquement dans les données sous-jacentes, mais seulement si vous effectuez certaines étapes. Si la collection d’éléments affectés à la propriété des modifications pendant l’exécution ItemsSource ListView , autrement dit, si des éléments peuvent être ajoutés ou supprimés de la collection, utilisez une ObservableCollection classe pour ces éléments. ObservableCollection implémente l’interface INotifyCollectionChanged et ListView installe un gestionnaire pour l’événement CollectionChanged .

Si les propriétés des éléments eux-mêmes changent pendant l’exécution, les éléments de la collection doivent implémenter l’interface INotifyPropertyChanged et signaler les modifications apportées aux valeurs de propriété à l’aide de l’événement PropertyChanged . Ceci est illustré dans la prochaine partie de cette série, partie 5. De la liaison de données à MVVM.

Résumé

Les liaisons de données fournissent un mécanisme puissant pour lier des propriétés entre deux objets au sein d’une page ou entre des objets visuels et des données sous-jacentes. Mais lorsque l’application commence à travailler avec des sources de données, un modèle architectural d’application populaire commence à apparaître comme un paradigme utile. Il s’agit de la partie 5. De liaisons de données à MVVM.