Winform中的ListView控件,其实应用很广泛,资源管理器(我的电脑)就是用的ListView控件。查看方式有很多种,包括大小图标、详细信息等,用来展示数据非常不错。最近用它做项目的时候,遇到个问题,就是加载很多数据的时候非常慢,如果不使用多线程+异步,程序就要卡一段时间。经过一系列的测试,问题还是能解决的,下面用2种方法解决。
介绍方法之前,先看下测试代码,假设有10个组,每个有200个项,每项带固定图标。最原始的方法,一般需要27-30秒之间。这是肯定不能接受的。
DataTable dtItem = new DataTable(); dtItem.Columns.Add("FileName", typeof(string)); dtItem.Columns.Add("FullName", typeof(string)); dtItem.Columns.Add("ImageKey", typeof(string)); dtItem.Columns.Add("GroupName", typeof(string)); for (int i = 1; i <= 10; i++) { string groupName = "group" + i.ToString(); for (int j = 1; j <= 200; j++) { string file = "file-" + j.ToString(); dtItem.Rows.Add(new object[] { groupName + "-" + file, file, "nine_file_mp4", groupName }); } } //最原始方法,耗时:29383毫秒、27320毫秒 FormUIHelper.SetListViewDataSourceEx(this.listview1, dtItem, "FileName", "FileName", "GroupName");
方法1:BeginUpdate和EndUpdate
看这2个方法命名就知道是什么意思,加载ListViewItem项前后使用,相当于告诉ListView控件,我要开始加载数据了。经测试,加载耗时10-12秒。提升60%左右了。
//方法2,耗时:12917毫秒、11815毫秒 this.listview1.BeginUpdate(); FormUIHelper.SetListViewDataSourceEx(this.listview1, dtItem, "FileName", "FileName", "GroupName"); this.listview1.EndUpdate();
方法2:VirtualMode虚方法模式
不得不说,这个方法真是快。同样的数据,加载是几百毫秒(即不到1秒)的时候。这性能提升效果,扛扛的。不过,这个小问题,用这种方式如何实现分组方式?
//方法3,耗时:几百毫秒级 this.myCache = new List<ListViewItem>(); foreach(DataRow drItem in dtItem.Rows) { string priValue = drItem["FileName"] as string; string dispValue = drItem["FullName"] as string; string group = drItem["GroupName"] as string; List<string> listArgs = new List<string>(); listArgs.Add(group + "-" + priValue); listArgs.Add(dispValue); //判断是否存在ImageKey栏目.若存在,则设置图标 string imageKey = null; if (dtItem.Columns.Contains("ImageKey")) { imageKey = drItem["ImageKey"] as string; } //注:虚模式下,ImageKey不可用,要用ImageIndex int imageIndex = FormUIHelper.GetIndexInImageList(this.imageList3, imageKey); //ListViewGroup groupAdd = dicGroup[group]; //ListViewItem lviAdd = new ListViewItem(listArgs.ToArray(), imageKey, groupAdd); ListViewItem lviAdd = new ListViewItem(listArgs.ToArray(), imageKey); lviAdd.Tag = drItem; lviAdd.Name = group + "-" + priValue; lviAdd.Text = group + "-" + dispValue; lviAdd.ImageKey = imageKey; lviAdd.ImageIndex = imageIndex; this.myCache.Add(lviAdd); } this.listview1.ShowGroups = true; this.listview1.VirtualMode = true; this.listview1.VirtualListSize = this.myCache.Count; this.listview1.RetrieveVirtualItem += listview1_RetrieveVirtualItem;
这个事件也要写代码,不然加载失败
private void listview1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { if (this.myCache != null) { e.Item = this.myCache[e.ItemIndex]; } else { //A cache miss, so create a new ListViewItem and pass it back. int x = e.ItemIndex * e.ItemIndex; e.Item = new ListViewItem(x.ToString()); } }
还有一种方法,应该也不错,就是先生成好ListViewItem的列表,然后调用this.listview1.Items.AddRange()方法,效果应该也不差。但肯定没有方法2好。
附SetListViewDataSourceEx方法
/// <summary> /// 加载ListView控件数据源,根据DataTable对象 /// 注:支持分组 /// </summary> /// <param name="listView"></param> /// <param name="dataTable"></param> /// <param name="primaryKey">主键名称</param> /// <param name="displayColumn">显示列名称</param> /// <param name="groupName">分组名称</param> /// <param name="dicColumn">Details时的Column列表</param> public static void SetListViewDataSourceEx(ListView listView, DataTable dataTable, string primaryKey, string displayColumn, string groupName, Dictionary<string, int> dicColumn = null) { //参数检测 if (listView == null) return; if (dataTable == null) return; //先增加列 if (dicColumn == null) { listView.Columns.Clear(); } else { listView.Columns.Clear(); foreach (string col in dicColumn.Keys) { int colWidth = dicColumn[col]; listView.Columns.Add(col, colWidth); } } //判断是否需要分组? if (string.IsNullOrEmpty(groupName)) { listView.Items.Clear(); foreach (DataRow drData in dataTable.Rows) { object primaryValue = drData[primaryKey]; if (StringHelper.ObjectIsNullOrEmpty(primaryValue)) continue; object displayColumnValue = drData[displayColumn]; if (StringHelper.ObjectIsNullOrEmpty(displayColumnValue)) displayColumnValue = ""; //判断是否栏目值存在? ListViewItem[] lviFinds = listView.Items.Find(primaryValue.ToString(), false); if (lviFinds != null && lviFinds.Length > 0) continue; List<string> listArgs = new List<string>(); foreach (DataColumn dc in dataTable.Columns) { object columnValue = drData[dc]; if (StringHelper.ObjectIsNullOrEmpty(columnValue)) columnValue = ""; listArgs.Add(columnValue.ToString()); } //判断是否存在ImageKey栏目.若存在,则设置图标 string imageKey = null; if (dataTable.Columns.Contains("ImageKey")) { imageKey = drData["ImageKey"] as string; } ListViewItem lviAdd = null; if (string.IsNullOrEmpty(imageKey)) { lviAdd = new ListViewItem(listArgs.ToArray()); } else { lviAdd = new ListViewItem(listArgs.ToArray(), imageKey); } lviAdd.Tag = drData; lviAdd.Name = primaryValue.ToString(); lviAdd.Text = displayColumnValue.ToString(); listView.Items.Add(lviAdd); } } else { listView.ShowGroups = true; listView.Groups.Clear(); listView.Items.Clear(); DataTable dtDistinct = DataTableHelper.GetTableByDistinct(dataTable, new string[] { groupName }); foreach (DataRow drDistinct in dtDistinct.Rows) { object value = drDistinct[0]; if (StringHelper.ObjectIsNullOrEmpty(value)) continue; //增加分组对象 ListViewGroup groupAdd = new ListViewGroup(value.ToString()); listView.Groups.Add(groupAdd); DataRow[] drFinds = dataTable.Select(groupName + " = '" + value.ToString() + "'"); foreach (DataRow drFind in drFinds) { object primaryValue = drFind[primaryKey]; if (StringHelper.ObjectIsNullOrEmpty(primaryValue)) continue; object displayColumnValue = drFind[displayColumn]; if (StringHelper.ObjectIsNullOrEmpty(displayColumnValue)) displayColumnValue = ""; //判断是否栏目值存在? ListViewItem[] lviFinds = listView.Items.Find(primaryValue.ToString(), false); if (lviFinds != null && lviFinds.Length > 0) continue; List<string> listArgs = new List<string>(); foreach (DataColumn dc in dataTable.Columns) { object columnValue = drFind[dc]; if (StringHelper.ObjectIsNullOrEmpty(columnValue)) columnValue = ""; listArgs.Add(columnValue.ToString()); } //判断是否存在ImageKey栏目.若存在,则设置图标 string imageKey = null; if (dataTable.Columns.Contains("ImageKey")) { imageKey = drFind["ImageKey"] as string; } ListViewItem lviAdd = null; if (string.IsNullOrEmpty(imageKey)) { lviAdd = new ListViewItem(listArgs.ToArray(), groupAdd); } else { lviAdd = new ListViewItem(listArgs.ToArray(), imageKey, groupAdd); } lviAdd.Tag = drFind; lviAdd.Name = primaryValue.ToString(); lviAdd.Text = displayColumnValue.ToString(); listView.Items.Add(lviAdd); } } } }
相关阅读
文章评论