我意识到我必须对 ListView 从中收集项目的集合进行排序:
ListView listCollection = new ListView();
但这似乎不起作用,除非将 ListView 作为 GUI 控件添加到表单中,这反过来又使得添加项目非常慢,因此为什么我必须首先在我的 GUI-ListView 上使用 VirtualMode .
任何人都知道如何解决这个问题或指出我正确的方向吗?
我意识到我必须对 ListView 从中收集项目的集合进行排序:
ListView listCollection = new ListView();
但这似乎不起作用,除非将 ListView 作为 GUI 控件添加到表单中,这反过来又使得添加项目非常慢,因此为什么我必须首先在我的 GUI-ListView 上使用 VirtualMode .
任何人都知道如何解决这个问题或指出我正确的方向吗?
基本上,您需要对数据泵本身应用排序。
我在 Google 上快速搜索了listview sort virtualmode。第一个结果是这个页面,上面的引用来自哪里。
例如,如果您的数据源是 DataView,则对其应用排序而不是 ListView。
如果只是添加项目时的性能问题,我会按照barism的建议进行;使用 BeginUpdate/EndUpdate 而不是 VirtualMode。
try {
listView1.BeginUpdate();
// add items
}
finally {
listView1.EndUpdate();
}
如果您使用虚拟模式,则必须对基础数据源进行排序。您可能已经发现,ListViewItemSorter 对虚拟列表没有任何作用。
如果您使用的是非虚拟列表视图,您还可以使用 AddRange(),它比一系列 Add() 快得多——除了使用已经描述的 BeginUpdate/EndUpdate 之外。
ObjectListView(一个围绕.NET WinForms ListView 的开源包装器)已经使用所有这些技术来提高自己的速度。它比普通的 ListView 有了很大的改进。它同时支持普通模式和虚拟模式列表视图,并使它们更易于使用。例如,排序是完全自动处理的。
我在现有项目中切换 VirtualMode True 时遇到了同样的问题,但解决方案非常简单:
第一步,我填充 ListViewItem 列表,而不是 ListView.Items 集合:
private List<ListViewItem> _ListViewItems;
然后我实现了 RetrieveVirtualItem 方法
private void mLV_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = _ListViewItems[e.ItemIndex];
}
最后,我使用之前使用的相同类对 ListViewItem 列表进行排序,我只需要更改基类
_ListViewItems.Sort((System.Collections.Generic.IComparer<ListViewItem>)new ListViewItemComparer(new int[] { e.Column }, mLV.Sorting));
这是我的 IComparer 类实现:
class ListViewItemComparer : System.Collections.Generic.IComparer<ListViewItem>
{
int[] mColonne;
private System.Windows.Forms.SortOrder order;
public ListViewItemComparer(int[] mCols)
{
this.mColonne = mCols;
this.order = System.Windows.Forms.SortOrder.Ascending;
}
public ListViewItemComparer(int[] mCols, System.Windows.Forms.SortOrder order)
{
this.mColonne = mCols;
this.order = order;
}
public int Compare(ListViewItem x, ListViewItem y)
{
int returnVal = -1;
foreach (int mColonna in mColonne)
{
double mNum1;
double mNum2;
String mStr1 = "";
String mStr2 = "";
if ((x.SubItems[mColonna].Text == "NULL") && (x.SubItems[mColonna].ForeColor == Color.Red))
{
mStr1 = "-1";
}
else
{
mStr1 = x.SubItems[mColonna].Text;
}
if ((y.SubItems[mColonna].Text == "NULL") && (y.SubItems[mColonna].ForeColor == Color.Red))
{
mStr2 = "-1";
}
else
{
mStr2 = y.SubItems[mColonna].Text;
}
if ((double.TryParse(mStr1, out mNum1) == true) && (double.TryParse(mStr2, out mNum2) == true))
{
if (mNum1 == mNum2)
{
returnVal = 0;
}
else if (mNum1 > mNum2)
{
returnVal = 1;
}
else
{
returnVal = -1;
}
}
else if ((double.TryParse(mStr1, out mNum1) == true) && (double.TryParse(mStr2, out mNum2) == false))
{
returnVal = -1;
}
else if ((double.TryParse(mStr1, out mNum1) == false) && (double.TryParse(mStr2, out mNum2) == true))
{
returnVal = 1;
}
else
{
returnVal = String.Compare(mStr1, mStr2);
}
if (returnVal != 0)
{
break;
}
}
// Determine whether the sort order is descending.
if (order == System.Windows.Forms.SortOrder.Descending)
{
// Invert the value returned by String.Compare.
returnVal *= -1;
}
return returnVal;
}
}
希望这会帮助你。
您是否尝试过 beginupdate() 和 endupdate()?当您使用 beginupdate/endupdate 时添加数据要快得多。(当您调用 beginupdate 时,listview 直到您调用 endupdate 才会绘制)
listView1.BeginUpdate();
for (int i = 0; i < 20000; i++)
{
listView1.Items.Add("abdc", 1);
}
listView1.EndUpdate();
对于非常大的列表,虚拟模式 ListView 是肯定的答案。在非虚拟模式下,它似乎绘制整个列表,然后将其剪辑到视图中,而在虚拟模式下,它只是在视图中绘制列表。在我的情况下,列表是 40K+ 记录。在非虚拟模式下,更新 ListView 可能需要几分钟时间。在虚拟模式下,它是瞬时的。
如前所述,要对列表进行排序,您必须对底层数据源进行排序。那是容易的部分。您还需要强制刷新显示,这不会自动完成。您可以使用 ListView.TopItem.Index 在底层数据源中查找与排序前的 Virtual ListView 的顶行相对应的索引。还有一个 API 调用返回 ListView 中的显示行数,您可以将其实现为 C# 函数,如下所示:
public const Int32 LVM_GETCOUNTPERPAGE = 0x1040;
public static int GetListViewRows( ListView xoView )
{
return (int)WindowsMessage.SendMessage( xoView.Handle, LVM_GETCOUNTPERPAGE, 0, 0 );
}
这将让您计算必须更新的范围。剩下的唯一问题是如何协调现有显示与数据排序后将出现的显示。如果你想在第一行保留相同的数据元素,你必须有一些机制可以在新排序的列表中找到它的新索引,这样你就可以在顶部位置替换它 - 这基本上等同于 SQL IDENTITY .