Улучшенный IValueConverter - MarkupExtension или DependencyObject? - PullRequest
13 голосов
/ 16 сентября 2011

Я видел в сети 2 разных подхода к расширению IValueConverter. Один из них расширил ValueConverter из MarkupExtension, другой из DependencyObject. Я не могу отойти от обоих, поэтому мне интересно, один из них лучше другого?

Ответы [ 2 ]

36 голосов
/ 16 сентября 2011

Извлечение из каждого дает вам различные виды мощности и гибкости:

  • Извлечение из MarkupExtension позволяет использовать преобразователь значений, не делая его статическим ресурсом, как описано ниже:

    public class DoubleMe : MarkupExtension, IValueConverter
    {
       public override object ProvideValue(IServiceProvider serviceProvider)
       {
          return this;
       }
       public object Convert(object value, /*rest of parameters*/ )
       {
          if ( value is int )
             return (int)(value) * 2; //double it
          else
             return value.ToString() + value.ToString();
       }
      //...
    }
    

    В XAML вы можете напрямую использовать его, не создавая StaticResource:

    <TextBlock Text="{Binding Name, Converter={local:DoubleMe}}"/>
    <TextBlock Text="{Binding Age, Converter={local:DoubleMe}}"/>
    

    Такой код очень удобен при отладке, так как вы можете просто написать local:DebugMe, а затем можетеотладить DataContext элемента управления, на котором вы его используете.

  • Получение из DependencyObject позволяет вам настроить преобразователь значений с некоторыми предпочтениями вболее выразительный способ , как описано ниже:

    public class TruncateMe : DependencyObject, IValueConverter
    {
         public static readonly DependencyProperty MaxLengthProperty =
             DependencyProperty.Register("MaxLength",
                                          typeof(int),
                                          typeof(TruncateMe),
                                          new PropertyMetadata(100));
         public int MaxLength
         {
             get { return (int) this.GetValue(MaxLengthProperty); }
             set { this.SetValue(MaxLengthProperty, value); }
         }
    
         public object Convert(object value, /*rest of parameters*/ )
         {
            string s = value.ToString();
            if ( s.Length > MaxLength)
              return s.Substring(0, MaxLength) + "...";
          else
              return s;
         }
         //...
    }
    

    В XAML вы можете напрямую использовать его как:

    <TextBlock>
       <TextBlock.Text>
           <Binding Path="FullDescription">
               <Binding.Converter>
                 <local:TruncateMe MaxLength="50"/>
               </Binding.Converter>
           </Binding>
       </TextBlock.Text> 
    

    Что он делает?Он обрезает строку FullDescription, если она превышает 50 символов!

@ crazyarabian прокомментировал, что:

Ваше утверждение «Получение из DependencyObject» позволяетчтобы вы сконфигурировали конвертер значений с некоторыми предпочтениями более выразительным образом », это не исключение для DependencyObject, поскольку вы можете создать то же свойство MaxLength для MarkupExtension, что приведет к <TextBlock Text="Binding Age, Converter={local:DoubleMe, MaxLength=50}}"/>.Я бы сказал, что MarkupExtension является более выразительным и менее многословным.

Это правда.Но тогда это не обязательно;то есть, когда вы выводите из MarkupExtension, вы не можете сделать:

MaxLength="{Binding TextLength}"

Но если вы извлекаете конвертер из DependencyObject, то вы можете сделать выше.В этом смысле он на более выразительный по сравнению с MarkupExtension.

Обратите внимание, что целевое свойство должно быть DependencyProperty, чтобы Binding работал. MSDN говорит:

  • Каждая привязка обычно имеет следующие четыре компонента: целевой объект привязки, целевое свойство, источник привязки и путь кзначение в источнике привязки для использования.Например, если вы хотите связать содержимое TextBox со свойством Name объекта Employee, вашим целевым объектом является TextBox, целевым свойством является свойство Text , используемым значением будет Name,а исходным объектом является объект Employee.

  • Целевое свойство должно быть свойством зависимости.

4 голосов
/ 05 октября 2011

Поскольку это моя библиотека , которую вы приводите в качестве примера преобразователей, расширяющих DependencyObject, я думаю, что это уместно объяснить самому.

На самом деле я начал с реализации IValueConverter с Object в качестве базового класса. Единственная причина, по которой я переключился на расширение DependencyObject, состояла в том, чтобы учесть методику - впервые предложенную Джошем Смитом - под названием виртуальное ветвление . Вы можете прочитать об этой технике здесь .

Предположим, вы хотите сделать что-то вроде этого:

<UserControl.Resources>
    <con:CaseConverter Casing="{Binding SomeProperty}"/>
</UserControl.Resources>

Это не будет работать, потому что ресурсы не являются частью визуального дерева, и поэтому привязка не будет выполнена. Виртуальное ветвление решает эту небольшую дилемму, позволяя вам выполнить такое связывание. Тем не менее, он по-прежнему полагается - как и любая другая привязка WPF - на цель, являющуюся DependencyObject. Таким образом, если бы я просто внедрил IValueConverter без расширения DependencyObject, вы бы не смогли использовать виртуальные ветви.

Теперь, если я полностью честен, я не уверен, что я все еще сделал бы это, если бы у меня было время снова. На самом деле у меня никогда не было , чтобы использовать виртуальную ветку самостоятельно - я просто хотел включить сценарий. Я могу даже изменить это в будущей версии моей библиотеки. Поэтому мой совет - придерживаться базового класса Object (или его простого производного), если только вы действительно не думаете, что вам понадобится виртуальное ветвление.

...