IValueConverter with MarkupExtension

孤街浪徒 提交于 2019-12-08 22:00:36

问题


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?


回答1:


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.




回答2:


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


来源:https://stackoverflow.com/questions/28501091/ivalueconverter-with-markupextension

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!