Фишки 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

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


Комментарии

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

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