Фішки XAML-розробника: вбудовані конвертери

Розберемо цікавий і нестандартний сценарій використання конвертерів — Inline Converter.
image
Напевно, деякі розробники стикалися з тією проблемою, що при використанні конвертерів параметри конвертера не передається інформації про подання, його контексті даних або самому візуальному елементі, до якого здійснена прив'язка. З одного боку це добре, виходить деяка захист і поділ логіки, не дуже правильно в конвертері безпосередньо працювати з контролом, з іншого ж боку в рідкісних випадках саме із-за такого обмеження доводиться йти на різні хитрощі.

Стара добра подієва модель як і раніше не втратила своєї актуальності навіть незважаючи на те, що отримав розвиток потужний і ефективний механізм прив'язки даних (Data Binding). Звичайно, не варто використовувати події в збиток прогресивним засобів розробки, але іноді їх застосування виходить зручним і природним.

Чому б не поєднати обидва цих способу? Наприклад, таким чином

ICompositeConverter
using System.Windows.Data;

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


IInlineConverter
using System;
using System.Globalization;
using System.Windows.Data;

namespace Aero.Converters.Patterns
{
public class ConverterEventArgs : EventArgs
{
public object ConvertedValue { get; set; }
public object Value { get; private set; }
public Type TargetType { get; private set; }
public object Parameter { get; private set; }
public CultureInfo Culture { get; private set; }

public ConverterEventArgs(object value, Type targetType, object parameter, CultureInfo culture)
{
TargetType = targetType;
Parameter = parameter;
Culture = culture;
Value = value;
}
}

public interface IInlineConverter : IValueConverter
{
event EventHandler<ConverterEventArgs> Converting;
event EventHandler<ConverterEventArgs> ConvertingBack;
}

//public interface IInlineConverter : IValueConverter
//{
// event Func<object, Type, object, CultureInfo, object> Converting;
// event Func<object, Type, object, CultureInfo, object> ConvertingBack;
//}
}


InlineConverter
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);
}
}
}


Що ми отримуємо? Примірник конвертера нам потрібно вмонтувати в ресурси контрола або подання, а у бехаин коді (Code Behind) подання зробити потрібні обробники для подій Converting ConvertingBack, після чого ці події стануть викликатися під час спрацьовування прив'язки, а в обробниках через покажчик this буде доступно саме уявлення c візуальним деревом, так і контекст даних! Несподівано вийшла велика свобода дій, до того ж все залишилося ідеологічно правильно, адже сам конвертер не потрапило інтерфейсної логіки, а вона залишилася лише в бехаин коді.

Ось простий приклад використання цього підходу

<Grid>
<Grid.Resources>
<InlineConverter x:Key="InlineConverter" Converting="InlineConverter_OnConverting"/>
</Grid.Resources>
<TextBlock Text="{Binding Number, Converter={StaticResource InlineConverter}}"/>
</Grid>

private void InlineConverter_OnConverting(object sender, ConverterEventArgs e)
{
e.ConvertedValue =
string.Format("Title: {0} \nDataContext:\n{1} \nConverter Value: {2}",
Title,
DataContext,
e.Value);
}

Додаткове використання патерну Composite Converter [ICompositeConverter ] дозволяє об'єднувати різні конвертери в ланцюжки, модифікуючи логіку, без необхідності створення нових класів.

Побачити в дії Inline Converter і деякі інші можна в демонстраційному проекті HelloAero бібліотеки Aero Framework.

Дякую за увагу!

P. S. Попередня стаття про динамічному Grid

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

0 коментарів

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