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