【问题标题】:ObservableCollection<T> and OnCollectionChangedObservableCollection<T> 和 OnCollectionChanged
【发布时间】:2011-04-04 08:33:16
【问题描述】:

我正在学习 WPF 中的 ObservableCollections,但我并不清楚。我了解,如果我将控件绑定到 ObservableCollection 并且集合发生更改,则控件将反映更改。我的问题是:

  • ObservableCollection 实现了 INotifyCollectionChanged,它只是一个事件,CollectionChanged。每当集合更改但谁订阅了该事件时,都应该触发该事件?当您创建到集合的绑定时,它会自动完成吗?

  • 我正在使用 Reflector 查看 ObservableCollection,并试图查看 CollectionChanged 事件何时被触发。但是我找不到它在哪里完成。例如,我想看看当我向集合中添加新项目时它何时被触发。 Add(...) 在 ObservableCollection 的基类 Collection 中实现,但 Collection 没有实现 INotifyCollectionChanged,所以我不明白绑定控件是如何收到更改通知的。

我猜这其中很多都是在幕后处理的,但非常感谢任何信息。

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    回答您的第一个问题:这一切都始于 ItemContainerGenerator 类的“Items”属性(所有 ItemsControl 对象都有一个实例)。如果您查看上述“Items”属性的设置器,您会发现它具有特殊的逻辑来检查给定的 IList 是否为 INotifyCollectionChanged 类型,它将附加一个事件侦听器。

    ItemContainerGenerator.Items 属性:

    
    internal IList Items
    {
        get
        {
            return this._items;
        }
        set
        {
            if (this._items != value)
            {
                INotifyCollectionChanged source = this._items as INotifyCollectionChanged;
                if ((this._items != this.Host.View) && (source != null))
                {
                    CollectionChangedEventManager.RemoveListener(source, this);
                }
                this._items = value;
                source = this._items as INotifyCollectionChanged;
                if ((this._items != this.Host.View) && (source != null))
                {
                    CollectionChangedEventManager.AddListener(source, this);
                }
            }
        }
    }
    
    

    它确实与绑定无关。看,如果您有以下代码根本不使用 Bindings,集合更改通知仍然有效:

    <Window x:Class="DynamicObjectTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <StackPanel>
                <Button Click="Button_Click">click</Button>
                <ListBox x:Name="listBox"/>
            </StackPanel>
        </Grid>
    </Window>
    
    
    public partial class MainWindow : Window
        {
            ObservableCollection items = new ObservableCollection();
    
            public MainWindow()
            {
                InitializeComponent();
    
                this.listBox.ItemsSource = items;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                items.Add("A");
            }
        }
    

    【讨论】:

    • 感谢您的详细解释。现在更有意义了。
    【解决方案2】:

    当集合发生变化但谁订阅了该事件时,应该触发该事件?当您创建到集合的绑定时,它会自动完成吗?

    Binding 对象订阅源的CollectionChanged 和/或PropertyChanged 事件。

    【讨论】:

    • 我认为这不是完全正确的吗?请参阅下面的示例。
    • 我会被诅咒的。所以我猜想当Binding 设置ItemsSource 时,是ItemsSource 设置器创建了ItemContainerGenerator 并将其与集合更改事件挂钩。这意味着Binding 不关心它绑定的属性是否是一个集合。这很有意义。
    【解决方案3】:

    @Grant Crofton's answer 提供INotifyCollectionChanged 的“肉和土豆”在ObservableCollection&lt;T&gt; 内部的位置。但是,要回答您的第一个问题,您应该阅读 WPF and Binding Sources

    基本上,当您在 WPF 中绑定对象时,会根据某些“合同”检查它,其中之一是 INotifyCollectionChanged。 WPF 处理附加到事件和接收通知。然后,Control 将确定在收到其DependencyProperty 对象之一的更新通知时如何响应。

    有趣的是,WPF 在绑定中使用集合的视图而不是集合本身:

    WPF 从不直接绑定到集合。如果将集合指定为绑定源,WPF 实际上会绑定到集合的默认视图。有关默认视图的信息,请参阅Data Binding Overview

    【讨论】:

      【解决方案4】:

      从反映 .Net 3.5 来看,Collection 的 Add() 方法调用 InsertItem:

      public void Add(T item)
      {
          //...
          int count = this.items.Count;
          this.InsertItem(count, item);
      }
      

      InsertItem() 在 ObservableColletion 中被覆盖,它执行通知:

      protected override void InsertItem(int index, T item)
      {
          this.CheckReentrancy();
          base.InsertItem(index, item);
          this.OnPropertyChanged("Count");
          this.OnPropertyChanged("Item[]");
          this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
      }
      

      【讨论】:

      • 该死,我不知道我怎么错过了覆盖。感谢您为我清理。
      【解决方案5】:

      简单明了的英文答案:

      ObservableCollections 会在从集合中添加或删除对象时更新它们数据绑定到的控件。

      当集合中的对象被修改时,它们不会更新数据绑定。

      【讨论】:

      • 是的,我明白那部分。不过,我并不关心收藏中的物品。我的问题是关于集合本身(谁在监听集合的“更改”事件,以及当新项目添加到集合时触发事件的时间)。
      猜你喜欢
      • 2012-05-28
      • 1970-01-01
      • 1970-01-01
      • 2011-10-29
      • 2016-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-09
      相关资源
      最近更新 更多