Skip to main content
 首页 » 编程设计

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

2024年02月27日34luoye11

我需要绑定(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))); 
        } 
    } 
}