Partager via


Xamarin.Forms Convertisseurs de valeur de liaison

Les liaisons de données transfèrent généralement des données d’une propriété source vers une propriété cible et, dans certains cas, de la propriété cible vers la propriété source. Ce transfert est direct lorsque les propriétés source et cible sont du même type, ou quand un type peut être converti vers l’autre type via une conversion implicite. Lorsque ce n’est pas le cas, une conversion de type doit avoir lieu.

Dans l’article Formatage de chaîne, vous avez vu comment utiliser la propriété StringFormat d’une liaison de données pour convertir n’importe quel type en chaîne. Pour les autres types de conversions, vous devez écrire du code spécialisé dans une classe qui implémente l’interface IValueConverter. (Le plateforme Windows universelle contient une classe similaire nommée IValueConverter dans l’espace Windows.UI.Xaml.Data de noms, mais elle IValueConverter se trouve dans l’espace Xamarin.Forms de noms.) Les classes qui implémentent IValueConverter sont appelées convertisseurs de valeur, mais elles sont également souvent appelées convertisseurs de liaison ou convertisseurs de valeur de liaison.

Interface IValueConverter

Supposons que vous souhaitiez définir une liaison de données où la propriété source soit de type int mais la propriété cible de type bool. Vous souhaitez que cette liaison de données produise une valeur false lorsque la source entière est égale à 0, et true dans le cas contraire.

Vous pouvez y parvenir avec une classe qui implémente l'interface IValueConverter :

public class IntToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value != 0;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? 1 : 0;
    }
}

Vous définissez une instance de cette classe sur la propriété Converter de la classe Binding ou sur la propriété Converter de l’extension de balisage Binding. Cette classe devient partie intégrante de la liaison de données.

La méthode Convert est appelée lorsque les données sont transférées de la source vers la cible dans des liaisons OneWay ou TwoWay. Le paramètre value correspond à l’objet ou la valeur de la source de liaison de données. La méthode doit retourner une valeur du type de la cible de liaison de données. La méthode illustrée ici caste le paramètre value en int puis le compare à 0 pour une valeur renvoyée bool.

La méthode ConvertBack est appelée lorsque les données sont transférées de la cible vers la source dans des liaisons TwoWay ou OneWayToSource. ConvertBack effectue la conversion inverse : il suppose que le paramètre value est un bool issu de la cible et le convertit en une valeur renvoyée int pour la source.

Si la liaison de données inclut également un paramètre StringFormat, le convertisseur de valeurs est appelé avant que le résultat soit formaté en tant que chaîne.

La page Activer les boutons de l’exemple montre comment utiliser ce convertisseur de valeurs dans une liaison de données. IntToBoolConverter est instancié dans le dictionnaire de ressources de la page. Il est ensuite référencé avec une extension de balisage StaticResource pour définir la propriété Converter dans deux liaisons de données. Il est très courant de partager des convertisseurs de données entre plusieurs liaisons de données sur la page :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.EnableButtonsPage"
             Title="Enable Buttons">
    <ContentPage.Resources>
        <ResourceDictionary>
            <local:IntToBoolConverter x:Key="intToBool" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />

        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="CenterAndExpand" />

        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="CenterAndExpand"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

Si un convertisseur de valeurs est utilisé dans plusieurs pages de votre application, vous pouvez l’instancier dans le dictionnaire de ressources, dans le fichier App.xaml.

La page Enable Buttons (Activer les boutons) illustre un besoin commun quand un Button effectue une opération basée sur du texte que l’utilisateur tape dans une vue Entry. Si rien n’a été saisi dans Entry, l’élément Button doit être désactivé. Chaque élément Button contient une liaison de données sur sa propriété IsEnabled. La source de liaison de données est la propriété Length de la propriété Text de l’élément Entry correspondant. Si cette propriété Length n’est pas égale à 0, le convertisseur de valeurs retourne true et le Button est activé :

Activer les boutons

Notez que la propriété Text dans chaque Entry est initialisée en tant que chaîne vide. La propriété Text est null par défaut et la liaison de données ne fonctionne pas dans ce cas.

Certains convertisseurs de valeur sont écrits spécifiquement pour des applications spécifiques, tandis que d’autres sont généralisées. Si vous savez qu’un convertisseur de valeurs servira uniquement dans des liaisons OneWay, la méthode ConvertBack peut retourner simplement null.

La méthode Convert illustrée ci-dessus suppose implicitement que l’argument value est de type int et que la valeur renvoyée doit être de type bool. De même, la méthode ConvertBack suppose que l’argument value est de type bool et que la valeur renvoyée est de type int. Si tel n’est pas le cas, une exception runtime se produit.

Vous pouvez écrire des convertisseurs de valeur plus généralisés qui acceptent plusieurs types de données différents. Les méthodes Convert et ConvertBack peuvent utiliser les opérateurs as et is avec le paramètre value, ou peuvent appeler GetType sur ce paramètre pour déterminer son type, puis prendre une mesure appropriée. Le type attendu de la valeur renvoyée de chaque méthode est fourni par le paramètre targetType. Parfois, les convertisseurs de valeur sont utilisés avec des liaisons de données de différents types de cible ; le convertisseur de valeurs peut utiliser l’argument targetType pour effectuer une conversion vers le type correct.

Si la conversion effectuée est différente pour différentes cultures, utilisez le paramètre culture à cet effet. L’argument parameter de Convert et ConvertBack est abordé plus loin dans cet article.

Propriétés des convertisseurs de liaison

Les classes de convertisseur de valeurs peuvent avoir des propriétés et des paramètres génériques. Ce convertisseur de valeurs particulier convertit un bool à partir de la source vers un objet de type T pour la cible :

public class BoolToObjectConverter<T> : IValueConverter
{
    public T TrueObject { set; get; }

    public T FalseObject { set; get; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? TrueObject : FalseObject;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((T)value).Equals(TrueObject);
    }
}

La page Switch Indicators (Indicateurs de commutateur) montre comment l’utiliser pour afficher la valeur d’une vue Switch. Bien qu’il soit courant d’instancier des convertisseurs de valeur comme des ressources dans un dictionnaire de ressources, cette page montre une alternative : chaque convertisseur de valeurs est instancié entre des balises d’élément de propriété Binding.Converter. x:TypeArguments indique l’argument générique, et TrueObject et FalseObject sont tous les deux définis sur des objets de ce type :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.SwitchIndicatorsPage"
             Title="Switch Indicators">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Label">
                <Setter Property="FontSize" Value="18" />
                <Setter Property="VerticalOptions" Value="Center" />
            </Style>

            <Style TargetType="Switch">
                <Setter Property="VerticalOptions" Value="Center" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="CenterAndExpand">
            <Label Text="Subscribe?" />
            <Switch x:Name="switch1" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch1}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Of course!"
                                                         FalseObject="No way!" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="CenterAndExpand">
            <Label Text="Allow popups?" />
            <Switch x:Name="switch2" />
            <Label>
                <Label.Text>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="x:String"
                                                         TrueObject="Yes"
                                                         FalseObject="No" />
                        </Binding.Converter>
                    </Binding>
                </Label.Text>
                <Label.TextColor>
                    <Binding Source="{x:Reference switch2}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Color"
                                                         TrueObject="Green"
                                                         FalseObject="Red" />
                        </Binding.Converter>
                    </Binding>
                </Label.TextColor>
            </Label>
        </StackLayout>

        <StackLayout Orientation="Horizontal"
                     VerticalOptions="CenterAndExpand">
            <Label Text="Learn more?" />
            <Switch x:Name="switch3" />
            <Label FontSize="18"
                   VerticalOptions="Center">
                <Label.Style>
                    <Binding Source="{x:Reference switch3}"
                             Path="IsToggled">
                        <Binding.Converter>
                            <local:BoolToObjectConverter x:TypeArguments="Style">
                                <local:BoolToObjectConverter.TrueObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Indubitably!" />
                                        <Setter Property="FontAttributes" Value="Italic, Bold" />
                                        <Setter Property="TextColor" Value="Green" />
                                    </Style>                                    
                                </local:BoolToObjectConverter.TrueObject>

                                <local:BoolToObjectConverter.FalseObject>
                                    <Style TargetType="Label">
                                        <Setter Property="Text" Value="Maybe later" />
                                        <Setter Property="FontAttributes" Value="None" />
                                        <Setter Property="TextColor" Value="Red" />
                                    </Style>
                                </local:BoolToObjectConverter.FalseObject>
                            </local:BoolToObjectConverter>
                        </Binding.Converter>
                    </Binding>
                </Label.Style>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

Dans la dernière des trois paires de Switch et Label, l’argument générique a la valeur Style et des objets Style entiers sont fournis pour les valeurs de TrueObject et FalseObject. Ceux-ci remplacent le style implicite pour Label, défini dans le dictionnaire de ressources, afin que les propriétés figurant dans ce style soient explicitement affectées à l’objet Label. Lors de l’activation/la désactivation de Switch, l’objet Label correspondant reflète la modification :

Indicateurs de commutateur

Il est également possible d’utiliser Triggers pour implémenter des modifications similaires dans l’interface utilisateur sur la base d’autres vues.

Paramètres des convertisseurs de liaison

La classe Binding définit une propriété ConverterParameter et l’extension de balisage Binding définit également une propriété ConverterParameter. Si cette propriété est définie, la valeur est transmise aux méthodes Convert et ConvertBack en tant qu’argument parameter. Même si l’instance du convertisseur de valeurs est partagée entre plusieurs liaisons de données, l’élément ConverterParameter peut être différent pour effectuer des conversions quelque peu différentes.

L’utilisation de ConverterParameter est illustrée avec un programme de sélection de couleur. Dans ce cas, l’élément RgbColorViewModel a trois propriétés de type double nommées Red, Green et Blue, qu’il utilise pour construire une valeur Color :

public class RgbColorViewModel : INotifyPropertyChanged
{
    Color color;
    string name;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Red
    {
        set
        {
            if (color.R != value)
            {
                Color = new Color(value, color.G, color.B);
            }
        }
        get
        {
            return color.R;
        }
    }

    public double Green
    {
        set
        {
            if (color.G != value)
            {
                Color = new Color(color.R, value, color.B);
            }
        }
        get
        {
            return color.G;
        }
    }

    public double Blue
    {
        set
        {
            if (color.B != value)
            {
                Color = new Color(color.R, color.G, value);
            }
        }
        get
        {
            return color.B;
        }
    }

    public Color Color
    {
        set
        {
            if (color != value)
            {
                color = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Red"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Green"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Blue"));
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Color"));

                Name = NamedColor.GetNearestColorName(color);
            }
        }
        get
        {
            return color;
        }
    }

    public string Name
    {
        private set
        {
            if (name != value)
            {
                name = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
            }
        }
        get
        {
            return name;
        }
    }
}

Les propriétés Red, Green et Blue sont comprises entre 0 et 1. Toutefois, vous préférerez peut-être que les composants soient affichés en tant que valeurs hexadécimales à deux chiffres.

Pour les afficher en tant que valeurs hexadécimales en XAML, ils doivent être multipliés par 255, convertis en entiers puis formatés avec une spécification de « X2 » dans la propriété StringFormat. Les deux premières tâches (multiplication par 255 et conversion en entier) peuvent être gérées par le convertisseur de valeur. Pour généraliser le plus possible le convertisseur de valeur, le facteur de multiplication peut être spécifié avec la propriété ConverterParameter, ce qui signifie qu’il entre les méthodes Convert et ConvertBack en tant qu’argument parameter :

public class DoubleToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((double)value * GetParameter(parameter));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)value / GetParameter(parameter);
    }

    double GetParameter(object parameter)
    {
        if (parameter is double)
            return (double)parameter;

        else if (parameter is int)
            return (int)parameter;

        else if (parameter is string)
            return double.Parse((string)parameter);

        return 1;
    }
}

Convert convertit un double en int tout en le multipliant par la valeur parameter ; ConvertBack divise l’argument value entier par parameter et retourne un résultat double. (Dans le programme illustré ci-dessous, le convertisseur de valeurs est utilisé uniquement en rapport avec le formatage de chaîne, si bien que ConvertBack n’est pas utilisé.)

Le type de l’argument parameter est susceptible de varier selon que la liaison de données est définie dans le code ou en XAML. Si la propriété ConverterParameter de Binding est définie dans le code, l’argument est probablement défini sur une valeur numérique :

binding.ConverterParameter = 255;

La propriété ConverterParameter est de type Object, si bien que le compilateur C# interprète le littéral 255 comme un entier et définit la propriété sur cette valeur.

En XAML, toutefois, l’élément ConverterParameter sera probablement défini comme suit :

<Label Text="{Binding Red,
                      Converter={StaticResource doubleToInt},
                      ConverterParameter=255,
                      StringFormat='Red = {0:X2}'}" />

L’élément 255 ressemble à un nombre, mais comme ConverterParameter est de type Object, l’analyseur XAML traite 255 en tant que chaîne.

Pour cette raison, le convertisseur de valeurs illustré ci-dessus inclut une méthode GetParameter distincte qui gère des cas où parameter est de type double, int ou string.

La page RGB Color Selector (Sélecteur de couleur RVB) instancie DoubleToIntConverter dans son dictionnaire de ressources qui suit la définition de deux styles implicites :

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.RgbColorSelectorPage"
             Title="RGB Color Selector">
    <ContentPage.Resources>
        <ResourceDictionary>
            <Style TargetType="Slider">
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
            </Style>

            <Style TargetType="Label">
                <Setter Property="HorizontalTextAlignment" Value="Center" />
            </Style>

            <local:DoubleToIntConverter x:Key="doubleToInt" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <StackLayout.BindingContext>
            <local:RgbColorViewModel Color="Gray" />
        </StackLayout.BindingContext>

        <BoxView Color="{Binding Color}"
                 VerticalOptions="FillAndExpand" />

        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />

            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource doubleToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />

            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource doubleToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />

            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource doubleToInt}">
                        <Binding.ConverterParameter>
                            <x:Double>255</x:Double>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>    

Les valeurs des propriétés Red et Green sont affichées avec une extension de balisage Binding. La propriété Blue, toutefois, instancie la classe Binding afin d’illustrer comment une valeur double explicite peut être définie sur la propriété ConverterParameter.

Voici le résultat :

Sélecteur de couleurs RVB