嵌套绑定和管道转换

时间:2011-04-02 17:59:02

标签: wpf binding nested

为了减少多余的XAML标记,我尝试将一个radiobutton类型的选择控件一般填充,即我使用枚举为ItemsControl的{​​{1}}并创建一个显示哪个项目的DataTemplate通过检查项目的枚举值是否与当前设置相同来选择。

仅使用简单的转换器或DataTrigger无法完成,因为需要两个绑定,所以我创建了一个通用ItemsSource来检查是否相等:

MutliValueConverter
<CheckBox.Visibility>
    <MultiBinding Converter="{StaticResource EqualityComparisonConv}">
        <Binding Path="Key"/>
        <Binding Path="DisplayMode_Current" Source="{x:Static local:App.Settings}"/>
    </MultiBinding>
</CheckBox.Visibility>

这里显而易见的问题是转换器返回一个布尔值,我需要首先转换为public class EqualityComparisonConverter : IMultiValueConverter { #region IMultiValueConverter Members public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 2) throw new Exception("At least two inputs are needed for comparison"); bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) => { return x1.Equals(x2); }); return output; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotSupportedException(); } #endregion }

仅使用转换器将MultiBinding包装在另一个绑定中不起作用,因为属性不是依赖项属性(因此不能为它们分配绑定)。我可以想到一些解决方法,比如将bool存储在某个Visibility属性中,所以我可以将其用作新的绑定源,但我会更喜欢这样的事情:

Tag

当原始绑定发生更改时,此类需要更新其输出,并且需要能够将其输出值公开给<CheckBox.Visibility> <local:PipeConverter Converter="{StaticResource BooleanToVisibilityConv}"> <MultiBinding Converter="{StaticResource EqualityComparisonConv}"> <Binding Path="Key"/> <Binding Path="DisplayMode_Current" Source="{x:Static local:App.Settings}"/> </MultiBinding> </local:PipeConverter> </CheckBox.Visibility> 属性,但我不知道如何实现。遇到的一个问题是需要依赖属性,因此继承自Visibility会很好,但继承自Binding类也是有意义的,因为PipeConverter应该绑定并需要设置为值另一个依赖属性。

2 个答案:

答案 0 :(得分:3)

基于这个想法(参见publicgk's answeroption C),人们可以创建一个包含一系列转换器的转换器,这些转换器在内部按顺序使用,我编写了一个适合我需要的伪劣实现。即我可以在开头使用MultiValueConverter并将输出传输到正常转换器列表中:

[ContentProperty("Converters")]
public class GroupConverter : IValueConverter, IMultiValueConverter
{
    private IMultiValueConverter _multiValueConverter;
    public IMultiValueConverter MultiValueConverter
    {
        get { return _multiValueConverter; }
        set { _multiValueConverter = value; }
    }

    private List<IValueConverter> _converters = new List<IValueConverter>();
    public List<IValueConverter> Converters
    {
        get { return _converters; }
        set { _converters = value; }
    }

    #region IValueConverter Members
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return GroupConvert(value, Converters);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return GroupConvertBack(value, Converters.ToArray().Reverse());
    }

    private static object GroupConvert(object value, IEnumerable<IValueConverter> converters)
    {
        return converters.Aggregate(value, (acc, conv) => { return conv.Convert(acc, typeof(object), null, null); });
    }

    private static object GroupConvertBack(object value, IEnumerable<IValueConverter> converters)
    {
        return converters.Aggregate(value, (acc, conv) => { return conv.ConvertBack(acc, typeof(object), null, null); });
    }
    #endregion

    #region IMultiValueConverter Members
    private InvalidOperationException _multiValueConverterUnsetException =
        new InvalidOperationException("To use the converter as a MultiValueConverter the MultiValueConverter property needs to be set.");

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (MultiValueConverter == null) throw _multiValueConverterUnsetException;
        var firstConvertedValue = MultiValueConverter.Convert(values, targetType, parameter, culture);
        return GroupConvert(firstConvertedValue, Converters);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        if (MultiValueConverter == null) throw _multiValueConverterUnsetException;
        var tailConverted = GroupConvertBack(value, Converters.ToArray().Reverse());
        return MultiValueConverter.ConvertBack(tailConverted, targetTypes, parameter, culture);
    }

    #endregion
}

正如您所看到的,我几乎完全忽略了ConverterParametersTargetTypesCultureInfo参数,ConvertBack方法未经测试,所以我不这样做建议任何人实际使用它。

XAML用法:

<vc:GroupConverter MultiValueConverter="{StaticResource EqualityComparisonConv}">
    <StaticResource ResourceKey="BoolToVisibilityConv"/>
</vc:GroupConverter>

答案 1 :(得分:2)

三种选择:

选项A :将您的bool转换为多值转换器中的可见性(仅一行)

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    if (values.Length < 2)
        throw new Exception("At least two inputs are needed for comparison");
    bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) =>
         { return x1.Equals(x2); });
    return output ? Visibility.Visible : Visibility.Collapsed;
}


选项B :以编程方式使用现有的booltovisibilityconverter

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    if (values.Length < 2)
        throw new Exception("At least two inputs are needed for comparison");
    bool output = (bool)values.Skip(1).Aggregate(values[0], (x1, x2) =>
         { return x1.Equals(x2); });
    System.Windows.Controls.BooleanToVisibilityConverter booltovisibilityconverter = new System.Windows.Controls.BooleanToVisibilityConverter();
    return booltovisibilityconverter.Convert(output, System.Type.GetType("System.Boolean"), parameter, culture);
}

PS:您可能希望缓存booltovisibilityconverter而不是每次都创建它。

选项C :管道您的转换器
Piping Value Converters in WPF
PS:要知道这篇文章很老了。