绑定转换器参数

190

有没有一种方法可以在 Style 中实现这个效果:

<Style TargetType="FrameworkElement">
    <Setter Property="Visibility">
        <Setter.Value>
            <Binding Path="Tag"
                RelativeSource="{RelativeSource AncestorType=UserControl}"
                Converter="{StaticResource AccessLevelToVisibilityConverter}"
                ConverterParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />                        
        </Setter.Value>
    </Setter>
</Style>

我只需要将顶层父级的标签和控件本身的标签发送到我的转换器类。

3个回答

358

ConverterParameter 属性不能被绑定,因为它不是依赖属性。

由于 Binding 不是从 DependencyObject 派生的,因此它的任何属性都不是依赖属性。因此,Binding 永远不能成为另一个 Binding 的目标对象。

然而,有一种替代方案。您可以使用 MultiBinding多值转换器 替代普通 Binding:

<Style TargetType="FrameworkElement">
    <Setter Property="Visibility">
        <Setter.Value>
            <MultiBinding Converter="{StaticResource AccessLevelToVisibilityConverter}">
                <Binding Path="Tag" RelativeSource="{RelativeSource Mode=FindAncestor,
                                                     AncestorType=UserControl}"/>
                <Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}"/>
            </MultiBinding>
        </Setter.Value>
    </Setter>
</Style>

多值转换器接收一个源值数组作为输入:

public class AccessLevelToVisibilityConverter : IMultiValueConverter
{
    public object Convert(
        object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return values.All(v => (v is bool && (bool)v))
            ? Visibility.Visible
            : Visibility.Hidden;
    }

    public object[] ConvertBack(
        object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

38

很抱歉,这是不可能的,因为ConverterParameter不是一个DependencyProperty,所以您无法使用绑定。

但是也许您可以通过使用带有IMultiValueConverterMultiBinding来传递2个Tag属性。


28

还有一种使用MarkupExtension的替代方法,以便为ConverterParameter使用Binding。 使用此解决方案,您仍然可以使用默认的IValueConverter而不是IMultiValueConverter,因为ConverterParameter像在第一个示例中预期的那样传递到IValueConverter中。

这是我的可重用的MarkupExtension:

/// <summary>
///     <example>
///         <TextBox>
///             <TextBox.Text>
///                 <wpfAdditions:ConverterBindableParameter Binding="{Binding FirstName}"
///                     Converter="{StaticResource TestValueConverter}"
///                     ConverterParameter="{Binding ConcatSign}" />
///             </TextBox.Text>
///         </TextBox>
///     </example>
/// </summary>
[ContentProperty(nameof(Binding))]
public class ConverterBindableParameter : MarkupExtension
{
    #region Public Properties

    public Binding Binding { get; set; }
    public BindingMode Mode { get; set; }
    public IValueConverter Converter { get; set; }
    public Binding ConverterParameter { get; set; }

    #endregion

    public ConverterBindableParameter()
    { }

    public ConverterBindableParameter(string path)
    {
        Binding = new Binding(path);
    }

    public ConverterBindableParameter(Binding binding)
    {
        Binding = binding;
    }

    #region Overridden Methods

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var multiBinding = new MultiBinding();
        Binding.Mode = Mode;
        multiBinding.Bindings.Add(Binding);
        if (ConverterParameter != null)
        {
            ConverterParameter.Mode = BindingMode.OneWay;
            multiBinding.Bindings.Add(ConverterParameter);
        }
        var adapter = new MultiValueConverterAdapter
        {
            Converter = Converter
        };
        multiBinding.Converter = adapter;
        return multiBinding.ProvideValue(serviceProvider);
    }

    #endregion

    [ContentProperty(nameof(Converter))]
    private class MultiValueConverterAdapter : IMultiValueConverter
    {
        public IValueConverter Converter { get; set; }

        private object lastParameter;

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (Converter == null) return values[0]; // Required for VS design-time
            if (values.Length > 1) lastParameter = values[1];
            return Converter.Convert(values[0], targetType, lastParameter, culture);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            if (Converter == null) return new object[] { value }; // Required for VS design-time

            return new object[] { Converter.ConvertBack(value, targetTypes[0], lastParameter, culture) };
        }
    }
}

有了这个MarkupExtension在你的代码库中,你可以通过以下方式简单地绑定ConverterParameter

<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
    <Setter.Value>
     <wpfAdditions:ConverterBindableParameter Binding="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}"
                 Converter="{StaticResource AccessLevelToVisibilityConverter}"
                 ConverterParameterBinding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />          
    </Setter.Value>
</Setter>

看起来几乎和你最初的提案一样。


4
这很有用。不过,MultiValueConverterAdapter 丢失了。我在这里找到了它(链接:https://www.codeproject.com/Articles/459958/Bindable-Converter-Converter-Parameter-and-StringF)。 - blearyeye
它在Xamarin Forms上无法工作,因为multiBinding.ProvideValue(serviceProvider)不存在。有任何替代方案吗? - Softlion
尝试绑定到 DP:System.InvalidCastException:“无法将类型为 'MS.Internal.NamedObject' 的对象转换为类型。 - CodingNinja
在框架4.6.2中像描述的一样工作。做得好! - Tib Schott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接