Фишки XAML-разработчика: композитные конвертеры

Статья будет посвящена простому, но эффективному паттерну — Composite Converter [составной конвертер].
image
Встречаются ситуации, когда уже есть несколько конвертеров, но возникает необходимость в создании нового, который является логической композицией имеющихся. Чтобы не создавать классов, которые отчасти дублируют функционал, можно поступить предложенным ниже образом. Обратите внимание на свойства PostConverter и PostConverterParameter.

using System.Windows.Data;  namespace Aero.Converters.Patterns {     public interface ICompositeConverter : IValueConverter     {         IValueConverter PostConverter { get; set; }         object PostConverterParameter { get; set; }     } } 

Inline Converter

using System; using System.Globalization; using System.Windows.Data; using Aero.Converters.Patterns;  namespace Aero.Converters {     public class InlineConverter : IInlineConverter, ICompositeConverter     {         public IValueConverter PostConverter { get; set; }         public object PostConverterParameter { get; set; }         public event EventHandler<ConverterEventArgs> Converting;         public event EventHandler<ConverterEventArgs> ConvertingBack;          public object Convert(object value, Type targetType, object parameter, CultureInfo culture)         {             var args = new ConverterEventArgs(value, targetType, parameter, culture);             var handler = Converting;             if (handler != null) handler(this, args);             return PostConverter == null                 ? args.ConvertedValue                 : PostConverter.Convert(args.ConvertedValue, targetType, PostConverterParameter, culture);         }          public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)         {             var args = new ConverterEventArgs(value, targetType, parameter, culture);             var handler = ConvertingBack;             if (handler != null) handler(this, args);             return PostConverter == null                 ? args.ConvertedValue                 : PostConverter.ConvertBack(args.ConvertedValue, targetType, PostConverterParameter, culture);         }     } } 

Switch Converter

using System; using System.Globalization; using System.Linq; using System.Windows; using System.Windows.Data; using System.Windows.Markup; using Aero.Converters.Patterns;  namespace Aero.Converters {     [ContentProperty("Cases")]     public class SwitchConverter : DependencyObject, ISwitchConverter, ICompositeConverter     {         public static readonly DependencyProperty DefaultProperty = DependencyProperty.Register(             "Default", typeof(object), typeof(SwitchConverter), new PropertyMetadata(CaseSet.UndefinedObject));          public SwitchConverter()         {             Cases = new CaseSet();         }          public object Default         {             get { return GetValue(DefaultProperty); }             set { SetValue(DefaultProperty, value); }         }          public CaseSet Cases { get; private set; }          public bool TypeMode { get; set; }          public object Convert(object value, Type targetType, object parameter, CultureInfo culture)         {             if (TypeMode) value = value == null ? null : value.GetType();             var pair = Cases.FirstOrDefault(p => Equals(p.Key, value) || SafeCompareAsStrings(p.Key, value));             var result = pair == null ? Default : pair.Value;             value = result == CaseSet.UndefinedObject ? value : result;             return PostConverter == null                 ? value                 : PostConverter.Convert(value, targetType, PostConverterParameter, culture);         }          public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)         {             if (TypeMode) value = value == null ? null : value.GetType();             var pair = Cases.FirstOrDefault(p => Equals(p.Value, value) || SafeCompareAsStrings(p.Value, value));             value = pair == null ? Default : pair.Key;             return PostConverter == null                 ? value                 : PostConverter.ConvertBack(value, targetType, PostConverterParameter, culture);         }          private static bool SafeCompareAsStrings(object a, object b)         {             if (a == null && b == null) return true;             if (a == null || b == null) return false;             return string.Compare(a.ToString(), b.ToString(), StringComparison.OrdinalIgnoreCase) == 0;         }          public IValueConverter PostConverter { get; set; }         public object PostConverterParameter { get; set; }     } } 

Это позволит объединять конвертеры в логические цепочки различной длины и запросто строить новые на базе существующих.

<Grid.Resources>     <BooleanConverter x:Key="YesNoConverter" OnTrue="Yes" OnFalse="No"/>          <SwitchConverter x:Key="CompositeSwitchConverter" PostConverter="{StaticResource YesNoConverter}">         <Case Key="0" Value="False"/>         <Case Key="1" Value="True"/>     </SwitchConverter> </Grid.Resources>  <TextBlock Text="{Binding Number, Converter={StaticResource CompositeSwitchConverter}}"/>  Number == 1 => out: Yes Number == 0 => out: No 

Гениальное просто! Пользуйтесь на здоровье!
Живые примеры доступны с библиотекой Aero Framework.

Спасибо!

P.S. Предыдущая статья о встраиваемых конвертерах

ссылка на оригинал статьи https://habrahabr.ru/post/276273/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *