在 .net 中,ObservableCollection 的用途是什么?

251

ObservableCollection在.net中有什么用途?


3
@TheMuffinMan 说得没错,但我更喜欢StackOverflow上的解释方式,与MSDN过于死板正式的解释创作方式相比。 - Sizons
4
十年后仍是一个好问题! - Chris Catignani
8个回答

253

ObservableCollection是一种集合,允许集合外的代码知道集合的更改(添加、移动、删除)何时发生。它在WPF和Silverlight中被广泛使用,但它的使用并不仅限于此。代码可以添加事件处理程序来查看集合何时发生变化,然后通过事件处理程序做一些额外的处理,例如更改UI或执行其他操作。

下面的代码实际上并没有做任何事情,但演示了如何在类中附加处理程序,然后使用事件参数以某种方式对更改进行反应。当使用ObservableCollections时,WPF已经内置了许多操作,比如刷新UI,因此您可以免费获得它们。

class Handler
{
    private ObservableCollection<string> collection;

    public Handler()
    {
        collection = new ObservableCollection<string>();
        collection.CollectionChanged += HandleChange;
    }

    private void HandleChange(object sender, NotifyCollectionChangedEventArgs e)
    {
        foreach (var x in e.NewItems)
        {
            // do something
        }

        foreach (var y in e.OldItems)
        {
            //do something
        }
        if (e.Action == NotifyCollectionChangedAction.Move)
        {
            //do something
        }
    }
}

24
根据操作,e.NewItemse.OldItems可能为空。这可能会抛出NullReferenceException异常。 - dovid
8
附注:当“Action”为“Move”时,移动的元素将同时出现在“NewItems”和“OldItems”中。 - bohdan_trotsenko
谢谢你说到这个问题:WPF已经内置了许多操作,比如刷新UI,当使用ObservableCollections时,你可以免费获得它们。 - SlowLearner

170
一个 ObservableCollection 基本上像普通的集合一样工作,但它实现了以下接口: 因此,当您想知道集合何时发生更改时,它非常有用。触发事件将告诉用户哪些条目已添加/删除或移动。
更重要的是,在表单上使用数据绑定时,它们非常有用。

62

来自《Pro C# 5.0和.NET 4.5 Framework》

ObservableCollection<T>类非常有用,因为它能够在其内容发生更改时通知外部对象。(您可能猜到了,使用ReadOnlyObservableCollection<T>也非常相似,但具有只读性质)。在许多方面,使用ObservableCollection<T>与使用List<T>是相同的,因为这两个类都实现了相同的核心接口。使ObservableCollection<T>类独特的是,此类支持名为CollectionChanged的事件。无论是插入新项、删除(或重定位)当前项,还是修改整个集合,该事件都会触发。像任何事件一样,CollectionChanged是基于委托定义的,在本例中是NotifyCollectionChangedEventHandler。此委托可以调用任何将对象作为第一个参数,NotifyCollectionChangedEventArgs作为第二个参数的方法。请考虑以下Main()方法,该方法填充包含Person对象的可观察集合并连接CollectionChanged事件:

class Program
{
   static void Main(string[] args)
   {
     // Make a collection to observe and add a few Person objects.
     ObservableCollection<Person> people = new ObservableCollection<Person>()
     {
        new Person{ FirstName = "Peter", LastName = "Murphy", Age = 52 },
        new Person{ FirstName = "Kevin", LastName = "Key", Age = 48 },
     };
     // Wire up the CollectionChanged event.
     people.CollectionChanged += people_CollectionChanged;
     // Now add a new item.
     people.Add(new Person("Fred", "Smith", 32));

     // Remove an item.
     people.RemoveAt(0);

     Console.ReadLine();
   }
   static void people_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
   {
       // What was the action that caused the event?
        Console.WriteLine("Action for this event: {0}", e.Action);

        // They removed something. 
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
        {
            Console.WriteLine("Here are the OLD items:");
            foreach (Person p in e.OldItems)
            {
                Console.WriteLine(p.ToString());
            }
            Console.WriteLine();
        }

        // They added something. 
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            // Now show the NEW items that were inserted.
            Console.WriteLine("Here are the NEW items:");
            foreach (Person p in e.NewItems)
            {
                Console.WriteLine(p.ToString());
            }
        }
   }
}

传入的NotifyCollectionChangedEventArgs参数定义了两个重要的属性,OldItemsNewItems,它们将为您提供在事件触发前集合中当前存在的项列表以及参与更改的新项列表。然而,只有在正确的情况下才需要检查这些列表。请注意,当添加、删除、重定位或重置项时,CollectionChanged事件可能会触发。要发现哪些操作触发了事件,您可以使用NotifyCollectionChangedEventArgs的Action属性。Action属性可以与NotifyCollectionChangedAction枚举的以下任何成员进行比较:

public enum NotifyCollectionChangedAction
{
Add = 0,
Remove = 1,
Replace = 2,
Move = 3,
Reset = 4,
}

System.Collections.ObjectModel的成员


2
如果我在集合中更改Person的名称(而不更改集合本身),那么people_CollectionChanged事件会触发吗? - BenKoshy
它应该属于NotifyCollectionChangedAction.Replace类别,因为新值替换了旧值(除非我们在修改尝试中将值重置为默认值)。虽然我很想知道更多关于你所提到的“修改集合中的项而不改变集合本身”的内容,你是指通过迭代器吗? - Abhay Nagaraj
2
修改观察集合的任何属性或字段都不会触发CollectionChanged事件。 - BillW

34

不使用代码的解释

对于那些想要一个没有任何代码的答案(说笑话),并伴随一个故事(以帮助您记住)的人:

普通集合 - 没有通知

每隔一段时间,我会去纽约市,我的妻子会叫我买东西。所以我会带一个购物清单。这个清单上有很多东西,比如:

  1. 路易威登手提包($5000)
  2. 克里夫·克里斯汀的帝国陛下香水($215,000)
  3. 古驰墨镜($2000)

哈哈哈,好吧,我不会买那些东西。所以我划掉它们,并将它们从清单中删除,然后加入以下内容:

  1. 12打泰特利斯特高尔夫球。
  2. 12磅保龄球。

所以我通常没有买到这些东西就回家了,她永远不会生气。问题是,她不知道我从清单上划掉了什么,也不知道我添加了什么;她没有收到任何通知。

ObservableCollection - 更改时通知

现在,每当我从清单中删除某些东西时:她会收到通知。

ObservableCollection 的工作方式与此类似。如果您向其中添加或移除了某些内容,则会通知某人。

并且当他们收到通知时,就会躲起来或奔跑!当然,通过事件处理程序可以自定义后果。

愚蠢的故事,但希望您现在能够记住这个概念。


7

最大的用途之一是可以将UI组件绑定到一个集合,如果集合的内容发生变化,它们将做出相应的响应。例如,如果将ListView的ItemsSource绑定到一个集合,则如果您修改该集合,ListView的内容将自动更新。

编辑: 这里有来自MSDN的一些示例代码: http://msdn.microsoft.com/en-us/library/ms748365.aspx

在C#中,将ListBox连接到集合可能很容易,如下所示:

listBox.ItemsSource = NameListData;

如果您还没有将列表作为静态资源挂接并定义了NameItemTemplate,那么您可能需要重写PersonName的ToString()方法。例如:

public override ToString()
{
    return string.Format("{0} {1}", this.FirstName, this.LastName);
}

6

ObservableCollection是一种集合类型,主要用于通知UI在集合中的变化,它支持自动通知。

它主要用于WPF中,

比如说你有一个带有列表框和添加按钮的UI界面,当你点击按钮时,一个Person类型的对象将被添加到ObservableCollection中,并将该集合绑定到列表框的ItemSource上,因此,一旦你向集合中添加了新项,列表框就会自动更新并添加一项。


真的吗?这会发生吗?:O - Kings

5
class FooObservableCollection : ObservableCollection<Foo>
{
    protected override void InsertItem(int index, Foo item)
    {
        base.Add(index, Foo);

        if (this.CollectionChanged != null)
            this.CollectionChanged(this, new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Add, item, index);
    }
}

var collection = new FooObservableCollection();
collection.CollectionChanged += CollectionChanged;

collection.Add(new Foo());

void CollectionChanged (object sender, NotifyCollectionChangedEventArgs e)
{
    Foo newItem = e.NewItems.OfType<Foo>().First();
}

你能解释一下为什么FooObservableCollection实现了集合?以及为什么你重写了InsertItem方法吗? - Arie
@Arie:老实说,8年后我已经不记得了,所以不知道。从我在文档中看到的内容来看,没有必要做任何这样的事情,所有的东西都应该是开箱即用的。 - abatishchev

0

ObservableCollection注意事项

如上所述(Said Roohullah Allem)

使ObservableCollection类独特的是,该类支持名为CollectionChanged的事件。

请记住...如果您向ObservableCollection添加大量项目,则UI也会更新相同次数。这可能会导致UI卡顿或冻结。 解决方法是创建一个新列表,将所有项目添加到其中,然后将属性设置为新列表。这只会影响UI一次。再次强调...这适用于添加大量项目。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接