Преобразователи значений привязки
Привязки данных .NET Multi-platform App UI (.NET MAUI) обычно передают данные из исходного свойства в целевое свойство, а в некоторых случаях из целевого свойства в исходное свойство. Эта передача проста в том случае, когда исходные и целевые свойства относятся к одному типу или когда один тип может быть преобразован в другой тип путем неявного преобразования. Если это не так, должно выполняться преобразование типов.
В статье о форматировании строк вы узнали, как использовать StringFormat
свойство привязки данных для преобразования любого типа в строку. Для других типов преобразований необходимо написать специальный код в классе, реализующем интерфейс IValueConverter. Классы, реализующие IValueConverter, называются преобразователями величин, а также преобразователями привязки или преобразователями значений привязки.
Преобразователи значений привязки
Предположим, что вы хотите определить привязку данных, в которой исходное свойство имеет тип int
, а целевым свойством является bool
. Требуется, чтобы эта привязка данных создавала значение false
, если целочисленный источник равняется нулю, и значение true
в противном случае. Это можно сделать с помощью класса, реализующего 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;
}
}
Затем экземпляр этого класса присваивает свойству Binding
класса Converter
или Converter
свойству Binding
расширения разметки. Этот класс становится частью привязки данных.
Метод Convert
вызывается при перемещении данных из источника в целевое свойство в привязках OneWay
или TwoWay
. Параметр value
— это объект или значение из источника привязки данных. Метод должен возвращать значение типа целевого свойства привязки данных. Показанный здесь метод приводит параметр value
к int
, а затем сравнивает их с нулем для получения логического (bool
) возвращаемого значения.
Метод ConvertBack
вызывается, когда данные перемещаются из целевого в исходное свойство в привязках TwoWay
или OneWayToSource
. ConvertBack
выполняет обратное преобразование: он предполагает, что параметр value
является bool
из целевого свойства и преобразует его в возвращаемое значение int
для источника.
Примечание.
Если привязка данных также включает StringFormat
параметр, преобразователь значений вызывается до форматирования результата в виде строки.
В следующем примере показано, как использовать этот преобразователь значений в привязке данных:
<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 x:DataType="Entry"
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 x:DataType="Entry"
Text="Submit"
HorizontalOptions="Center"
VerticalOptions="Center"
IsEnabled="{Binding Source={x:Reference entry2},
Path=Text.Length,
Converter={StaticResource intToBool}}" />
</StackLayout>
</ContentPage>
В этом примере IntToBoolConverter
экземпляр создается в словаре ресурсов страницы. Затем он ссылается с расширением StaticResource
разметки, чтобы задать Converter
свойство в двух привязках данных. Очень часто используются преобразователи данных между несколькими привязками данных на странице. Если преобразователь значений используется на нескольких страницах приложения, его можно создать в словаре ресурсов на уровне приложения.
В этом примере демонстрируется общая потребность при Button выполнении операции на основе текста, который пользователь вводит в Entry представление. Свойство Text
каждого из них Entry инициализировано в пустую строку, так как Text
свойство по null
умолчанию, а привязка данных в этом случае не будет работать. Если никакие данные не были введены в Entry, Button должен быть отключен. Каждый элемент Button содержит привязку данных для его свойства IsEnabled
. Источником привязки данных является свойство Length
свойства Text
соответствующего элемента Entry. Если это свойство Length
не возвращает нуль, преобразователь величин возвращает true
и включается элемент Button:
Примечание.
Если вы знаете, что преобразователь величин будет использоваться только в привязках OneWay
, то метод ConvertBack
может просто возвращать null
.
В приведенном Convert
выше методе предполагается, что value
аргумент имеет тип int
, а возвращаемое значение должно иметь тип bool
. Аналогичным образом метод ConvertBack
предполагает, что аргумент value
имеет тип bool
, и возвращает значение int
. Если это не так, возникнет исключение времени выполнения.
Вы можете написать более общие преобразователи величин, которые принимают несколько разных типов данных. Методы Convert
и ConvertBack
могут использовать операторы as
или is
с параметром value
либо вызывать GetType
для этого параметра, чтобы определить его тип, а затем выполнять соответствующие действия. Ожидаемый тип возвращаемого значения метода задается параметром targetType
. Иногда преобразователи значений используются с привязками данных различных целевых типов. В этом случае преобразователь значений может использовать targetType
аргумент для преобразования правильного типа.
Если выполняемое преобразование отличается для разных языков и региональных параметров, используйте для этой цели параметр culture
.
Свойства преобразователя привязки
Классы преобразователей величин могут иметь свойства и универсальные параметры. Следующий преобразователь значений преобразует bool
из источника в объект типа T
целевого объекта:
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);
}
}
В следующем примере показано, как этот преобразователь можно использовать для отображения значения Switch представления. Хотя в словаре ресурсов обычно создается экземпляр преобразователей значений в качестве ресурсов, в этом примере демонстрируется альтернатива. Здесь каждый преобразователь значений создается между Binding.Converter
тегами элементов свойств. x:TypeArguments
указывает универсальный аргумент, а для TrueObject
и FalseObject
задаются объекты этого типа:
<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 x:DataType="Switch"
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 x:DataType="Switch"
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 x:DataType="Switch"
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 x:DataType="Switch"
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>
В этом примере в последнем из трех Switch и Label пар универсальный аргумент имеет Styleзначение , а целые Style объекты предоставляются для значений TrueObject
и FalseObject
. Они переопределяют неявный стиль для Label, заданный в словаре ресурсов, поэтому свойства в этом стиле явно назначаются Label. Включение и выключение Switch приводит к тому, что изменения отражаются в соответствующем Label:
Примечание.
Также можно использовать триггеры для реализации изменений в пользовательском интерфейсе на основе других представлений. Дополнительные сведения см. в описании триггеров.
Параметры преобразователя привязки
Класс Binding
определяет свойство ConverterParameter
, и расширение разметки Binding
также определяет свойство ConverterParameter
. Если это свойство задано, то значение передается в методы Convert
и ConvertBack
как аргумент parameter
. Даже если экземпляр преобразователя значений является общим для нескольких привязок данных, ConverterParameter
может отличаться для выполнения различных преобразований.
Использование ConverterParameter
свойства можно продемонстрировать с помощью программы выбора цвета. В следующем примере показаны RgbColorViewModel
три свойства типа float
с именем Red
, Green
а Blue
также то, что используется для создания Color значения:
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"));
}
}
}
}
Значения Red
свойств и Blue
свойств Green
могут варьироваться от 0 до 1. При этом требуется, чтобы компоненты отображались как шестнадцатеричные значения из двух цифр. Чтобы отобразить их в виде шестнадцатеричных значений в XAML, они должны быть умножены на 255, преобразованы в целое число, а затем отформатированы со спецификацией X2 в свойстве StringFormat
. Умножение на 255 и преобразование в целое число может выполняться преобразователем значений. Чтобы сделать преобразователь величин максимально универсальным, можно указать множитель с помощью свойства ConverterParameter
, которое означает, что он вводит методы Convert
и ConvertBack
как аргумент 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;
}
}
В этом примере Convert
метод преобразуется из a float
в int
то время, как умножается на parameter
значение. Метод ConvertBack
делит целый value
аргумент на parameter
и возвращает float
результат.
Тип аргумента parameter
может отличаться в зависимости от того, определена ли привязка данных в XAML или коде. Если свойство ConverterParameter
объекта Binding
задается в коде, вполне вероятно, ему будет присвоено числовое значение:
binding.ConverterParameter = 255;
Свойство ConverterParameter
имеет тип Object
, поэтому компилятор C# интерпретирует литерал 255 как целое число и присваивает это значение свойству.
Однако в XAML ConverterParameter
скорее всего будет задано следующее:
<Label Text="{Binding Red,
Converter={StaticResource doubleToInt},
ConverterParameter=255,
StringFormat='Red = {0:X2}'}" />
Хотя 255 выглядит как число, так как ConverterParameter
имеет тип Object
, средство синтаксического анализа XAML обрабатывает 255 как строку. По этой причине преобразователь значений float
включает отдельный GetParameter
метод, который обрабатывает варианты для parameter
типа , int
или string
.
Следующий пример XAML создает FloatToIntConverter
экземпляры в словаре ресурсов:
<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"
x:DataType="local:RgbColorViewModel">
<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>
Значения свойств Red
и Green
отображаются с помощью расширения разметки Binding
. Однако Blue
свойство создает экземпляр Binding
класса, чтобы продемонстрировать, как явное float
значение можно задать для ConverterParameter
свойства: