在WPF日常开发中,经常会用到ObservableCollection进行数据源绑定,并且使用+=CollectionChanged事件来监控Add或Remove元素时数据集合的变化。
最近在开发需求时,牵扯到了历史功能undo redo,当ObservableCollection发生变化了,为了能够撤销恢复,在CollectionChanged事件中捕捉e.OldItems,并将它加入到undo/redo队列:
public ObservableCollection<Info> Infos { get; } = new ObservableCollection<Info>();
// ...
private void InfosOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//...
DefaultChangeFactory.Current.OnCollectionChanged(this, "Infos", e.OldItems);
}
一直也没啥事,直到调用了
Infos.Clear();
发现调用Clear时InfosOnCollectionChanged中e.OldItems为null,无法将历史Infos内的数据进行保存。
网上搜了搜,根据资料显示,Clear时发出的是Reset动作,此时CollectionChanged不携带任何历史数据,.Net设计如此。
既然如此,那就只能想办法规避,两种方案:
一是自定义类继承自ObservableCollection,实现其内部虚函数ClearItems(),如下:
public class NewObservableCollection<T> : ObservableCollection<T>
{
protected override void ClearItems()
{
List<T> removedList = new List<T>(this); //先缓存历史数据
base.ClearItems(); //再clear all
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedList)); //最后把缓存的数据通过事件参数发出去
}
//重载事件处理
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action != NotifyCollectionChangedAction.Reset)
base.OnCollectionChanged(e);
}
// Constructors omitted
...
}
二是定义扩展方法,添加新方法进行调用:
public static void RemoveAll(this IList list)
{
while (list.Count > 0)
{
list.RemoveAt(list.Count - 1);
}
}
此方案最简单,代码量也最小。