Condividi tramite


Convertitori di valori di associazione

Browse sample. Esplorare l'esempio

I data binding dell'interfaccia utente dell'app multipiattaforma .NET (.NET MAUI) in genere trasferiscono i dati da una proprietà di origine a una proprietà di destinazione e in alcuni casi dalla proprietà di destinazione alla proprietà di origine. Si tratta di un semplice trasferimento quando la proprietà di origine e quella di destinazione sono dello stesso tipo o quando un tipo può essere convertito nell'altro tipo tramite una conversione implicita. Se non è questo il caso, è necessaria una conversione del tipo.

Nell'articolo Formattazione stringa è stato illustrato come usare la StringFormat proprietà di un data binding per convertire qualsiasi tipo in una stringa. Per altri tipi di conversioni, è necessario scrivere codice specializzato in una classe che implementa l'interfaccia IValueConverter. Le classi che implementano IValueConverter vengono chiamate convertitori di valori, ma vengono anche definite convertitori di data binding oppure convertitori di valori di binding.

Convertitori di valori di associazione

Si supponga di voler definire un data binding in cui la proprietà di origine è di tipo int mentre la proprietà di destinazione è di tipo bool. Questo data binding deve generare un valore false quando l'origine del numero intero è uguale a 0; in caso contrario, deve generare true. Questa operazione può essere ottenuta con una classe che implementa l'interfaccia 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;
    }
}

Si imposta quindi un'istanza di questa classe sulla Converter proprietà della Binding classe o sulla Converter proprietà dell'estensione Binding di markup. Questa classe diventa parte dal data binding.

Il metodo Convert viene chiamato quando i dati vengono trasferiti dall'origine alla destinazione in binding OneWay o TwoWay. Il parametro value rappresenta l'oggetto o il valore ricevuto dall'origine del data binding. Il metodo deve restituire un valore del tipo della destinazione del data binding. Il metodo illustrato di seguito esegue il cast del parametro value per un valore int e lo confronta con 0 per un valore restituito bool.

Il metodo ConvertBack viene chiamato quando i dati vengono trasferiti dalla destinazione all'origine in binding TwoWay o OneWayToSource. Il metodo ConvertBack esegue la conversione inversa. Suppone che il parametro value sia un tipo bool della destinazione e lo converte in valore restituito int per l'origine.

Nota

Se un data binding include anche un'impostazione StringFormat , il convertitore di valori viene richiamato prima che il risultato venga formattato come stringa.

Nell'esempio seguente viene illustrato come usare questo convertitore di valori in un data binding:

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

    <StackLayout Padding="10, 0">
        <Entry x:Name="entry1"
               Text=""
               Placeholder="enter search term"
               VerticalOptions="Center" />
        <Button Text="Search"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry1},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
        <Entry x:Name="entry2"
               Text=""
               Placeholder="enter destination"
               VerticalOptions="Center" />
        <Button Text="Submit"
                HorizontalOptions="Center"
                VerticalOptions="Center"
                IsEnabled="{Binding Source={x:Reference entry2},
                                    Path=Text.Length,
                                    Converter={StaticResource intToBool}}" />
    </StackLayout>
</ContentPage>

In questo esempio viene IntToBoolConverter creata un'istanza di nel dizionario risorse della pagina. Viene quindi fatto riferimento a un'estensione StaticResource di markup per impostare la Converter proprietà in due data binding. È molto comune condividere convertitori di dati tra più data binding nella pagina. Se un convertitore di valori viene usato in più pagine dell'applicazione, è possibile crearne un'istanza nel dizionario risorse a livello di applicazione.

In questo esempio viene illustrata una necessità comune quando un'operazione Button viene eseguita in base al testo digitato dall'utente in una Entry visualizzazione. La Text proprietà di ogni Entry oggetto viene inizializzata in una stringa vuota, perché la Text proprietà è null per impostazione predefinita e il data binding non funzionerà in questo caso. Se in Entry non viene digitato nulla, Button sarà disabilitato. Ogni oggetto Button contiene un data binding nella relativa proprietà IsEnabled. L'origine del data binding è la proprietà Length della proprietà Text dell'oggetto Entry corrispondente. Se la proprietà Length non è 0, il convertitore di valori restituisce true e Button viene abilitato:

Enable buttons.

Nota

Se si sa che un convertitore di valori non viene usato nei binding OneWay, il metodo ConvertBack potrà restituire soltanto null.

Il Convert metodo illustrato in precedenza presuppone che l'argomento value sia di tipo int e che il valore restituito sia di tipo bool. Analogamente, il metodo ConvertBack presuppone che l'argomento value è di tipo bool e il valore restituito è int. Se non è questo il caso, verrà generata un'eccezione in fase di esecuzione.

È possibile scrivere convertitori di valori in modo che siano più generici e accettino diversi tipi di dati. I metodi Convert e ConvertBack possono usare gli operatori as e is con il parametro value oppure possono chiamare GetType per tale parametro al fine di determinarne il tipo e procedere nel modo appropriato. Il tipo previsto di ogni valore restituito del metodo viene specificato dal parametro targetType. In alcuni casi, i convertitori di valori vengono usati con data binding di tipi di destinazione diversi. In questo caso il convertitore di valori può usare l'argomento targetType per eseguire una conversione per il tipo corretto.

Se la conversione eseguita varia a seconda delle impostazioni cultura, usare il parametro culture.

Proprietà del convertitore di binding

Le classi del convertitore di valori possono avere proprietà e parametri generici. Il convertitore di valori seguente converte un oggetto bool dall'origine a un oggetto di tipo T per la destinazione:

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

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

Nell'esempio seguente viene illustrato come usare questo convertitore per visualizzare il valore di una Switch visualizzazione. Anche se è comune creare istanze di convertitori di valori come risorse in un dizionario risorse, questo esempio dimostra un'alternativa. In questo caso, ogni convertitore di valori viene creata un'istanza tra Binding.Converter i tag dell'elemento proprietà. x:TypeArguments indica l'argomento generico. TrueObject e FalseObject sono entrambi impostati su oggetti di quel tipo:

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

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

    <StackLayout Padding="10, 0">
        <StackLayout Orientation="Horizontal"
                     VerticalOptions="Center">
            <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="Center">
            <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="Center">
            <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>

In questo esempio, nell'ultima delle tre Switch coppie e Label , l'argomento generico viene impostato su un Styleoggetto e vengono forniti interi Style oggetti per i valori di TrueObject e FalseObject. Questi eseguono l'override dello stile implicito per Label impostato nel dizionario risorse. Le proprietà in tale stile vengono quindi assegnate in modo esplicito all'oggetto Label. L'attivazione/disattivazione di Switch determina la modifica dell'oggetto Label corrispondente:

Switch indicators.

Nota

È anche possibile usare i trigger per implementare le modifiche nell'interfaccia utente in base ad altre visualizzazioni. Per altre informazioni, consultare Trigger.

Parametri del convertitore di associazioni

La classe Binding definisce una proprietà ConverterParameter. Anche l'estensione di markup Binding definisce una proprietà ConverterParameter. Se si imposta questa proprietà, il valore viene passato ai metodi Convert e ConvertBack come argomento parameter. Anche se l'istanza del convertitore di valori viene condivisa tra più data binding, può ConverterParameter essere diversa per eseguire conversioni diverse.

L'uso della ConverterParameter proprietà può essere illustrato con un programma di selezione colori. Nell'esempio seguente viene illustrato , RgbColorViewModelche ha tre proprietà di tipo float denominato Red, Greene Blue che usa per costruire un Color valore:

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

    public event PropertyChangedEventHandler PropertyChanged;

    public float Red
    {
        get { return color.Red; }
        set
        {
            if (color.Red != value)
            {
                Color = new Color(value, color.Green, color.Blue);
            }
        }
    }

    public float Green
    {
        get { return color.Green; }
        set
        {
            if (color.Green != value)
            {
                Color = new Color(color.Red, value, color.Blue);
            }
        }
    }

    public float Blue
    {
        get { return color.Blue; }
        set
        {
            if (color.Blue != value)
            {
                Color = new Color(color.Red, color.Green, value);
            }
        }
    }

    public Color Color
    {
        get { return 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);
            }
        }
    }

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

I valori delle Redproprietà , Greene Blue possono essere compresi tra 0 e 1. Potrebbe tuttavia essere preferibile visualizzare i componenti come valori esadecimali a due cifre. Per visualizzare queste informazioni come valori esadecimali in XAML, moltiplicare i valori per 255, convertirli in un numero intero e formattarli usando una specifica di "X2" nella proprietà StringFormat. La moltiplicazione per 255 e la conversione in un numero intero può essere eseguita dal convertitore di valori. Perché il convertitore di valori sia il più possibile generico, è possibile specificare il fattore di moltiplicazione con la proprietà ConverterParameter. In questo modo i metodi Convert e ConvertBack vengono immessi come argomento parameter:

public class FloatToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (int)Math.Round((float)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 float)
            return (float)parameter;
        else if (parameter is int)
            return (int)parameter;
        else if (parameter is string)
            return float.Parse((string)parameter);

        return 1;
    }
}

In questo esempio, il Convert metodo viene convertito da a float int durante la moltiplicazione per il parameter valore. Il ConvertBack metodo divide l'argomento integer value per parameter e restituisce un float risultato.

Il tipo dell'argomento parameter è probabilmente diverso a seconda che il data binding sia definito in XAML o nel codice. Se la proprietà ConverterParameter di Binding è impostata nel codice, è probabile che sia impostato un valore numerico:

binding.ConverterParameter = 255;

La proprietà ConverterParameter è di tipo Object, pertanto il compilatore C# interpreta il valore letterale 255 come numero intero e imposta la proprietà su tale valore.

Tuttavia, in XAML ConverterParameter è probabile che sia impostato come segue:

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

Anche se 255 ha un aspetto simile a un numero, poiché ConverterParameter è di tipo Object, il parser XAML considera 255 come stringa. Per questo motivo il convertitore di valori include un metodo separato GetParameter che gestisce i case per parameter essere di tipo float, into string.

L'esempio XAML seguente crea un'istanza FloatToIntConverter nel relativo dizionario risorse:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:DataBindingDemos"
             x:Class="DataBindingDemos.RgbColorSelectorPage"
             Title="RGB Color Selector">
    <ContentPage.BindingContext>
        <local:RgbColorViewModel Color="Gray" />
    </ContentPage.BindingContext>
    <ContentPage.Resources>
        <Style TargetType="Slider">
            <Setter Property="VerticalOptions" Value="Center" />
        </Style>

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

        <local:FloatToIntConverter x:Key="floatToInt" />
    </ContentPage.Resources>

    <StackLayout Margin="20">
        <BoxView Color="{Binding Color}"
                 HeightRequest="100"
                 WidthRequest="100"
                 HorizontalOptions="Center" />
        <StackLayout Margin="10, 0">
            <Label Text="{Binding Name}" />
            <Slider Value="{Binding Red}" />
            <Label Text="{Binding Red,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Red = {0:X2}'}" />
            <Slider Value="{Binding Green}" />
            <Label Text="{Binding Green,
                                  Converter={StaticResource floatToInt},
                                  ConverterParameter=255,
                                  StringFormat='Green = {0:X2}'}" />
            <Slider Value="{Binding Blue}" />
            <Label>
                <Label.Text>
                    <Binding Path="Blue"
                             StringFormat="Blue = {0:X2}"
                             Converter="{StaticResource floatToInt}">
                        <Binding.ConverterParameter>
                            <x:Single>255</x:Single>
                        </Binding.ConverterParameter>
                    </Binding>
                </Label.Text>
            </Label>
        </StackLayout>
    </StackLayout>
</ContentPage>

I valori delle proprietà Red e Green vengono visualizzati con un'estensione di markup Binding. La Blue proprietà crea tuttavia un'istanza della Binding classe per illustrare come impostare un valore esplicito float sulla ConverterParameter proprietà :

RGB color selector.