15

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

4

2 回答 2

39

从每一个派生给你不同的力量和灵活性:

  • 派生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.

请注意,目标属性必须是DependencyPropertyforBinding才能工作。MSDN 说,

  • 每个绑定通常具有以下四个组件:绑定目标对象、目标属性、绑定源和绑定源中要使用的值的路径。例如,如果要将 TextBox 的内容绑定到 Employee 对象的 Name 属性,则目标对象是 TextBox,目标属性是 Text 属性,要使用的值是 Name,源对象是员工对象。

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

于 2011-09-16T13:25:14.113 回答
5

由于您引用的是我的库DependencyObject作为扩展转换器的示例,因此我认为自己解释一下是合适的。

我实际上是通过简单地实现IValueConverterwithObject作为我的基类开始的。我转而使用扩展的唯一原因DependencyObject是允许使用由 Josh Smith 首创的称为虚拟分支的技术。您可以在此处阅读有关该技术的信息。

假设你想做这样的事情:

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

这不起作用,因为资源不是可视树的一部分,因此绑定将失败。虚拟分支破解了这个小困境,使您能够执行这样的绑定。但是,它仍然依赖于 - 与任何其他 WPF 绑定一样 - 目标是DependencyObject. 因此,如果我只是简单地实现IValueConverter而不扩展DependencyObject,您将无法使用虚拟分支。

现在,如果我完全诚实,我不确定如果我有时间我还会这样做。我自己从来没有真正使用虚拟分支——我只是想启用这个场景。我什至可以在我的库的未来版本中更改它。所以我的建议是坚持一个基类Object(或其简单派生类),除非你真的认为你需要虚拟分支。

于 2011-10-05T08:18:17.190 回答