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?
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 than50
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.
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. –
Fever MaxLength="{Binding TextLength}"
. –
Collyrium DependencyObject
enables bindable properties (but I got sidetracked). While I like like and agree with the updates you've made, your code examples and initial answer still imply that DependencyObject
allows for configuration with properties while MarkupExtension
does not. –
Fever DependencyObject
way in Silverlight without great success, am I missing anything? –
Polypary 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.
© 2022 - 2024 — McMap. All rights reserved.