本文由DevDiv Vincent(Vincent@devdiv.com)原创,转载请注明出处。
文本参考:http://www.codeproject.com/Articles/...ableCollection
对原文主要代码进行了解释,并修改了源代码,使其支持Windows Phone平台,最后提供一个Windows Phone版本demo。
欢迎关注我的微博,接收最新Android/iOS/WP/Win8研发及行业动态:
http://weibo.com/xueyw
http://t.qq.com/ixueyw

1.1. 介绍
根据MSDN的描述,ObservableCollection作为动态数据集合,当Item增加、删除的时候会发送通知。
ObservableCollection可以作为绑定源。它实现了INotifyPropertyChanged和INotifyCollectionChanged接口,所以当数据集合改变时候,它会立即触发event,绑定目标收到event后可以更新。
ObservableCollection多数情况下工作良好,不过有的时候延缓甚至禁止通知是很有用处的。比如我们一次性添加10个item,我们可能希望10个添加完成后UI更新一次,而不是每添加一个item就刷新一次屏幕。延迟更新可以提高性能,进而减少闪屏几率。不幸的是,ObservableCollection没有提供这样的功能。
ObservableCollectionEx类设计的目的是提供这个缺失的功能。ObservableCollectionEx可以替换ObservableCollection,与其兼容,并且提供了延迟、禁用通知的功能。
1.2. 背景
ObservableCollectionEx是基于Collection类实现的(ObservableCollection也是一样),它实现了ObservableCollection提供的类和方法。
除了ObservableCollection的方法外,ObervableCollectionEx另外暴露两个方法,用来实现禁用或者延迟通知机制的。
1.3. 工作原理
除了性能优化外,ObervableCollectionEx工作方式与ObservableCollection一样。它内部使用Collection用来存储item,利用INotifyPropertyChanged和INotifyCollectionChanged机制通知ObservableCollectionEx的使用者数据源变化情况。

与ObservableCollection不同在于DelayNotifications或DisableNotifications方法。这个方法实际上是创建了一个ObservableCollectionEx实例,新实例构造时候传入的是老的实例作为构造函数的参数,构造函数另一个参数是表示是否启用或者延迟启用通知机制。

打开附件中的源码,可以看到我们重写了INotifyChanged.CollectionChanged的add和remove方法,代码如下:
Code:
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged

        {

            add

            {

                if (null == _notifyInfo)

                {

                    if (null == PropertyChanged)

                    {

                        FireCountAndIndexerChanged = delegate

                        {

                            OnPropertyChanged(new PropertyChangedEventArgs(_countString));

                            OnPropertyChanged(new PropertyChangedEventArgs(_indexerName));

                        };

                        FireIndexerChanged = delegate

                        {

                            OnPropertyChanged(new PropertyChangedEventArgs(_indexerName));

                        };

                    }



                    PropertyChanged += value;

                }

                else

                    _notifyInfo.RootCollection.PropertyChanged += value;

            }



            remove

            {

                if (null == _notifyInfo)

                {

                    PropertyChanged -= value;



                    if (null == PropertyChanged)

                    {

                        FireCountAndIndexerChanged = delegate { };

                        FireIndexerChanged = delegate { };

                    }

                }

                else

                    _notifyInfo.RootCollection.PropertyChanged -= value;

            }

        }
以上代码是在数据源观察者设置和移除event handler时候调用。
从代码中可以看到,如果没有启用disable/delay机制,那么FireCountAndIndexerChanged和FireIndexerChanged会触发OnPropertyChanged,最终调用PropertyChanged方法。你会发现ObservableCollectionEx的SetItem和MoveItem方法会调用FireIndexerChanged,也就是说同样是ObservableCollectionEx,在是否启用disable/delay机制时候执行的方法是不同的;不启用这两种机制最终会通过OnPropertyChanged调用PropertyChanged,而启用时候FireIndexerChanged是个空delegate,这个就是ObservableCollectionEx神奇的地方。

同样ObservableCollectionEx通过实现event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged的add和remove方法为不同状态下的ObservableCollectionEx提供不同的通知机制。

一旦ObservableCollectionEx实例离开作用于或者调用Dispose方法,它就会触发通知机制,我们来看一下相关代码:
Code:
protected virtual void Dispose(bool reason)
{
    if ((this._notifyInfo != null) && this._notifyInfo.HasEventArgs)
    {
        if (this._notifyInfo.RootCollection.PropertyChanged != null)
        {
            if (this._notifyInfo.IsCountChanged)
            {
                this._notifyInfo.RootCollection.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
            }
            this._notifyInfo.RootCollection.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
        }
        using (this._notifyInfo.RootCollection.BlockReentrancy())
        {
            NotifyCollectionChangedEventArgs eventArgs = this._notifyInfo.EventArgs;
            foreach (Delegate delegate2 in this._notifyInfo.RootCollection.CollectionChanged.GetInvocationList())
            {
                //  DynamicInvoke could be time-consuming, so it's removed by Vincent@devdiv.com
                //try
                //{
                //    NotifyCollectionChangedEventHandler delegate3 = (NotifyCollectionChangedEventHandler)delegate2;
                //    delegate2.DynamicInvoke(new object[] { this._notifyInfo.RootCollection, eventArgs });
                //}
                //catch (TargetInvocationException exception)
                //{
                //    if (!(exception.InnerException is NotSupportedException) || !(delegate2.Target is ICollectionView))
                //    {
                //        throw;
                //    }
                //  Support CollectionViewSource only 
                //  Modified and comment by Vincent@devdiv.com
                (delegate2.Target as ICollectionView).Refresh();
                //}
            }
        }
        this.CollectionChanged = this._notifyInfo.Initialize();
    }
}
从代码中可以看出,它是请求RootCollection调用OnPropertyChanged方法,而RootCollection就是我们构造ObservableCollectionEx对象时候保存的parent collection。
1.4. 代码使用
使用方法和ObservableCollection完全相同。
为了推迟通知,请使用using指令
Code:
using (ObservableCollectionEx<string> ocdelay = oce.DelayNotifications())
{
        ocdelay.Add("item1");
        ocdelay.Add("item2");
        ocdelay.Add("item3");
        ocdelay.Add("item4");
        ocdelay.Add("item5");
        ocdelay.Add("item6");
        ocdelay.Add("item7");
}
当ocdelay出了using作用域后,通知机制即会触发。
也可以通过调用Dispose,让通知立即触发。
Code:
ObservableCollectionEx<T> target = new ObservableCollectionEx<T>();
using (ObservableCollectionEx<T> iDelayed = target.DelayNotifications())
{
    iDelayed.Add(item0);
    iDelayed.Add(item0);
}
using (ObservableCollectionEx<T> iDelayed = target.DelayNotifications())
{
    iDelayed.Remove(item0);
    iDelayed.Remove(item0);
}
using (ObservableCollectionEx<T> iDelayed = target.DelayNotifications())
{
    iDelayed.Add(item0);
    iDelayed.Add(item0); 
    iDelayed.Dispose();
    iDelayed.Remove(item0);
    iDelayed.Remove(item0);
}
1.5. 测试工程
由于我没有权限上传附件,请这里下载代码
http://www.devdiv.com/Windows_Phone-...97052-1-1.html