.NET 3.5和.NET 4.5之间的MultiBinding发生了什么?

ghn*_*hnz 5 wpf converter multibinding imultivalueconverter

我们目前正在将项目从3.5版转换为.NET 4.5版.

我们设置了一个文本框IsEnabled,它使用多绑定转换器进行多重绑定.每个绑定都有自己的转换器.

一切都在.NET 3.5中运行良好,但在.NET 4.5中,传递给子转换器的目标类型是object类型而不是bool.

这是一个已知的问题?MS重构多重绑定,不将目标类型传递给子转换器.

我创建了一个简化的项目来演示这个问题.我在VS2008中创建了项目,然后将其转换为VS2012和.NET 4.5.

窗口XAML:

<Window x:Class="TestMultiBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestMultiBinding"        
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <local:NotConverter x:Key="NotConverter"/>
        <local:MultiBoolConverter x:Key="MultiBoolConverter"/>
    </Window.Resources>
    <StackPanel>

        <TextBox>
            <TextBox.IsEnabled>
                <MultiBinding Converter="{StaticResource MultiBoolConverter}">
                    <Binding Path="ConditionOne" />
                    <Binding Path="ConditionTwo" Converter="{StaticResource NotConverter}"/>
                </MultiBinding>
            </TextBox.IsEnabled>
        </TextBox>


    </StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Globalization;

namespace TestMultiBinding
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

    public class ViewModel
    {
        public bool ConditionOne { get { return true; } }
        public bool ConditionTwo { get { return false; } }
    }

    /// <summary>
    /// Converts a boolean to its inverse (useful for radio buttons).
    /// </summary>
    [ValueConversion(typeof(bool), typeof(bool))]
    public class NotConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (targetType != typeof(bool) && targetType != typeof(bool?)) { throw new ArgumentException("Can only convert booleans.", "targetType"); }

            //return !(bool)value;
            return !true.Equals(value);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Convert(value, targetType, parameter, culture);
        }
    }

    /// <summary>
    /// Converts multiple boolean values to one. Uses AND by default. Possible extension: Pass the desired operation as parameter
    /// </summary>
    [ValueConversion(typeof(bool), typeof(bool))]
    public class MultiBoolConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                // todo: support other operations like OR, XOR
                return values.Cast<bool>().Aggregate(true, (res, cur) => res && cur);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError("MultiBoolConverter({0}): {1}", parameter, ex.Message);
                return DependencyProperty.UnsetValue;
            }
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            System.Diagnostics.Trace.TraceError("MultiBoolConverter: does not support TwoWay or OneWayToSource bindings.");
            return null;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

Rob*_*b H 1

您测试 targetType 为 bool 是否有原因?

我很惊讶它在 3.5 中起作用,因为 NonConverter 正在从 bool 转换为对象(因为 MultiBinding 将对象数组作为输入输出)。


我使用反射器进行了一些挖掘,底层逻辑确实发生了变化。

这是从internal void TransferValue(object newValue, bool isASubPropertyChange)方法BindingExpression

在 3.5 中:

internal void TransferValue(object newValue, bool isASubPropertyChange)
{
  DependencyObject targetElement = this.TargetElement;
  if (targetElement == null || this.Worker == null)
    return;
  Type propertyType = this.TargetProperty.PropertyType;
Run Code Online (Sandbox Code Playgroud)

在 4.5 中,所有对 的调用都propertyType被以下定义替换effectiveTargetType

internal void TransferValue(object newValue, bool isASubPropertyChange)
{
  DependencyObject targetElement = this.TargetElement;
  if (targetElement == null || this.Worker == null)
    return;
  Type effectiveTargetType = this.GetEffectiveTargetType();
...
}

internal Type GetEffectiveTargetType()
{
  Type type = this.TargetProperty.PropertyType;
  for (BindingExpressionBase bindingExpressionBase = this.ParentBindingExpressionBase; bindingExpressionBase != null; bindingExpressionBase = bindingExpressionBase.ParentBindingExpressionBase)
  {
    if (bindingExpressionBase is MultiBindingExpression)
    {
      type = typeof (object);
      break;
    }
  }
  return type;
}
Run Code Online (Sandbox Code Playgroud)

我不确定在这种情况下 TargetProperty 设置为什么,但您可以看到为什么它现在被设置为 MultiBindings 的对象。

而且,仅供参考,此更改似乎发生在 .NET 4.0 中。