我在我的视图模型中有一组变量:
public ObservableCollection<ObservableVariable> Variables { get; }= new ObservableCollection<ObservableVariable>();
ObservableVariable类有两个属性:string Name和bool Selected;该类实现了INotifyPropertyChanged接口。
我的目标是将此集合绑定到WPF视图中的复选框,并使用MultiBinding实现将“全选”复选框绑定到该列表。下面的图像说明了所需的视图。
请注意下面的XAML代码:
<CheckBox Content="Select All" Name="SelectAllCheckbox"></CheckBox>
...
<ListBox ItemsSource="{Binding Variables}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource LogicalOrConverter}" Mode="TwoWay">
<Binding Path="Selected"></Binding>
<Binding ElementName="SelectAllCheckbox" Path="IsChecked"></Binding>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
LogicalOrConverter 接受任意数量的 bool 值;如果有任何一个为 true,则返回 true。
如上所示,每个复选框都绑定到视图模型中的一个变量和“全选”复选框的状态。目前,除以下情况外,一切都按预期工作:如果我单击“全选”,则复选框在视图中更新,但更改不会传播回视图模型。
请注意,我的实现中大多数东西都可以正确工作。例如,如果我单击单个复选框,则视图模型会正确更新。
问题更详细地描述如下:
当我单击单个复选框时,在刚刚更改了该框的变量中触发 OnPropertyChanged 事件;调用转换器中的 ConvertBack 函数;更新视图模型,一切都很好。
然而,当我单击“全选”复选框时,视图中的单个复选框会更新,但不会在任何变量中调用 OnPropertyChanged,并且不会调用转换器中的 ConvertBack 函数。
还要注意的是,如果我取消选择“全选”,则各个检查框会恢复到之前的状态。
唯一更新视图模型的方法是单击单个复选框。但是,多绑定对于视图的目的是有效的。
我的问题是:
为什么不将复选框的更改传播到视图模型中的源集合?
该转换器:
public class LogicalOrConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
foreach (object arg in values)
{
if ((arg is bool) && (bool)arg == true)
{
return true;
}
}
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
object[] values = new object[2] {false, false};
if (value is bool && (bool) value == true)
values[0] = true;
return values;
}
}
ObservableVariable定义:
public class ObservableVariable : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
private bool _selected;
public bool Selected
{
get { return _selected; }
set
{
_selected = value;
OnPropertyChanged(nameof(Selected));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}