Разберём интересный и нестандартный сценарий использования конвертеров — Inline Converter.
Наверно, некоторые разработчики сталкивались с той проблемой, что при использовании конвертеров в параметры конвертера не передаётся информации о представлении, его контексте данных либо самом визуальном элементе, к которому осуществлена привязка. С одной стороны это хорошо, получается некоторая защита и разделение логики, не слишком правильно в конвертере напрямую работать с контролом, с другой же стороны в редких случаях именно из-за такого ограничения приходится идти на различные ухищрения.
Старая добрая событийная модель по-прежнему не утратила своей актуальности даже несмотря на то, что получил развитие мощный и эффективный механизм привязки данных (Data Binding). Конечно, не стоит использовать события в ущерб прогрессивным средствам разработки, но иногда их применение получается удобным и естественным.
Почему бы не скомбинировать оба этих способа? К примеру, таким образом
using System.Windows.Data;
namespace Aero.Converters.Patterns
{
public interface ICompositeConverter : IValueConverter
{
IValueConverter PostConverter { get; set; }
object PostConverterParameter { get; set; }
}
}
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;
//}
}
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
Автор: Makeman