我需要绑定(bind)到一个 bool 属性,只有当集合中的一个属性为真时才为真。
这是绑定(bind):
<tk:BusyIndicator IsBusy="{Binding Tabs, Converter={StaticResource BusyTabsToStateConverter}}">
和 View 模型:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ObservableCollection<Tab> _tabs;
public ObservableCollection<Tab> Tabs
{
get
{ return _tabs; }
set
{
if (value != _tabs)
{
_tabs = value;
NotifyPropertyChanged();
}
}
}
Tab
类也有属性更改通知:
public class Tab : INotifyPropertyChanged
{
public bool IsBusy { get{...} set{...NotifyPropertyChanged();} }
这是转换器:
public class BusyTabsToStateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var tabs = value as ObservableCollection<Tab>;
return tabs.Any(tab => tab.IsBusy);
}
}
问题是,当 Tab.IsBusy
更改时,绑定(bind)源未得到通知,因为它绑定(bind)到可观察集合而不是 IsBusy
属性。
当集合中任何项目的 IsBusy
属性发生变化时,是否有办法使通知正确触发?
最佳答案
您可以使用 AnyTabBusy
而不是绑定(bind)转换器MainWindowViewModel 中的属性,由 PropertyChanged 事件处理程序触发更改通知,该事件处理程序附加或分离到 Tabs
中的各个元素将它们添加到集合或从集合中删除时的集合。
在下面的示例中,Tabs
属性是只读的。如果它必须是可写的,则必须附加和分离 TabsCollectionChanged
Tabs setter 中的处理程序。
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Tab> Tabs { get; } = new ObservableCollection<Tab>();
public bool AnyTabBusy
{
get { return Tabs.Any(t => t.IsBusy); }
}
public MainWindowViewModel()
{
Tabs.CollectionChanged += TabsCollectionChanged;
}
private void TabsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (Tab tab in e.NewItems)
{
tab.PropertyChanged += TabPropertyChanged;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (Tab tab in e.OldItems)
{
tab.PropertyChanged -= TabPropertyChanged;
}
break;
default:
break;
}
}
private void TabPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Tab.IsBusy))
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(AnyTabBusy)));
}
}
}
如果您想让这段代码可重用,您可以将其放入如下所示的派生集合类中,您可以在其中附加一个处理程序来处理 ItemPropertyChanged
。事件。
public class ObservableItemCollection<T>
: ObservableCollection<T> where T : INotifyPropertyChanged
{
public event PropertyChangedEventHandler ItemPropertyChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (INotifyPropertyChanged item in e.NewItems)
{
item.PropertyChanged += OnItemPropertyChanged;
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (INotifyPropertyChanged item in e.OldItems)
{
item.PropertyChanged -= OnItemPropertyChanged;
}
break;
default:
break;
}
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
ItemPropertyChanged?.Invoke(this, e);
}
}
View 模型现在可以简化为:
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableItemCollection<Tab> Tabs { get; }
= new ObservableItemCollection<Tab>();
public bool AnyTabBusy
{
get { return Tabs.Any(t => t.IsBusy); }
}
public MainWindowViewModel()
{
Tabs.ItemPropertyChanged += TabPropertyChanged;
}
private void TabPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(Tab.IsBusy))
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AnyTabBusy)));
}
}
}
关于c# - WPF 绑定(bind)到集合中所有项目的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46601730/