WPF绑定到集合中所有项目的属性 [英] WPF binding to property of all items in a collection

查看:271
本文介绍了WPF绑定到集合中所有项目的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要绑定到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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆