改进了IValueConverter - MarkupExtension还是DependencyObject?

时间:2011-09-16 13:05:59

标签: c# wpf ivalueconverter markup-extensions dependencyobject

我在网上看到了两种不同的增强IValueConverter的方法。其中一个从MarkupExtension扩展了ValueConverter,另一个从DependencyObject扩展。我无法从两者延伸,所以我想知道是否有一个比另一个好?

2 个答案:

答案 0 :(得分:35)

从每种方法中获得不同的力量和灵活性:

  • 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所独有的,因为您可以在MarkupExtension上创建相同的MaxLength属性,从而生成<TextBlock Text="Binding Age, Converter={local:DoubleMe, MaxLength=50}}"/>。我认为MarkupExtension更具表现力,更简洁。

这是事实。但那是不可约束的;也就是说,当你从MarkupExtension派生时,你就做不到:

MaxLength="{Binding TextLength}"

但是,如果您从DependencyObject派生转换器,那么您可以执行上述操作。从这个意义上说,与MarkupExtension相比,更具表现力

请注意,目标属性必须为DependencyProperty Binding才能生效。 MSDN says,

  
      
  • 每个绑定通常都包含以下四个组件:绑定目标   对象,目标属性,绑定   source,以及该值中的路径   绑定源使用。例如,如果   你想绑定一个的内容   TextBox到Name的Name属性   Employee对象,你的目标对象是   TextBox,目标属性是   Text属性,要使用的值是   名称和源对象是   员工对象。

  •   
  • 目标属性必须是依赖属性。

  •   

答案 1 :(得分:4)

由于它是my library你引用的扩展DependencyObject的转换器的例子,我认为这适合解释自己。

我实际上是通过简单地将IValueConverterObject作为我的基类来实现的。我转而延伸DependencyObject的唯一原因是允许一种由Josh Smith开创的技术 - 称为虚拟分支。您可以阅读有关该技术的here

假设你想做这样的事情:

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

这不起作用,因为资源不是可视化树的一部分,因此绑定将失败。虚拟分支破解了这个小难题,使您能够执行这样的绑定。但是,它仍然依赖于 - 就像任何其他WPF绑定一样 - 在目标上是DependencyObject。因此,如果我只是在不扩展IValueConverter的情况下实施DependencyObject,则会阻止您使用虚拟分支。

现在,如果我完全诚实,我不确定如果我再次有时间,我仍然会这样做。我自己从来没有 自己使用虚拟分支 - 我只想启用该方案。我甚至可以在我的库的未来版本中更改它。所以我的建议是坚持Object(或其简单衍生物)的基类,除非你真的认为你需要虚拟分支。