【问题标题】:Improved IValueConverter -- MarkupExtension or DependencyObject?改进的 IValueConverter -- MarkupExtension 或 DependencyObject?
【发布时间】:2011-11-18 16:41:55
【问题描述】:

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

【问题讨论】:

  • 我想这取决于您要达到的目标。你能补充一些细节吗?

标签: c# wpf ivalueconverter markup-extensions dependencyobject


【解决方案1】:

从每一种派生而来,为您提供不同类型的功能和灵活性:

  • 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 属性,从而产生&lt;TextBlock Text="Binding Age, Converter={local:DoubleMe, MaxLength=50}}"/&gt;。我认为 MarkupExtension 更具表现力且不那么冗长。

确实如此。但是那是不可绑定的;也就是说,当你从MarkupExtension 派生时,你不能这样做:

MaxLength="{Binding TextLength}"

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

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

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

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

【讨论】:

  • 我看到 Kent Boogaart 在 Codeplex 上的 Converters 项目扩展了他从 DependencyObject 编写的所有 IValueConverter。 wpfconverters.codeplex.com/SourceControl/changeset/view/61942#
  • @Nawaz 您的声明“源自DependencyObject 使您能够以更具表现力的方式配置具有某些首选项的值转换器”并不是DependencyObject 独有的,因为您可以创建相同的@987654342 MarkupExtension 上的 @ 属性导致 &lt;TextBlock Text="Binding Age, Converter={local:DoubleMe, MaxLength=50}}"/&gt;。我认为MarkupExtension 更具表现力且不那么冗长。
  • @crazyarabian:是的。你可以这样做,但是那是不可绑定的。你不能这样做MaxLength="{Binding TextLength}"
  • @crazyarabian:阅读更新后的答案并提供更多解释。
  • 其实我扩展 DependencyObject 的原因是因为它允许你使用一个虚拟分支,以便你可以绑定你的转换器的属性。所述示例不起作用,因为转换器不是可视化树的一部分,无论是定义为资源还是内联。在此处阅读有关虚拟分支的信息:codeproject.com/KB/WPF/AttachingVirtualBranches.aspx
【解决方案2】:

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

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

假设你想做这样的事情:

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

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

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

【讨论】:

    猜你喜欢
    • 2015-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    • 2013-01-22
    • 2017-10-30
    相关资源
    最近更新 更多