Фішки 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="Так" OnFalse="Ні"/>

<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. Попередня стаття про вбудовуваних конвертерах

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.