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é TextProperty
pouvant ê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 :
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 cibleOneWayToSource
— les valeurs sont transférées de la cible à la sourceTwoWay
— les valeurs sont transférées de deux façons entre la source et la cibleOneTime
— les données vont de la source à la cible, mais uniquement lorsque lesBindingContext
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 Rotate
RotateY
Scale
RotateX
proprié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 Label
curseur 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 Scale
propriétés , et RotateY
Rotate
RotateX
les 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 .label
Label
Ces trois Slider
vues entraînent des modifications apportées aux propriétés et RotateY
aux Rotate
RotateX
proprié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.
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 IEnumerable
et 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 ListView
liaisons 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
:
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 :
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 G
R
B
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 :
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.