TwoWay MultiBinding

冷暖自知 提交于 2019-12-25 06:41:40

问题


Playing with MultiBinding:

What I want: clicking either checkbox should toggle all others.

Problem: clicking A doesn't change B, clicking B doesn't change A. Result works.

Question: how would I fix it, while still using MultiBinding?

P.S.: this is an attempt to solve more complicated problem, please refer to it before offering to bind all checkboxes to a single property.


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

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

Converter:

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

回答1:


The simple reason is, the ConvertBack() method will never be called when you click CheckBox A.

Consider the following reasoning:

Checkbox A gets checked.

<Binding /> calls Property-Setter A.

Property-Setter A gets called.

Property-Setter A calls OnPropertyChanged("A").

PropertyChanged-Event is recieved by the <MultiBinding /> of CheckBox Result.

Property-Getters A and B (which is still unchanged) are being called.

MultiBindingConverter.Convert() method gets called by the Binding.

<MultiBinding /> updates the CheckBox Result IsChecked state in the view.


Handling of change is done without ever touching CheckBox B and only calling the getter of property B.


If you have a MultiBinding on all CheckBoxes, then all appropriate setters will be called. You might need to implement a different Converter though, if the change behaviour should be different for each CheckBox.

That is also the reason, why changing stuff like that should - preferably - be done within the ViewModel if possible, because all those Bindings and Converters make it a little hard to track.




回答2:


I think your converter should look like this

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

and then

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


来源:https://stackoverflow.com/questions/36064419/twoway-multibinding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!