改进的 IValueConverter —— MarkupExtension 还是 DependencyObject?

发布于 2024-12-04 23:30:46 字数 136 浏览 7 评论 0原文

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

I saw online 2 different approaches to enhancing an IValueConverter. One of them extended a ValueConverter from MarkupExtension, the other from DependencyObject. I can't extend from both, so I'm wondering if any one is better than the other?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

神回复 2024-12-11 23:30:46

从每个派生都可以为您提供不同类型的功能和灵活性:

  • MarkupExtension 派生使您能够使用值转换器,而无需将其设为静态资源,如下所述:

    公共类 DoubleMe :MarkupExtension、IValueConverter
    {
       公共重写对象 ProvideValue(IServiceProvider serviceProvider)
       {
          返回这个;
       }
       public object Convert(对象值, /*其余参数*/ )
       {
          if(值为 int )
             返回(int)(值)* 2; //加倍
          别的
             返回 value.ToString() + value.ToString();
       }
      //...
    }
    

    在XAML中,无需创建StaticResource就可以直接使用:

    
    
    

    此类代码在调试时非常方便,因为您只需编写 local:DebugMe 即可调试使用它的控件的 DataContext。

  • DependencyObject 派生,您可以以更具表现力的方式使用某些首选项配置值转换器,如下所述:

    公共类 TruncateMe :DependencyObject、IValueConverter
    {
         公共静态只读 DependencyProperty MaxLengthProperty =
             DependencyProperty.Register("最大长度",
                                          类型(int),
                                          类型(截断我),
                                          新的PropertyMetadata(100));
         公共 int 最大长度
         {
             获取 { return (int) this.GetValue(MaxLengthProperty); }
             设置 { this.SetValue(MaxLengthProperty, value); }
         }
    
         public object Convert(对象值, /*其余参数*/ )
         {
            字符串 s = value.ToString();
            if ( s.Length > MaxLength)
              return s.Substring(0, MaxLength) + "...";
          别的
              返回 s;
         }
         //...
    }
    

    在XAML中,您可以直接将其用作:

    ;
       
           <绑定路径=“完整描述”>
               <绑定.转换器>
                 <本地:TruncateMe MaxLength =“50”/>
               
           
        
    

    它有什么作用?如果字符串 FullDescription 超过 50 个字符,它会截断它!

@crazyarabian 评论说:

您的声明“从 DependencyObject 派生使您能够以更具表现力的方式使用某些首选项配置值转换器”并不是 DependencyObject 独有的,因为您可以在 MarkupExtension 上创建相同的 MaxLength 属性,从而导致 。我认为 MarkupExtension 更具表现力且更简洁。

确实如此。但那是不可绑定的;也就是说,当您从 MarkupExtension 派生时,则不能执行以下操作:

MaxLength="{Binding TextLength}"

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

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

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

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

Deriving from each gives you different kind of power and flexibility:

  • Deriving from MarkupExtension enables you to use the value converter without making it a static resource, as described below:

    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();
       }
      //...
    }
    

    In XAML, you can directly use it without creating a StaticResource:

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

    Such code is very handy when debugging, as you can just write local:DebugMe and then can debug the DataContext of the control on which you use it.

  • Deriving from DependencyObject enables you to configure the value converter with some preferences in a more expressive way, as described below:

    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;
         }
         //...
    }
    

    In XAML, you can directly use it as:

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

    What does it do? It truncates the string FullDescription if it is more than 50 characters!

@crazyarabian commented that:

Your statement "Deriving from DependencyObject enables you to configure the value converter with some preferences in a more expressive way" isn't exclusive to DependencyObject as you can create the same MaxLength property on a MarkupExtension resulting in <TextBlock Text="Binding Age, Converter={local:DoubleMe, MaxLength=50}}"/>. I would argue that a MarkupExtension is more expressive and less verbose.

That is true. But then that is not bindable; that is, when you derive from MarkupExtension, then you cannot do :

MaxLength="{Binding TextLength}"

But if you derive your converter from DependencyObject, then you can do the above. In that sense, it is more expressive compared to MarkupExtension.

Note that the target property must be a DependencyProperty for Binding to work. MSDN says,

  • Each binding typically has these four components: a binding target
    object, a target property, a binding
    source, and a Path to the value in the
    binding source to use. For example, if
    you want to bind the content of a
    TextBox to the Name property of an
    Employee object, your target object is
    the TextBox, the target property is
    the Text property
    , the value to use is
    Name, and the source object is the
    Employee object.

  • The target property must be a dependency property.

花桑 2024-12-11 23:30:46

由于它是我的库,您引用它作为扩展DependencyObject的转换器的示例,我我觉得自己解释一下比较合适。

实际上,我首先简单地以 Object 作为基类实现 IValueConverter 。我转而扩展 DependencyObject 的唯一原因是允许采用由 Josh Smith 首创的称为虚拟分支的技术。您可以在此处了解该技术。

假设您想做这样的事情:

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

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

现在,如果我完全诚实的话,我不确定如果有机会的话我还会这样做。我自己实际上从未不得不使用虚拟分支 - 我只是想启用该场景。我什至可能会在我的库的未来版本中更改这一点。因此,我的建议是坚持使用 Object 的基类(或其简单派生类),除非您确实认为需要虚拟分支。

Since it's my library you're citing as an example of converters that extend DependencyObject, I think it fitting to explain myself.

I actually started out by simply implementing IValueConverter with Object as my base class. The only reason I switched to extending DependencyObject was to allow for a technique - pioneered by Josh Smith - called virtual branching. You can read about that technique here.

Suppose you want to do something like this:

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

This won't work because resources are not part of the visual tree, and so the binding will fail. Virtual branching hacks around this little dilemma, enabling you to perform such a binding. However, it still relies - like any other WPF binding - on the target being a DependencyObject. Thus, if I simply implemented IValueConverter without extending DependencyObject, you would be precluded from using virtual branches.

Now, if I'm completely honest, I'm not sure I'd still do this if I had my time again. I've never actually had to use a virtual branch myself - I just wanted to enable the scenario. I may even change this in a future version of my library. So my advice would be stick to a base class of Object (or a simple derivative thereof) unless you really think you'll need virtual branching.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文