Содержание
Не всегда бывает удобным (а иногда и возможным) отображать в элементе управления или передавать в свойство целевого объекта текущее значение свойства источника привязки. Например, нам может потребоваться каким-либо образом форматировать строку со значением из источника прежде, чем вывести её в тексте метки. Или привязываемые свойства вообще не совпадают по типу — одно свойство имеет тип Double, а второе, скажем Color и нам необходимо «вытащить» из Color, скажем, только значение яркости, только значение красного и так далее. В этом случае мы можем использовать при привязке данных форматирование строк или написать свой конвертер значений и применить его в привязке.
Форматирование строк в привязке
Форматирование строк в привязке WPF производится с использованием обычного метода string.Format(), а для использования этого метода у расширения разметки Binding имеется параметр StringFormat. Этот параметр стоит использовать в том случае, если целевое свойство является строкой. Например, создадим новое приложение WPF и напишем следующий код MainWindow.xaml:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
xmlns:components="clr-namespace:WpfApp1.Components"
Title="MainWindow" Height="450" Width="435">
<StackPanel>
<TextBlock Text="{Binding Source={x:Reference RValue},Path=Value, StringFormat='Красный {0:F0}'}"/>
<Slider x:Name="RValue" Maximum="255" Minimum="0"/>
<TextBlock Text="{Binding Source={x:Reference GValue}, Path=Value, StringFormat='Зеленый {0:F0}'}"/>
<Slider x:Name="GValue" Maximum="255" Minimum="0"/>
<TextBlock Text="{Binding Source={x:Reference BValue}, Path=Value, StringFormat='Синий {0:F0}'}"/>
<Slider x:Name="BValue" Maximum="255" Minimum="0"/>
</StackPanel>
</Window>
Здесь метки TextBlock привязываются к свойствам Value трех элементов Slider. При этом, для Binding задано форматирование строк, например:
StringFormat='Зеленый {0:F0}'}"
Таким образом, при выводе текста, строка будет отформатирована и на экране мы увидим, например, вот такой результат:

Теперь попробуем ответить на вопрос: каким образом мы можем использовать значения из трех Slider, чтобы сформировать из них цвет Color и передать это значение, например, в качестве фона страницы? Для выполнения такой задачи нам пригодятся конвертеры значений.
Конвертеры значений
Привязка свойств в WPF производится, если оба свойства совпадают по типу. При этом, для примитивных типов инфраструктура WPF может сама конвертировать значение свойства из одного типа в другой. Например, выше мы без лишних проблем привязываем свойство Text у метки к свойству Value у Slider, несмотря на то, что целевое свойство имеет тип string, а исходное — double. Однако, возможностей инфраструктуры WPF не всегда достаточно и может потребоваться написать свой собственный конвертер значений.
- Конвертеры, которые преобразуют одно значение. Классы таких конвертеров должны реализовывать интерфейс
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. Изменим код окна следующим образом:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
xmlns:converters="clr-namespace:WpfApp1"
Title="MainWindow" Height="450" Width="435">
<Window.Resources>
<converters:DoubleToHexConverter x:Key="MyConverter"/>
</Window.Resources>
<StackPanel>
<TextBlock Text="{Binding Source={x:Reference RValue},Path=Value, Converter={StaticResource MyConverter}, StringFormat='Красный {0}'}"/>
<Slider x:Name="RValue" Maximum="255" Minimum="0"/>
<TextBlock Text="{Binding Source={x:Reference GValue}, Path=Value, Converter={StaticResource MyConverter},StringFormat='Зеленый {0}'}"/>
<Slider x:Name="GValue" Maximum="255" Minimum="0"/>
<TextBlock Text="{Binding Source={x:Reference BValue}, Path=Value, Converter={StaticResource MyConverter}, StringFormat='Синий {0}'}"/>
<Slider x:Name="BValue" Maximum="255" Minimum="0"/>
</StackPanel>
</Window>
Первое, что мы сделали — это добавили пространство имен в котором содержится конвертер:
xmlns:local="clr-namespace:WpfApp1"
Далее, мы зарегистрировали наш конвертер, как ресурс:
<Window.Resources>
<converters:DoubleToHexConverter x:Key="MyConverter"/>
</Window.Resources>
и, наконец, использовали этот ресурс в расширении
<TextBlock Text="{Binding Source={x:Reference RValue},Path=Value, Converter={StaticResource MyConverter}, StringFormat='Красный {0}'}"/>
StringFormat и параметр Converter, то стоит отметить, что вначале сработает конвертер и только потом строка будет отформатирована. То есть приложение будет выглядеть как показано ниже:
Теперь вернемся к нашему основному вопросу: как преобразовать сразу три значения в одно — Color? Здесь нам пригодится второй тип конвертера — IMultiValueConverter.
Интерфейс IMultiValueConverter и MultiBinding в WPF
Иногда нам необходимо привязать сразу несколько значений свойств к одному целевому. Например, как в нашем случае — мы должны каким-то образом обработать три значения Value, чтобы получить одно — Color. В WPF и 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)
{
return [];
}
}
Единственное отличие этого конвертера от предыдущего заключается в том, что в качестве первого параметра в методе Convert() мы принимаем не одно, а массив значений, а в методе ConvertBack() мы должны, наоборот — вернуть массив значений из одного полученного. В нашем случае, нам достаточно реализовать для работы только метод Convert(), что мы и сделали. Теперь вернемся к нашему приложению и воспользуемся конвертером:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
xmlns:converters="clr-namespace:WpfApp1"
Title="MainWindow" Height="450" Width="435">
<Window.Resources>
<ResourceDictionary>
<converters:DoubleToHexConverter x:Key="MyConverter"/>
<converters:RgbConverter x:Key="RgbConverter"/>
</ResourceDictionary>
</Window.Resources>
<Window.Background>
<SolidColorBrush>
<SolidColorBrush.Color>
<MultiBinding Converter="{StaticResource RgbConverter}">
<MultiBinding.Bindings>
<Binding ElementName="RValue" Path="Value" />
<Binding ElementName="GValue" Path="Value"/>
<Binding ElementName="BValue" Path="Value"/>
</MultiBinding.Bindings>
</MultiBinding>
</SolidColorBrush.Color>
</SolidColorBrush>
</Window.Background>
<StackPanel>
<TextBlock Text="{Binding Source={x:Reference RValue},Path=Value, Converter={StaticResource MyConverter}, StringFormat='Красный {0}'}"/>
<Slider x:Name="RValue" Maximum="255" Minimum="0"/>
<TextBlock Text="{Binding Source={x:Reference GValue}, Path=Value, Converter={StaticResource MyConverter},StringFormat='Зеленый {0}'}"/>
<Slider x:Name="GValue" Maximum="255" Minimum="0"/>
<TextBlock Text="{Binding Source={x:Reference BValue}, Path=Value, Converter={StaticResource MyConverter}, StringFormat='Синий {0}'}"/>
<Slider x:Name="BValue" Maximum="255" Minimum="0"/>
</StackPanel>
</Window>
Мы также зарегистрировали конвертер, как ресурс и обратите внимание, как он был использован в коде:
<Window.Background>
<SolidColorBrush>
<SolidColorBrush.Color>
<MultiBinding Converter="{StaticResource RgbConverter}">
<MultiBinding.Bindings>
<Binding ElementName="RValue" Path="Value" />
<Binding ElementName="GValue" Path="Value"/>
<Binding ElementName="BValue" Path="Value"/>
</MultiBinding.Bindings>
</MultiBinding>
</SolidColorBrush.Color>
</SolidColorBrush>
</Window.Background>
Вначале мы создали мульти-привязку, определив MultiBinding, указав в качестве конвертера наш RgbConverter, а затем, добавили в коллекцию Bindings три привязки, как мы добавляли их ранее. Таким образом, мы решаем задачу по передаче нескольких значений свойств в одно целевое свойство. Проверим работу нашего приложения:
Фон окна приложения меняется по мере изменения того или иного значения в слайдерах.
Итого
Конвертеры значений используются для преобразования одного типа значения свойства в другой тип. При этом, конвертеры могут преобразовывать значения как «один-к-одному», то есть одно значение в другое значение, так и «многие-к-одному» — когда нам необходимо несколько значений свойств преобразовать в одно. Второй способ работы конвертеров использует объект MultiBinding.

