【问题标题】:Nested binding and piped conversion嵌套绑定和管道转换
【发布时间】:2011-04-02 17:59:01
【问题描述】:

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

仅此一项无法使用简单的转换器或 DataTrigger 完成,因为需要两个绑定,所以我创建了一个通用的 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
}

这里明显的问题是转换器返回一个布尔值,我需要先将其转换为Visibility

仅使用转换器将 MultiBinding 包装在另一个绑定中不起作用,因为这些属性不是依赖项属性(因此不能为它们分配绑定)。我可以想到一些解决方法,例如将 bool 存储在一些 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 属性,但我也不知道如何实现。遇到的一个问题是需要依赖属性,因此从 DependencyObject 继承会很好,但从 Binding 类继承也有意义,因为 PipeConverter 应该绑定并且需要设置为另一个依赖项的值属性。

【问题讨论】:

    标签: wpf binding nested


    【解决方案1】:

    基于这样的想法(参见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>
    

    【讨论】:

      【解决方案2】:

      三个选项:

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

      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:请注意这篇文章已经很老了。

      【讨论】:

      • A & B 不是我想做的(我希望转换器保持通用),我想我会接受 C 的想法并编写自己的实现,除非有更好的选择。 ..
      • @H.B.,那么我相信,C 符合您的需要。此外,它是此类要求的通用解决方案。
      • 我带了一个管道转换器,谢谢。 (我在回答中详细说明了我的程序)
      猜你喜欢
      • 2013-12-18
      • 2020-09-05
      • 1970-01-01
      • 1970-01-01
      • 2019-08-07
      • 2018-11-05
      • 1970-01-01
      • 1970-01-01
      • 2020-07-16
      相关资源
      最近更新 更多