WPF绑定到集合中所有项目的属性 [英] WPF binding to property of all items in a collection
问题描述
我需要绑定到bool属性,只有当集合中的一个属性为true时,这才是正确的.
I need to bind to a bool property, that is only true, when one of the properties in the collection is true.
这是绑定:
<tk:BusyIndicator IsBusy="{Binding Tabs, Converter={StaticResource BusyTabsToStateConverter}}">
和视图模型:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ObservableCollection<Tab> _tabs;
public ObservableCollection<Tab> Tabs
{
get
{ return _tabs; }
set
{
if (value != _tabs)
{
_tabs = value;
NotifyPropertyChanged();
}
}
}
Tab
类还具有属性更改通知:
The Tab
class also has property change notification:
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
更改时,不会通知绑定源,因为它绑定到可观察的集合而不是IsBusy
属性.
The problem is, when Tab.IsBusy
changes the binding source is not notified, because it is bound to the observable collection and not to the IsBusy
property.
当集合中任何项目的IsBusy
属性发生更改时,是否可以使通知正确触发?
Is there a way to make the notification trigger correctly when the IsBusy
property on any of the items in the collection changes?
推荐答案
您可以在MainWindowViewModel中拥有一个AnyTabBusy
属性,而不是绑定转换器,该属性由附加的PropertyChanged事件处理程序触发更改通知或将Tabs
集合中的单个元素添加到集合中或从集合中删除时,将其分离.
Instead of a Binding Converter, you could have a AnyTabBusy
property in MainWindowViewModel, for which a change notification is fired by a PropertyChanged event handler, which is attached or detached to individual elements from the Tabs
collection when they are added to or removed from the collection.
在下面的示例中,Tabs
属性是只读的.如果必须可写,则必须在Tabs设置器中附加和分离TabsCollectionChanged
处理程序.
In the example below, the Tabs
property is readonly. If it has to be writeable, you would have to attach and detach the TabsCollectionChanged
handler in the 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
事件附加处理程序.
If you want to make this code reusable, you could put it into a derived collection class like shown below, where you could attach a handler for an ItemPropertyChanged
event.
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);
}
}
现在可以将视图模型简化为:
The view model could now be reduced to this:
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)));
}
}
}
这篇关于WPF绑定到集合中所有项目的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!