Содержание
Не всегда бывает удобным (и возможным) отображать в элементе управления или передавать в свойство целевого объекта текущее значение свойства источника привязки. Например, нам может потребоваться каким-либо образом форматировать строку со значением из источника прежде, чем вывести её в тексте метки. Или привязываемые свойства вообще не совпадают по типу — одно свойство имеет тип Double, а второе, скажем Color и нам необходимо «вытащить» из Color, например, только значение яркости и так далее. В этом случае мы можем использовать при привязке данных форматирование строк или написать свой конвертер значений и применить его в привязке.
Форматирование строк в привязке
Форматирование строк производится с использованием метода string.Format(), а для использования этого метода у расширения разметки Binding имеется параметр StringFormat. Этот параметр стоит использовать в том случае, если целевое свойство является строкой. Например, создадим новое приложение .NET MAUI и напишем следующий код страницы MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp13.MainPage">
<ScrollView>
<VerticalStackLayout
<Label Text="{Binding Source={x:Reference RValue},
Path=Value,
StringFormat='Красный {0:F0}'}"/>
<Slider x:Name="RValue" Maximum="255" Minimum="0"/>
<Label Text="{Binding Source={x:Reference GValue},
Path=Value,
StringFormat='Зеленый {0:F0}'}"/>
<Slider x:Name="GValue" Maximum="255" Minimum="0"/>
<Label Text="{Binding Source={x:Reference BValue},
Path=Value,
StringFormat='Синий {0:F0}'}"/>
<Slider x:Name="BValue" Maximum="255" Minimum="0"/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Здесь метки Label привязываются к свойствам Value трех элементов Slider. При этом, для Binding задано форматирование строк, например:
StringFormat='Зеленый {0:F0}'}"
Таким образом, при выводе текста, строка будет отформатирована и на экране мы увидим, например, вот такой результат:
Теперь попробуем ответить на вопрос: каким образом мы можем использовать значения из трех Slider, чтобы сформировать из них цвет Color и передать это значение, например, в качестве фона страницы? Для выполнения такой задачи нам пригодятся конвертеры значений.
Конвертеры значений
Привязка свойств в .NET MAUI производится, если оба свойства совпадают по типу. При этом, для примитивных типов инфраструктура .NET MAUI может сама конвертировать значение свойства из одного типа в другой. Например, выше мы без лишних проблем привязываем свойство Text у метки к свойству Value у Slider, несмотря на то, что целевое свойство имеет тип string, а исходное — double. Однако, возможностей инфраструктуры .NET MAUI не всегда достаточно и может потребоваться написать свой собственный конвертер значений.
В .NET MAUI можно выделить два типа конвертеров значений:
- Конвертеры, которые преобразуют одно значение. Классы таких конвертеров должны реализовывать интерфейс
IValueConverter - Конвертеры, которые преобразуют несколько значений. Классы таких конвертеров должны реализовывать интерфейс
IMultiValueConverter
Изучим эти типы конвертеров на нашем примере.
IValueConverter
Пользовательский конвертер, реализующий интерфейс IValueConverter используется в том случае, когда необходимо конвертировать одно значение в другое. Например, наши элементы Slider используют тип double для свойства Value. Но нам может потребоваться, чтобы в метке выводилось, например, HEX-значение. Для этого мы можем реализовать свой конвертер значений.
Добавим в проект новый класс DoubleToHexConverter
public class DoubleToHexConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value == null) return null;
var data = System.Convert.ToInt32(value);
return data.ToString("X");
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value == null) return null;
return (double)int.Parse((string)value, NumberStyles.HexNumber);
}
}
Здесь у конвертера реализованы два метода интерфейса IValueConverter:
Convert(), который преобразует значение свойства источника в тип, который понимается целью (в нашем случае — изdoubleв hex). МетодConvert()вызывается при использовании режимов привязкиOneWayилиTwoWay.ConvertBack(), который выполняет противоположную операцию — из hex вdouble. Этот метод вызывается при использовании режимов привязокTwoWayилиOneWayToSource
Оба эти метода содержат по четыре параметра:
| Параметр | Тип | Описание |
value |
object |
значение, которое надо преобразовать |
targetType |
Type |
тип, к которому надо преобразовать значение value |
parameter |
object |
дополнительный параметр, который может использовать при конвертировании |
culture |
CultureInfo |
текущая культура в приложении |
В нашем случае мы реализовали два оба метода, хотя, было бы достаточно и одного Convert(), так как мы не передаем никаких данных от цели к источнику.
Воспользуемся конвертером в нашем приложении.
Использование конвертеров значений XAML
Чтобы воспользоваться конвертером значений в XAML мы должны зарегистрировать этот конвертер как ресурс и передать этот ресурс в параметр Converter расширения Binding.
Изменим код страницы следующим образом:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:MauiApp13"
x:Class="MauiApp13.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<converter:DoubleToHexConverter x:Key="DoubleToHexConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ScrollView>
<VerticalStackLayout>
<Label Text="{Binding Source={x:Reference RValue}, Path=Value, StringFormat='Красный {0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
<Slider x:Name="RValue" Maximum="255" Minimum="0"/>
<Label Text="{Binding Source={x:Reference GValue}, Path=Value, StringFormat='Зеленый {0:F0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
<Slider x:Name="GValue" Maximum="255" Minimum="0"/>
<Label Text="{Binding Source={x:Reference BValue}, Path=Value, StringFormat='Синий {0:F0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
<Slider x:Name="BValue" Maximum="255" Minimum="0"/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Первое, что мы сделали — это добавили пространство имен в котором содержится конвертер:
xmlns:converter="clr-namespace:MauiApp13"
Далее, мы зарегистрировали наш конвертер, как ресурс:
<ContentPage.Resources>
<ResourceDictionary>
<converter:DoubleToHexConverter x:Key="DoubleToHexConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
и, наконец, использовали этот ресурс в расширении Binding
<Label Text="{Binding Source={x:Reference RValue}, Path=Value, StringFormat='Красный {0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
Так как мы используем и параметр StringFormat и параметр Converter, то стоит отметить, что вначале сработает конвертер и только потом строка будет отформатирована. То есть приложение будет выглядеть как показано на рисунке ниже:
Теперь вернемся к нашему основному вопросу: как преобразовать сразу три значения Value в одно — Color? Здесь нам пригодится второй тип конвертера — IMultiValueConverter
IMultiValueConverter и MultiBinding
Иногда нам необходимо привязать сразу несколько значений свойств к одному целевому. Например, как в нашем случае — мы должны каким-то образом обработать три значения Value, чтобы получить одно — Color. В .NET MAUI и XAML для таких задач может использоваться множественная привязка или MultiBinding. Расширение MultiBinding работает также как и уже известное нам Binding, за одним различием — в это расширение мы должны передавать список привязок, то есть список объектов класса BindingBase. Чтобы воспользоваться MultiBinding в нашем приложении мы также должны создать свой класс конвертера, который реализует интерфейс IMultiValueConverter.
Итак, добавим в наш проект новый класс RgbConverter:
public class RgbConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var r = System.Convert.ToByte(values[0]);
var g = System.Convert.ToByte(values[1]);
var b = System.Convert.ToByte(values[2]);
return Color.FromRgb(r, g, b);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Единственное отличие этого конвертера от предыдущего заключается в том, что в качестве первого параметра в методе Convert() мы принимаем не одно, а массив значений, а в методе ConvertBack() мы должны, наоборот — вернуть массив значений из одного полученного. В нашем случае, нам достаточно реализовать для работы только метод Convert(), что мы и сделали. Теперь вернемся к нашему приложению и воспользуемся конвертером:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:MauiApp13"
x:Class="MauiApp13.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<converter:RgbConverter x:Key="RgbConverter"/>
<converter:DoubleToHexConverter x:Key="DoubleToHexConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Background>
<MultiBinding Converter="{StaticResource RgbConverter}">
<MultiBinding.Bindings>
<Binding Path="Value" Source="{x:Reference RValue}" />
<Binding Path="Value" Source="{x:Reference GValue}" />
<Binding Path="Value" Source="{x:Reference BValue}" />
</MultiBinding.Bindings>
</MultiBinding>
</ContentPage.Background>
<ScrollView>
<VerticalStackLayout>
<Label Text="{Binding Source={x:Reference RValue}, Path=Value, StringFormat='Красный {0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
<Slider x:Name="RValue" Maximum="255" Minimum="0"/>
<Label Text="{Binding Source={x:Reference GValue}, Path=Value, StringFormat='Зеленый {0:F0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
<Slider x:Name="GValue" Maximum="255" Minimum="0"/>
<Label Text="{Binding Source={x:Reference BValue}, Path=Value, StringFormat='Синий {0:F0}', Converter={x:StaticResource DoubleToHexConverter}}"/>
<Slider x:Name="BValue" Maximum="255" Minimum="0"/>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Мы также зарегистрировали конвертер, как ресурс и обратите внимание, как он был использован в коде:
<ContentPage.Background>
<MultiBinding Converter="{StaticResource RgbConverter}">
<MultiBinding.Bindings>
<Binding Path="Value" Source="{x:Reference RValue}" />
<Binding Path="Value" Source="{x:Reference GValue}" />
<Binding Path="Value" Source="{x:Reference BValue}" />
</MultiBinding.Bindings>
</MultiBinding>
</ContentPage.Background>
Здесь, для свойства страницы Background мы используем множественную привязку, указав в параметре Converter у MultiBinding наш конвертер. Далее мы создаем список привязок:
<MultiBinding.Bindings>
<Binding Path="Value" Source="{x:Reference RValue}" />
<Binding Path="Value" Source="{x:Reference GValue}" />
<Binding Path="Value" Source="{x:Reference BValue}" />
</MultiBinding.Bindings>
к значениям Value у элементов Slider. Таким образом, мы решаем задачу по передаче нескольких значений свойств в одно целевое свойство. Проверим работу нашего приложения:
Фон страницы приложения меняется по мере изменения того или иного значения в слайдерах.
Итого
Конвертеры значений используются для преобразования одного типа значения свойства в другой тип. При этом, конвертеры могут преобразовывать значения как «один-к-одному», то есть одно значение в другое значение, так и «многие-к-одному» — когда нам необходимо несколько значений свойств преобразовать в одно. Второй способ работы конвертеров используется при множественной привязке — когда нам необходимо одному свойству привязать несколько значений свойств из источника (или из нескольких источников).