TwoWay MultiBinding

Играть с MultiBinding:

Что я хочу: щелкнув любой флажок должен переключить все остальные.

Проблема: нажатие A не меняется B, нажав B не меняется A, Result работает.

Вопрос: как бы это исправить, пока еще использую MultiBinding?

PS: это попытка решить более сложную проблему, пожалуйста, ознакомьтесь с ней, прежде чем предлагать привязать все флажки к одному свойству.


Ниже находится 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(); }
    }
}

Преобразователь:

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

2 ответа

Решение

Простая причина в том, что ConvertBack() метод никогда не будет вызываться при нажатии CheckBox А.

Рассмотрим следующие рассуждения:

Checkbox А проверяется.

<Binding /> звонки Собственнику-установщику А.

Свойство-Setter A вызывается.

Собственность-Застройщик А звонки OnPropertyChanged("A"),

Событие PropertyChanged получено <MultiBinding /> из CheckBox Результат

Собираются свойства-получатели A и B (которые все еще остаются неизменными).

MultiBindingConverter.Convert() метод вызывается привязкой.

<MultiBinding /> обновляет CheckBox Результат IsChecked состояние в представлении.


Обработка изменений осуществляется без прикосновения CheckBox B и только вызывающий получатель имущества B.


Если у тебя есть MultiBinding на все CheckBoxЕсли все соответствующие установщики будут вызваны. Возможно, вам потребуется реализовать другой конвертер, если поведение изменения должно быть разным для каждого CheckBox,

Это также причина, по которой изменение подобного рода должно - предпочтительно - должно выполняться внутри ViewModel, если это возможно, потому что все эти привязки и преобразователи затрудняют отслеживание.

Я думаю, что ваш конвертер должен выглядеть так

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");
            }
    }
}
Другие вопросы по тегам