Конверторы являются одной из важнейшей особенностью механизма привязки в WPF. Они позволяют управлять тем, как источник привязки будет представлен в UI. В данной статье я покажу, как немного упростить использование конвертеров в XAML коде.
Рассмотрим простейший пример:
Тут всё просто: конвертер на вход получает значение типа DateTime и конвертирует его в строку. Обратная конвертация не предусмотрена.
Используется конвертер следующим образом:
Здесь тоже нет ничего сложного, однако, минус такого подхода – для каждого конвертера нужно создавать соответсвующий ресурс. Причём нужно либо делать это в глобальном словаре ресурсов, либо нужно в каждом XAML файле создавать свои ресурсы для всех используемых конвертеров, что сильно напрягает, когда таких конвертеров много. После некоторых поисков на просторах интернета, здесь я нашёл альтернативное решение.
Вначале модифицируем сам конвертер:
После такой модификации всё, что нужно для его использования в XAML, это:
Естественно. нужно не забыть добавить соотвествуещее пространство имён «converters».
Приятным бонусом будет то, что при наборе отображается список доступных конвертеров:

Давайте не будем останавливаться на достигнутом, а для того, чтобы максимально упростить написание новых конвертеров, введём базовый класс:
Теперь унаследуем от него наш DateConverter и имплементируем в нём метод Convert. Окончательная версия будет выглядеть так:
XAML код остаётся идентичен второму примеру.
Таким образом, мы получили возможность использовать упрощённый синтаксис в XAML разметке, а код конвертера практически не изменился.
P.S. Проекты с примерами можно скачать здесь.
Рассмотрим простейший пример:
public class DateConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date = (DateTime)value; return date.ToShortDateString(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } }
Тут всё просто: конвертер на вход получает значение типа DateTime и конвертирует его в строку. Обратная конвертация не предусмотрена.
Используется конвертер следующим образом:
<Window x:Class="TestConvertorMarkup.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converters="clr-namespace:TestConvertorMarkup.Converters" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <converters:DateConverter x:Shared="false" x:Key="dateConverter"/> </Window.Resources> <Label Content="{Binding Path=Date, Converter={StaticResource dateConverter}}" /> </Window>
Здесь тоже нет ничего сложного, однако, минус такого подхода – для каждого конвертера нужно создавать соответсвующий ресурс. Причём нужно либо делать это в глобальном словаре ресурсов, либо нужно в каждом XAML файле создавать свои ресурсы для всех используемых конвертеров, что сильно напрягает, когда таких конвертеров много. После некоторых поисков на просторах интернета, здесь я нашёл альтернативное решение.
Вначале модифицируем сам конвертер:
public class NumberToStringConverterExtension: MarkupExtension, IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date = (DateTime)value; return date.ToShortDateString(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { if (_converter == null) _converter = new NumberToStringConverterExtension(); return _converter; } private static NumberToStringConverterExtension _converter = null; }
После такой модификации всё, что нужно для его использования в XAML, это:
<Label Content="{Binding Path=Date, Converter={converters:DateTimeToString}}" />
Естественно. нужно не забыть добавить соотвествуещее пространство имён «converters».
Приятным бонусом будет то, что при наборе отображается список доступных конвертеров:

Давайте не будем останавливаться на достигнутом, а для того, чтобы максимально упростить написание новых конвертеров, введём базовый класс:
public abstract class ConvertorBase<T> : MarkupExtension, IValueConverter where T : class, new() { /// <summary> /// Must be implemented in inheritor. /// </summary> public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); /// <summary> /// Override if needed. /// </summary> public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } #region MarkupExtension members public override object ProvideValue(IServiceProvider serviceProvider) { if (_converter == null) _converter = new T(); return _converter; } private static T _converter = null; #endregion }
Теперь унаследуем от него наш DateConverter и имплементируем в нём метод Convert. Окончательная версия будет выглядеть так:
public class DateTimeToString : ConvertorBase<DateTimeToString> { public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date = (DateTime)value; return date.ToShortDateString(); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } }
XAML код остаётся идентичен второму примеру.
Таким образом, мы получили возможность использовать упрощённый синтаксис в XAML разметке, а код конвертера практически не изменился.
P.S. Проекты с примерами можно скачать здесь.
