Привязка в WPF. Форматирование строк и конвертеры значений

Не всегда бывает удобным (а иногда и возможным) отображать в элементе управления или передавать в свойство целевого объекта текущее значение свойства источника привязки. Например, нам может потребоваться каким-либо образом форматировать строку со значением из источника прежде, чем вывести её в тексте метки. Или привязываемые свойства вообще не совпадают по типу — одно свойство имеет тип 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}'}"

Таким образом, при выводе текста, строка будет отформатирована и на экране мы увидим, например, вот такой результат:

StringFormat
Теперь попробуем ответить на вопрос: каким образом мы можем использовать значения из трех Slider, чтобы сформировать из них цвет Color и передать это значение, например, в качестве фона страницы? Для выполнения такой задачи нам пригодятся конвертеры значений.

Конвертеры значений

Привязка свойств в WPF производится, если оба свойства совпадают по типу. При этом, для примитивных типов инфраструктура WPF может сама конвертировать значение свойства из одного типа в другой. Например, выше мы без лишних проблем привязываем свойство Text у метки к свойству Value у Slider, несмотря на то, что целевое свойство имеет тип string, а исходное — double. Однако, возможностей инфраструктуры WPF не всегда достаточно и может потребоваться написать свой собственный конвертер значений.

  1. Конвертеры, которые преобразуют одно значение. Классы таких конвертеров должны реализовывать интерфейс IValueConverter
  2. Конвертеры, которые преобразуют несколько значений. Классы таких конвертеров должны реализовывать интерфейс 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, то стоит отметить, что вначале сработает конвертер и только потом строка будет отформатирована. То есть приложение будет выглядеть как показано ниже:

Конвертеры значений IValueConverter

Теперь вернемся к нашему основному вопросу: как преобразовать сразу три значения в одно — 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 WPF

Фон окна приложения меняется по мере изменения того или иного значения в слайдерах.

Итого

Конвертеры значений используются для преобразования одного типа значения свойства в другой тип. При этом, конвертеры могут преобразовывать значения как «один-к-одному», то есть одно значение в другое значение, так и «многие-к-одному» — когда нам необходимо несколько значений свойств преобразовать в одно. Второй способ работы конвертеров использует объект MultiBinding.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии