双向多绑定

TwoWay MultiBinding

MultiBinding一起玩:

我想要的:单击任一复选框应切换所有其他复选框。

问题:单击 A 不会更改 B,单击 B 不会更改 AResult 有效。

问题:如何修复它,同时仍在使用 MultiBinding

P.S.: 这是尝试解决更复杂的 problem,请在提供将所有复选框绑定到单个 属性.

之前参考它

下面是mcve.

xaml:

<StackPanel>
    <CheckBox Content="A" IsChecked="{Binding A}" />
    <CheckBox Content="B" IsChecked="{Binding B}" />
    <CheckBox Content="Result">
        <CheckBox.IsChecked>
            <MultiBinding Converter="{local:MultiBindingConverter}">
                <Binding Path="A" />
                <Binding Path="B" />
            </MultiBinding>
        </CheckBox.IsChecked>
    </CheckBox>
</StackPanel>

cs:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel();
    }
}

视图模型:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

    bool _a;
    public bool A
    {
        get { return _a; }
        set { _a = value; OnPropertyChanged(); }
    }

    bool _b;
    public bool B
    {
        get { return _b; }
        set { _b = value; OnPropertyChanged(); }
    }
}

转换器:

public class MultiBindingConverter : MarkupExtension, IMultiValueConverter
{
    public MultiBindingConverter() { }

    public override object ProvideValue(IServiceProvider serviceProvider) => this;

    object[] _old;

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        // first time init
        if (_old == null)
            _old = values.ToArray();
        // find if any value is changed and return value
        for (int i = 0; i < values.Length; i++)
            if (values[i] != _old[i])
            {
                _old = values.ToArray();
                return values[i];
            }
        // if no changes return first value
        return values[0];
    }


    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) =>
        Enumerable.Repeat(value, targetTypes.Length).ToArray();
}

我认为你的转换器应该是这样的

public class MultiBindingConverter : MarkupExtension, IMultiValueConverter
    {
        public MultiBindingConverter() { }

        public override object ProvideValue(IServiceProvider serviceProvider) => this;

        object[] _old;

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return ((bool)values[0] /*A */) || ((bool)values[1]/* B */);
        }


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

然后是

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string property = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

    bool _a;
    public bool A
    {
        get { return _a || _b; }
        set {
              if (_a == value) return;
              _a = value; 
              OnPropertyChanged("A");
              OnPropertyChanged("B");
            }
    }

    bool _b;
    public bool B
    {
        get { return _b || _a; }
        set {
              if (_b == value) return;
              _b = value; 
              OnPropertyChanged("B");
              OnPropertyChanged("A");
            }
    }
}

原因很简单,当你点击CheckBox A.

时,ConvertBack()方法永远不会被调用

考虑以下推理:

Checkbox A 得到 检查.

<Binding /> 调用 属性-Setter A.

属性-Setter A 被调用。

属性-Setter A 调用 OnPropertyChanged("A").

属性Changed-Event 由 CheckBox<MultiBinding /> 接收 Result.

属性-Getters AB(仍未改变)被调用。

MultiBindingConverter.Convert() 方法被绑定调用。

<MultiBinding /> 更新视图中的 CheckBox Result IsChecked 状态。


更改处理无需触及 CheckBox B,仅调用 属性 B.[=25= 的 getter ]


如果您在所有 CheckBox 上都有一个 MultiBinding,那么将调用所有适当的设置器。但是,如果每个 CheckBox 的更改行为应该不同,您可能需要实现不同的转换器。

这也是为什么更改此类内容应该 - 最好 - 如果可能的话在 ViewModel 中完成的原因,因为所有这些绑定和转换器使其有点难以跟踪。