Convertitori di valori di associazione
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:
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:
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 , RgbColorViewModel
che ha tre proprietà di tipo float
denominato Red
, Green
e 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 Red
proprietà , Green
e 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
, int
o 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à :