问题
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 CheckBox
es, 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