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

вчера в 20:59

Статья будет посвящена простому, но эффективному паттерну — 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 Converting;
        public event EventHandler 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; }
    }
}



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

    
    
    
        
        
    




Number == 1 => out: Yes
Number == 0 => out: No


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

Спасибо!

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

54203ce1ecb01b293d8a0d9a80695724.jpg
@Makeman

карма 28,0

рейтинг 20,8

Пользователь

© Habrahabr.ru