c# - WPF 绑定(bind)到集合中所有项目的属性

标签 c# wpf binding inotifypropertychanged

我需要绑定(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/

相关文章:

c# - 带有 Xamarin 的 Handlebars.net 在 Release 中不起作用

c# - UI 对长时间的 for 循环无响应

c# - 使用 Webdriver PageFactory 字段从未分配警告

数据网格上的 WPF 触发器以根据绑定(bind)隐藏/显示列

java - 如何将 TableView 内的 TableColumn 的总和绑定(bind)到外部 Label textProperty()?

c# - 使用 wpf 在列表框中绑定(bind)字典的键和值

c# - 在 C# 中将 DateTime 属性序列化为 XML

wpf - x :Key & TargetType in styles

.net - 增加单选按钮的点击大小

.net - WPF WebBrowser 浏览器版本