Recently I read about an IValueConverter
which also inherits from MarkupExtension
. It was something like:
internal class BoolToVisibilityConverter : MarkupExtension, IValueConverter
{
private static BoolToVisibilityConverter converter;
public BoolToVisibilityConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
if ((bool)value)
{
return Visibility.Visible;
}
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility)
{
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Collapsed)
{
return false;
}
}
return true;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return converter ?? (converter = new BoolToVisibilityConverter());
}
}
The usage than looks like:
<Button Content="Delete" Visibility="{Binding CanDelete, UpdateSourceTrigger=PropertyChanged, Converter={local:BoolToVisibilityConverter}"/>
I was used to use converters from a Resource like:
<loc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
...
<Button Content="Delete" Visibility="{Binding CanDelete, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BoolToVisibilityConverter}"/>
My first question now is: What is the better way? What advantages does it have if I'm using the MarkupExtension-Version
(Beside the usage is easier to type)?
I also saw a very similar implementation which looks like:
internal class BoolToVisibilityConverter : MarkupExtension, IValueConverter
{
public BoolToVisibilityConverter()
{
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
if ((bool)value)
{
return Visibility.Visible;
}
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility)
{
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Collapsed)
{
return false;
}
}
return true;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
If I understand it right, the first solution only creates one instance of this converter. The second one creates for every XAML a new instance of this converter, right?
One massive advantage of using MarkupExtension
which I have never seen being used online is the fact it can allow you pass values to the converter which could be used as argument or return values, for example:
public class CustomNullToVisibilityConverter : MarkupExtension, IValueConverter
{
public object NullValue { get; set; }
public object NotNullValue { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return NullValue;
return NotNullValue;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Usage:
...
Visibility="{Binding Property,
Converter={cnv:CustomNullToVisibilityConverter
NotNullValue=Visible, NullValue=Collapsed}}" />
...
Be sure to reference the namespace of the converter in the .xaml
.
Edit:
One thing I forgot to mention is that yes you are correct in the fact that this method would create a new instance of the converter each time it's used which is one downside.
However there nothing to stop you adding a converter with MarkupExtension
to a resource dictionary - that way it will only be instanced once. Like so:
<cnv:CustomNullToVisibilityConverter x:Key="NullToVisibilityConverter"
NotNullValue=Visible, NullValue=Collapsed />
...
Visibility="{Binding Property, Converter={StaticResource NullToVisibilityConverter}" />
...
The only (slight) advantage that the markup extension is providing in this case is more concise XAML syntax.
Instead of this:
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
...
{Binding SomeBooleanProperty, Converter={StaticResource BooleanToVisibilityConverter}}
you can have this:
{Binding SomeBooleanProperty, Converter={my:BoolToVisibilityConverter}}
In my opinion it's not really worth it. If you were that bothered about saving keystrokes you could just shorten the key used to reference the converter:
<BooleanToVisibilityConverter x:Key="btvc" />
...
{Binding SomeBooleanProperty, Converter={StaticResource my:btvc}}
As the ProvideValue
method of the markup extension is an instance method, it can only be called once an instance of the class has been created. As the class is both a markup extension and a converter, both variants of the code will create a converter each time. The only difference is that the first variant will always return the same converter: it won't however, stop another converter from being created.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With