【问题标题】:TwoWay MultiBinding双向多重绑定
【发布时间】:2016-03-17 15:05:12
【问题描述】:

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();
}

【问题讨论】:

  • 为什么不使用返回 || 的属性结果b 并在更新 a 或 b 时提高属性更改?
  • @Boo,因为您还没有阅读过P.S.;)其中一个属性(例如A)在 ViewModel 中将不可用。
  • 我的两分钱:你在这里错过了一个 ViewModel。
  • @ArnaudWeil,按control+F,输入ViewModel,然后按Enter
  • 有趣。说真的,您所说的 ViewModel 不是 ViewModel,因为它不会为每个输入公开一个属性。您的多重绑定是为了隐藏它不是读取 ViewModel 的事实。

标签: c# wpf multibinding two-way-binding


【解决方案1】:

原因很简单,当你点击CheckBoxA时,ConvertBack()方法永远不会被调用。

考虑以下推理:

Checkbox A选中

&lt;Binding /&gt; 调用 Property-Setter A

Property-Setter A 被调用。

Property-Setter A 调用OnPropertyChanged("A")

PropertyChanged-Event 由&lt;MultiBinding /&gt;CheckBox 结果接收。

Property-Getter AB(仍然没有改变)正在被调用。

MultiBindingConverter.Convert() 方法被 Binding 调用。

&lt;MultiBinding /&gt; 更新视图中的CheckBox 结果 IsChecked 状态。


无需接触CheckBox B 即可处理更改,只调用属性 B 的 getter。


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

这也是为什么像这样的更改应该 - 最好 - 如果可能的话在 ViewModel 中完成,因为所有这些 Bindings 和 Converters 都使它有点难以跟踪。

【讨论】:

  • 花了我一些时间来记住东西,感谢您的关注。 “如果可能,在 ViewModel 内” - 不幸的是,这是不可能的,VM 不知道附加属性,也不知道有关 VM 的附加属性(请参阅this question 了解问题的根源,其中多重绑定是 尝试解决方案)。解释很清楚,我接受“不”作为答案。
【解决方案2】:

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

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");
            }
    }
}

【讨论】:

  • 这个转换器有同样的问题:点击A不会改变B(只有AResult会改变)。
  • @Sinatr 添加了对虚拟机的更改
  • 对 ViewModel 的这种更改使 AB 过于依赖并呈现 MultiBinding 无用。正如我在 cmets 中提到的,A 在 ViewModel 中将不可用(它将是视图中的附加属性),所以这种解决方案对我没有用,对此感到抱歉。我试图让mcve 尽可能简单,所以没有将AB 充分分开(这样您就无法在ViewModel 中使用它们进行操作)。
  • 那么在这种情况下,只需添加一个返回 a|| 的属性结果b,我没有看到问题
猜你喜欢
  • 2012-09-14
  • 2011-01-19
  • 1970-01-01
  • 2015-04-26
  • 2016-04-19
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多