DataGrid CollectionViewSource Refresh性能问题
在WPF中,CollectionViewSource是一个经常用到的集合类型,主要用于对后台数据排序,条件筛选或分组操作。
场景:当需要筛选包含“test”的数据项时,我们会分步输入搜索条件,依次为“t”、“te”、“tes”,而最后是“test”,而我们简单的处理经常会是在Textbox的TextChanged事件中直接添加CollectionViewSource的View的Refresh,这样筛选操作实际上进行了四次筛选操作,每一步筛选操作都会Filter及RefreshUI,对于应用来说,前几次的Filter及RefreshUI均是无用的,白白浪费了资源。
解决方案:
1:TextChanged中的Refresh操作进行延迟刷新,即用户输入完成后先不进行刷新,超过一定的时间再进行刷新,对于筛选条件,需要一个定时器延迟,(当筛选条件过多时,定时器数量过量,不易管理,易出错)
2:将延迟做到CollectionViewSource中View的刷新供码中,比如,封装一个方法SourceRefresh,协定刷新_source时只能通过SourceRefresh方法。
private CollectionViewSource _source = new CollectionViewSource(); private DispatcherTimer timer=new DispatcherTimer(); public void SourceRefresh() { if(timer.IsEnabled) timer.Stop(); timer.Interval = new TimeSpan(0, 0, 0, 0, 200); timer.Tick += (sender, e) => { timer.Stop(); if (null != _source.View) _source.View.Refresh(); }; timer.Start(); }
看源码会比较直观点,如下:
下面的代码是我写的例子,可以参考下:
public class MyListCollectionView<T> : ListCollectionView { public MyListCollectionView(IList list) : base(list) { _filterTimer.Interval = new TimeSpan(0, 0, 0, 0, 1); _filterTimer.Tick += (sender, e) => { CancelAllTimer(); RefreshFilter(); _freshUITimer.Start(); Console.WriteLine("A"); }; _freshUITimer.Interval = new TimeSpan(0, 0, 0, 0, 500); _freshUITimer.Tick += (sender, e) => { CancelAllTimer(); RefreshUI(); _freshCurrentTimer.Start(); Console.WriteLine("B"); }; _freshCurrentTimer.Interval = new TimeSpan(0, 0, 0, 0, 20); _freshCurrentTimer.Tick += (sender, e) => { CancelAllTimer(); RefreshCurrent(); Console.WriteLine("C"); }; } private DispatcherTimer _filterTimer = new DispatcherTimer(); private DispatcherTimer _freshUITimer = new DispatcherTimer(); private DispatcherTimer _freshCurrentTimer = new DispatcherTimer(); private bool _isFreshFilter = false; private void CancelAllTimer() { _filterTimer.Stop(); _freshUITimer.Stop(); _freshCurrentTimer.Stop(); } protected override void RefreshOverride() { _filterTimer.Start(); } protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args) { if (!_isFreshFilter) base.OnCollectionChanged(args); } protected override void OnCurrentChanged() { if (!_isFreshFilter) base.OnCurrentChanged(); } protected override void OnPropertyChanged(PropertyChangedEventArgs e) { if (!_isFreshFilter) base.OnPropertyChanged(e); } private void RefreshFilter() { try { //cancel all _isFreshFilter = true; base.RefreshOverride(); } finally { _isFreshFilter = false; } } private void RefreshUI() { OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } private void RefreshCurrent() { OnCurrentChanged(); OnPropertyChanged("IsCurrentAfterLast"); OnPropertyChanged("IsCurrentBeforeFirst"); OnPropertyChanged("CurrentPosition"); OnPropertyChanged("CurrentItem"); } private void OnPropertyChanged(string proName) { OnPropertyChanged(new PropertyChangedEventArgs(proName)); } } public class MyCollection<T> : ObservableCollection<T>, ICollectionViewFactory { public MyCollection() { } public MyCollection(IList<T> list) : base(list) { } public MyCollection(IEnumerable<T> list) : base(list) { } public MyListCollectionView<T> _view; public ICollectionView CreateView() { _view = new MyListCollectionView<T>(this); return _view; } }