本文由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方法,代码如下:
以上代码是在数据源观察者设置和移除event handler时候调用。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; } }
从代码中可以看到,如果没有启用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方法,它就会触发通知机制,我们来看一下相关代码:
从代码中可以看出,它是请求RootCollection调用OnPropertyChanged方法,而RootCollection就是我们构造ObservableCollectionEx对象时候保存的parent collection。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(); } }
1.4. 代码使用
使用方法和ObservableCollection完全相同。
为了推迟通知,请使用using指令
当ocdelay出了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"); }
也可以通过调用Dispose,让通知立即触发。
1.5. 测试工程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); }
由于我没有权限上传附件,请这里下载代码
http://www.devdiv.com/Windows_Phone-...97052-1-1.html

Reply With Quote

