带有 MarkupExtension 的 IValueConverter

IValueConverter with MarkupExtension

最近我读到一个 IValueConverter 也继承自 MarkupExtension。它是这样的:

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

用法比看起来像:

<Button Content="Delete" Visibility="{Binding CanDelete, UpdateSourceTrigger=PropertyChanged, Converter={local:BoolToVisibilityConverter}"/>

我习惯于使用来自以下资源的转换器:

<loc:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
...
<Button Content="Delete" Visibility="{Binding CanDelete, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BoolToVisibilityConverter}"/>

我现在的第一个问题是:更好的方法是什么?如果我使用 MarkupExtension-Version 有什么好处(除了用法更容易打字)?

我也看到了一个非常相似的实现,如下所示:

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

如果我没理解错的话,第一个解决方案只创建了这个转换器的一个实例。第二个为每个 XAML 创建此转换器的一个新实例,对吗?

标记扩展在这种情况下提供的唯一(轻微)优势是更简洁的 XAML 语法。

而不是这个:

<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
...
{Binding SomeBooleanProperty, Converter={StaticResource BooleanToVisibilityConverter}}

你可以拥有这个:

{Binding SomeBooleanProperty, Converter={my:BoolToVisibilityConverter}}

在我看来,这真的不值得。如果您对保存击键感到烦恼,您可以缩短用于引用转换器的键:

<BooleanToVisibilityConverter x:Key="btvc" />
...
{Binding SomeBooleanProperty, Converter={StaticResource my:btvc}}

由于标记扩展的 ProvideValue 方法是一个 实例 方法,它只能在创建 class 的实例后调用.由于 class 既是标记扩展又是转换器,因此代码的两种变体每次都会创建一个转换器。唯一的区别是第一个变体将始终 return 相同的转换器 :但是,它不会阻止另一个转换器被 创建.

使用 MarkupExtension 的一个巨大优势是我从未在网上看到它被使用的事实是它可以让你将值传递给转换器,可以用作参数或 return 值,因为示例:

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

用法:

...
Visibility="{Binding Property, 
            Converter={cnv:CustomNullToVisibilityConverter 
                       NotNullValue=Visible, NullValue=Collapsed}}" />
...

务必在.xaml中引用转换器的命名空间。


编辑:

有一件事我忘了提,是的,你是对的,因为这种方法每次使用时都会创建一个新的转换器实例,这是一个缺点。

然而,没有什么可以阻止您将带有 MarkupExtension 的转换器添加到资源字典中 - 这样它只会被实例化一次。像这样:

<cnv:CustomNullToVisibilityConverter x:Key="NullToVisibilityConverter"
        NotNullValue=Visible, NullValue=Collapsed />
...
Visibility="{Binding Property, Converter={StaticResource NullToVisibilityConverter}" />
...