DataGrid控件是一个列表控件, 可以进行过滤,排序等。本文主要针对DataGrid的过滤功能进行分析, 并提供优化方案。

1)DataGrid的过滤过程:
     用户输入过滤条件
     调用DataGrid的CollectionViewSource的View.Refresh()功能
     DataGrid控件内部调用CollectionView的RefreshOverride方法
     CollectionView会调用CollectionViewSource的Filter回调函数来过滤符合自定义过滤条件的数据
     CollectionView调用内部的OnCollectionChanged和OnCurrentChanged分别更新界面上的数据和当前选中的Item
2)通过分析发现(10W条数据, 实时过滤时UI非常卡,导致用户输入过滤字符丢失),调用CollectionViewSource的View.Refresh()的性能损耗主要集中于:
     CollectionViewSource.Filter注册的方法,以及OnCollectionChanged(每次更新都导致ItemContainerGenerator重新构造UI元素)
     
     
3)优化方向:
     减少CollectionViewSource.Filter注册的方法的耗时(在实时过滤中每个条件的更改都会调用Refresh从而调用Filter方法)
     减少OnCollectionChanged调用的次数。
4)具体优化措施:
     实例化3个Timer, 分别用于获取过滤后的数组(调用Filter)、调用OnCollectionChanged、OnCurrentItemChanged。3个timer分别由前一个timer完成时启动, 形成一个顺序操作。每次调用Timer时,先停止后续Timer的执行, 这样保证在合理的时间间隔里只有一次Refresh完整完成。
5)实现:
     下面代码重载了ObservableCollection, 然后创建自定义的ListCollectionview.使用时只要用CustomCollection声明列表数据,包装为CollectionViewSource, 绑定到DataGrid的ItemSource即可。
//声明数组数据
  public CustomCollection<StudyInfoModel> StudyList
        {
            get { return studyList; }
        }
//包装为CollectionView
     <CollectionViewSource Source="{Binding StudyList}" x:Key="StudyListView">
                <CollectionViewSource.SortDescriptions>
                    <ComponentModel:SortDescription PropertyName="DateTime" Direction="Descending"/>
                </CollectionViewSource.SortDescriptions>
            </CollectionViewSource>
//绑定到DataGrid
<DataGrid ItemsSource="{Binding Mode=OneWay, Source={StaticResource StudyListView}}" />

      public class CustomCollectionView<T> : ListCollectionView
    {
private readonly DispatcherTimer _timerRefreshCalculate = new DispatcherTimer();
private readonly DispatcherTimer _timerRefreshUI = new DispatcherTimer();
private readonly DispatcherTimer _timerRefreshCurrentItem = new DispatcherTimer();
private bool _isRefreshingCalculate = false;
private object _oldSelectedItem = null; public CustomCollectionView(IList list)
: base(list)
{
_timerRefreshUI.Interval = new TimeSpan(0, 0, 0, 0, 300);
_timerRefreshCurrentItem.Interval = new TimeSpan(0, 0, 0, 0, 500);
_timerRefreshCalculate.Interval = new TimeSpan(0, 0, 0, 0, 200);
} #region Override Method protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (_isRefreshingCalculate)
{
return;
} base.OnPropertyChanged(e);
} protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
if (_isRefreshingCalculate)
{
return;
} base.OnCollectionChanged(args);
} protected override void OnCurrentChanged()
{
if (_isRefreshingCalculate)
{
return;
} base.OnCurrentChanged();
} protected override void RefreshOverride()
{
CancelAllRefreshRequest(); StartRefresh();
} #endregion #region Public Method public void CancelAllRefreshRequest()
{
_timerRefreshCurrentItem.Stop();
_timerRefreshCurrentItem.Tick -= TimerCurrentItem; _timerRefreshUI.Stop();
_timerRefreshUI.Tick -= TimerUI; _timerRefreshCalculate.Stop();
_timerRefreshCalculate.Tick -= TimerCalculate; if (null != this.CurrentItem)
{
_oldSelectedItem = this.CurrentItem;
} SetCurrent(null, -1);
} #endregion #region Private Method private void StartRefresh()
{
_timerRefreshCurrentItem.Stop();
_timerRefreshCurrentItem.Tick -= TimerCurrentItem; _timerRefreshUI.Stop();
_timerRefreshUI.Tick -= TimerUI; _timerRefreshCalculate.Stop();
_timerRefreshCalculate.Tick -= TimerCalculate; //begin to refresh from calculate, so set flag by true.
//and it shielded any collection action during the calculating time.
//this logic will avoid items are not correct at UI during refresh.
_isRefreshingCalculate = true; _timerRefreshCalculate.Tick += TimerCalculate;
_timerRefreshCalculate.Start();
} private void RefreshCalculate(CancellationToken? token)
{
_timerRefreshCalculate.Tick -= TimerCalculate; if (null != token && null != token.Value)
{
token.Value.ThrowIfCancellationRequested();
} _isRefreshingCalculate = true; base.RefreshOverride(); _isRefreshingCalculate = false; if (null != token && null != token.Value)
{
token.Value.ThrowIfCancellationRequested();
}
} private void RefreshUI()
{
try
{
//detach timer
_timerRefreshUI.Tick -= TimerUI; base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); //set timer to refresh current item
_timerRefreshCurrentItem.Tick -= TimerCurrentItem;
_timerRefreshCurrentItem.Tick += TimerCurrentItem;
_timerRefreshCurrentItem.Start();
}
catch (OperationCanceledException)
{
return;
}
} private void RefreshCurrentItem()
{
_timerRefreshCurrentItem.Tick -= TimerCurrentItem; if (null == this.InternalList || this.InternalList.Count <= 0)
{
return;
} if (null != _oldSelectedItem)
{
var index = this.InternalList.IndexOf(_oldSelectedItem);
if (index != -1)
{
SetCurrent(_oldSelectedItem, index);
}
else
{
SetCurrent(this.InternalList[0], 0);
}
}
else
{
SetCurrent(this.InternalList[0], 0);
} //Set event to update UI
base.OnCurrentChanged(); this.OnPropertyChanged("IsCurrentAfterLast");
this.OnPropertyChanged("IsCurrentBeforeFirst");
this.OnPropertyChanged("CurrentPosition");
this.OnPropertyChanged("CurrentItem");
} private void TimerCalculate(object sender, EventArgs e)
{
_timerRefreshCurrentItem.Stop();
_timerRefreshCurrentItem.Tick -= TimerCurrentItem; _timerRefreshUI.Stop();
_timerRefreshUI.Tick -= TimerUI; try
{
RefreshCalculate(null);
}
catch (OperationCanceledException)
{
} _timerRefreshUI.Interval = new TimeSpan(0, 0, 0, 0, 50 + Math.Min((int)(this.InternalCount / 80), 300));
_timerRefreshUI.Tick += TimerUI;
_timerRefreshUI.Start();
} private void TimerUI(object sender, EventArgs e)
{
RefreshUI();
} private void TimerCurrentItem(object sender, EventArgs e)
{
RefreshCurrentItem();
} private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
} #endregion
} public class CustomCollection<T> : ObservableCollection<T>, ICollectionViewFactory
{
public CustomCollection()
{
} public CustomCollection(List<T> list)
: base(list)
{
} public CustomCollection(IEnumerable<T> collection)
: base(collection)
{
} public ICollectionView CreateView()
{
return new CustomCollectionView<T>(this);
}
}

WPF中DataGrid控件的过滤(Filter)性能分析及优化的更多相关文章

  1. Working Experience - WPF 中 DataGrid 控件的应用

    问题: 添加控件后, 编辑单元格会出现异常 绑定 ItemsSource 属性后, 更新绑定对象的数据, UI 不刷新 如何显示控件中 ComboBox 类型 解决方法: 绑定 ItemsSource ...

  2. WPF中DataGrid控件内Button的Command和CommandParameter的绑定

    场景:视频上传功能,上传列表使用DataGrid控件,视频有不同的状态对应不同的操作,DataGrid中最后一列为操作列,里面是Button控件.希望点击Button后执行对应的操作,但是设置Butt ...

  3. WPF中Datagrid控件添加行号

    /// <summary> /// 导入Excel文件按钮 /// </summary> /// <param name="sender">&l ...

  4. wpf 中DataGrid 控件的样式设置及使用

    本次要实现的效果为: 这个DataGrid需要绑定一个集合对象,所以要先定义一个Experience类,包含三个字段 /// <summary> /// 定义工作经历类 /// </ ...

  5. WPF中查找控件的扩展类

    在wpf中查找控件要用到VisualTreeHelper类,但这个类并没有按照名字查找控件的方法,于是搜索网络,整理出下面这个类,感觉用起来很是方便. 贴出来,供大家参考. /// <summa ...

  6. WPF的DataGrid控件从excel里复制数据然后粘贴

    WPF的DataGrid控件不能像winform的DataGridView控件一样,支持值的粘贴.WPF的DataGrid控件本质上是跟数据绑定联系在一起,所以需要进行复制粘贴的操作,可以在wpf里用 ...

  7. WPF 4 DataGrid 控件(进阶篇一)

    原文:WPF 4 DataGrid 控件(进阶篇一)      上一篇<WPF 4 DataGrid 控件(自定义样式篇)>中,我们掌握了DataGrid 列表头.行表头.行.单元格相关的 ...

  8. WPF 4 DataGrid 控件(进阶篇二)

    原文:WPF 4 DataGrid 控件(进阶篇二)      上一篇<WPF 4 DataGrid 控件(进阶篇一)>中我们通过DataGridTemplateColumn 类自定义编辑 ...

  9. WPF 4 DataGrid 控件(基本功能篇)

    原文:WPF 4 DataGrid 控件(基本功能篇)      提到DataGrid 不管是网页还是应用程序开发都会频繁使用.通过它我们可以灵活的在行与列间显示各种数据.本篇将详细介绍WPF 4 中 ...

随机推荐

  1. 以太网100Mhz频率为什么可以达到带宽1000Mbps

    转: https://wenku.baidu.com/view/353ea8ecb0717fd5370cdc0b.html

  2. Hadoop单机搭建

    单机Hadoop搭建 1.下载hadoop-2.7.3.tar.gz http://www.apache.org/dyn/closer.cgi/hadoop/common/hadoop-2.7.3/h ...

  3. gem Errno::ECONNRESET: Connection reset by peer - SSL_connect

    问题描述 在使用gem安装软件包时,会时常遇到下面的问题: ERROR: While executing gem ... (Gem::RemoteFetcher::FetchError) Errno: ...

  4. Hadoop相关知识整理系列之一:HBase基本架构及原理

    1. HBase框架简单介绍 HBase是一个分布式的.面向列的开源数据库,它不同于一般的关系数据库,是一个适合于非结构化数据存储的数据库.另一个不同的是HBase基于列的而不是基于行的模式.HBas ...

  5. Qt下TCP编程

    一.服务器 1.声明一个QTcpServer对象 QTcpServer* serverListener; 2.new出对象 this->serverListener = new QTcpServ ...

  6. INSPIRED启示录 读书笔记 - 前言

    好的产品具备三个基本条件 价值.可用性.可行性,三者缺一不可 产品经理日常工作 1.人员是指负责定义和开发产品的团队成员的角色和职责 2.流程是指探索.开发富有创意的产品时,反复应用的和成功的实践经验 ...

  7. 斯坦福机器学习视频笔记 Week4 & Week5 神经网络 Neural Networks

    神经网络是一种受大脑工作原理启发的模式. 它在许多应用中广泛使用:当您的手机解释并理解您的语音命令时,很可能是神经网络正在帮助理解您的语音; 当您兑现支票时,自动读取数字的机器也使用神经网络. Non ...

  8. vRA7 Business error “Untrusted certificate chain”

    报错截图: 服务无法注册 第一步:登录vRB 5480页面,取消到vRA的注册 第二部:SSH登录到VRB中,查看bio-ssl.keystore.password. cat /shared/cata ...

  9. Windows Server 2008 架设 Web 服务器教程(图文详解)

    Windows Server 2008 架设 Web 服务器教程(图文详解) 一.安装 IIS 7.0 : 虽然 Windows Server 2008 内置了I IS 7.0,但是默认情况下并没有安 ...

  10. HttpComponents之httpclient

    HttpComponents源码目录:http://www.boyunjian.com/javasrc/org.apache.httpcomponents/httpclient/4.3.4/_/ pa ...