改进的IValueConverter--MarkupExtension还是DependencyObject?

15
我在网上看到了两种增强IValueConverter的方法,其中一种是从MarkupExtension扩展ValueConverter,另一种是从DependencyObject扩展。我无法同时从两个方面进行扩展,所以想知道哪种更好一些?

我猜这取决于你想要实现什么。你能补充一些细节吗? - Fredrik Hedblad
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相比,它更加表达力强。

    请注意,目标属性必须是DependencyProperty才能使Binding起作用。MSDN说:

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

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


    我在Codeplex上看到了Kent Boogaart的Converters项目,它扩展了他从DependencyObject编写的所有IValueConverter。http://wpfconverters.codeplex.com/SourceControl/changeset/view/61942# - michael
    @crazyarabian:请阅读更新后的答案,其中包含更多解释。 - Nawaz
    2
    实际上,我扩展DependencyObject的原因是它允许您使用虚拟分支,以便您可以绑定转换器的属性。所述示例无法工作,因为转换器不是可视树的一部分,无论是定义为资源还是内联。在此处阅读有关虚拟分支的信息:http://www.codeproject.com/KB/WPF/AttachingVirtualBranches.aspx - Kent Boogaart
    @KentBoogaart:愿意回答这个问题吗?我喜欢抓住任何WPF大师们投入的东西... - myermian
    @KentBoogaart @Nawaz,我正在尝试在Silverlight中实现“DependencyObject”的方式,但并不成功,我有什么遗漏吗? - Shimmy Weitzhandler
    显示剩余3条评论

    5

    既然你引用我的库作为扩展DependencyObject的转换器示例,我认为有必要解释一下。

    我最初只是使用Object作为基类来实现IValueConverter。我改为扩展DependencyObject的唯一原因是为了使用Josh Smith开创的一种名为虚拟分支的技术。你可以在这里阅读相关内容。

    假设你想做这样的事情:

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

    这样做是行不通的,因为资源不是可视树的一部分,所以绑定会失败。虚拟分支可以解决这个问题,使您能够执行此类绑定。然而,它仍然依赖于目标是一个DependencyObject,就像任何其他WPF绑定一样。因此,如果我只实现了IValueConverter而没有扩展DependencyObject,您将无法使用虚拟分支。

    现在,坦白地说,如果我再来一次,我不确定我还会这样做。我从未真正需要使用虚拟分支 - 我只是想启用这种情况。我甚至可能在我的库的未来版本中更改这一点。因此,我的建议是除非您真的认为需要虚拟分支,否则坚持使用Object(或其简单派生类)作为基类。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接