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);
}
}
}
}
相关阅读

文章评论