WinForm的ListView在加載大量數據時會出現閃爍的問題,同時數據加載很慢。如果你的列表中有超過千條的數據且不做特殊處理還是用普通的ListView.Items.Add(),估計你的用戶得抱怨了。
下面說下解決方法:
1、使用listView1.Items.AddRange()代替Add
這種方法需要先將數據放入一個緩存數組中,然后調用AddRange一次性加入ListView中,同時可以用一個計數器記錄一次性加入緩存的數量,如下:
listView1.Items.Clear();
if (vList.Count > 0)
{
int indexI = 0;
List<ListViewItem> listBuffer = new List<ListViewItem>();
foreach (var item in vList)
{
ListViewItem li = new ListViewItem();
li.ImageIndex = 0;
li.SubItems[0].Text = item.Name;
li.Tag = item;
li.ForeColor = item.Status == 0 ? Color.Green : Color.Red;
listBuffer.Add(li);
if (indexI++ % 1000 == 0)
{
listView1.Items.AddRange(listBuffer.ToArray());
listBuffer.Clear();
}
if (indexI % 50 == 0)
{
Application.DoEvents();
}
}
listView1.Items.AddRange(listBuffer.ToArray());
}
這樣可以減少ListView閃爍的次數,數據量不是很大時有效果。
2、自定義ListView類
下面這個類在網上廣為流傳,雖然解決了ListView閃爍的問題,但是在打開速度上和原來沒什么區別,同時帶來一個問題就是如果程序切換到別的ListView上,數據還會繼續忘原來的ListView中添加,直到數據全面添加完成。
public class ListViewLargeData : System.Windows.Forms.ListView
{
public ListViewLargeData()
{
this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.EnableNotifyMessage, true);
}
protected override void OnNotifyMessage(Message m)
{
if (m.Msg != 0x14)
{
base.OnNotifyMessage(m);
}
}
}
3、開啟ListView的VirtualMode模式
此方式也是本文重點推薦的方式,可以實現不閃爍效果,而且打開速度很快。往往好的效果帶來的問題是代碼較為復雜,具體寫法可以看官方示例http://msdn.microsoft.com/zh-cn/library/system.windows.forms.listview.virtualmode.aspx,或者http://www.cnblogs.com/hcfalan/archive/2008/07/08/1238493.html,下面說下需要注意的幾點:
(1)必須設置VirtualMode為true並設置VirtualListSize大小
listView1.VirtualMode = true;
listView1.VirtualListSize = bufferItems.Count;
listView1.RetrieveVirtualItem += new RetrieveVirtualItemEventHandler(listView_RetrieveVirtualItem);
(2)綁定該事件為ListView計算Item
void listView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = m_hListViewItems[e.ItemIndex];
}
(3)如果中間更新了數據需要重新設置VirtualListSize,並調用Invalidate()方法(此方法並非必須請高手指點)。
listView1.VirtualListSize = bufferItems.Count;
listView1.Invalidate();
(4)禁用selectedItem,在該模式下使用selectedItem將產生異常,可以用下面方法代替
private List<ListViewItem> FindSelectedAll()
{
List<ListViewItem> r = new List<ListViewItem>();
foreach (int item in listView1.SelectedIndices)
{
r.Add(bufferItems[item]);
}
return r;
}
