這是我第一次發佈在StackOverflow上,所以請原諒下面的亂碼格式。
爲了防止在更新ListView時鎖定窗體,可以使用下面我寫的方法來解決此問題。
注意:如果您希望用超過20,000個項目填充ListView,則不應使用此方法。如果您需要向ListView添加20多個項目,請考慮以虛擬模式運行ListView。
public static async void PopulateListView<T>(ListView listView, Func<T, ListViewItem> func,
IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
{
if (listView != null && listView.IsHandleCreated)
{
var conQue = new ConcurrentQueue<ListViewItem>();
// Clear the list view and refresh it
if (listView.InvokeRequired)
{
listView.BeginInvoke(new MethodInvoker(() =>
{
listView.BeginUpdate();
listView.Items.Clear();
listView.Refresh();
listView.EndUpdate();
}));
}
else
{
listView.BeginUpdate();
listView.Items.Clear();
listView.Refresh();
listView.EndUpdate();
}
// Loop over the objects and call the function to generate the list view items
if (objects != null)
{
int objTotalCount = objects.Count();
foreach (T obj in objects)
{
await Task.Run(() =>
{
ListViewItem item = func.Invoke(obj);
if (item != null)
conQue.Enqueue(item);
if (progress != null)
{
double dProgress = ((double)conQue.Count/objTotalCount) * 100.0;
if(dProgress > 0)
progress.Report(dProgress > int.MaxValue ? int.MaxValue : (int)dProgress);
}
});
}
// Perform a mass-add of all the list view items we created
if (listView.InvokeRequired)
{
listView.BeginInvoke(new MethodInvoker(() =>
{
listView.BeginUpdate();
listView.Items.AddRange(conQue.ToArray());
listView.Sort();
listView.EndUpdate();
}));
}
else
{
listView.BeginUpdate();
listView.Items.AddRange(conQue.ToArray());
listView.Sort();
listView.EndUpdate();
}
}
}
if (progress != null)
progress.Report(100);
}
您不必提供IProgress對象,只需使用null,該方法也可以正常工作。
下面是該方法的示例用法。
首先,定義一個包含ListViewItem的數據的類。
public class TestListViewItemClass
{
public int TestInt { get; set; }
public string TestString { get; set; }
public DateTime TestDateTime { get; set; }
public TimeSpan TestTimeSpan { get; set; }
public decimal TestDecimal { get; set; }
}
然後,創建一個返回數據項的方法。此方法可以查詢數據庫,調用Web服務API或其他任何方法,只要它返回類類型的IEnumerable即可。
public IEnumerable<TestListViewItemClass> GetItems()
{
for (int x = 0; x < 15000; x++)
{
yield return new TestListViewItemClass()
{
TestDateTime = DateTime.Now,
TestTimeSpan = TimeSpan.FromDays(x),
TestInt = new Random(DateTime.Now.Millisecond).Next(),
TestDecimal = (decimal)x + new Random(DateTime.Now.Millisecond).Next(),
TestString = "Test string " + x,
};
}
}
最後,在ListView所在的窗體上,您可以填充ListView。爲了演示目的,我使用表單的Load事件來填充ListView。更可能的是,您會希望在表單上的其他位置執行此操作。
我已經包含了從我的類的一個實例TestListViewItemClass生成ListViewItem的函數。在生產場景中,您可能需要在其他地方定義該功能。
private async void TestListViewForm_Load(object sender, EventArgs e)
{
var function = new Func<TestListViewItemClass, ListViewItem>((TestListViewItemClass x) =>
{
var item = new ListViewItem();
if (x != null)
{
item.Text = x.TestString;
item.SubItems.Add(x.TestDecimal.ToString("F4"));
item.SubItems.Add(x.TestDateTime.ToString("G"));
item.SubItems.Add(x.TestTimeSpan.ToString());
item.SubItems.Add(x.TestInt.ToString());
item.Tag = x;
return item;
}
return null;
});
PopulateListView<TestListViewItemClass>(this.listView1, function, GetItems(), progress);
}
在上面的例子中,我創建的窗體的構造函數的IProgress對象是這樣的:
progress = new Progress<int>(value =>
{
toolStripProgressBar1.Visible = true;
if (value >= 100)
{
toolStripProgressBar1.Visible = false;
toolStripProgressBar1.Value = 0;
}
else if (value > 0)
{
toolStripProgressBar1.Value = value;
}
});
我使用的項目填充一個ListView很多次,我們被填充起來的這種方法到ListView中的12,000個項目,並且速度非常快。主要的是你需要從數據庫完全構建你的對象,然後你甚至可以觸摸ListView進行更新。
希望這是有幫助的。
我在下面包含了該方法的異步版本,該版本調用了此帖子頂部顯示的主要方法。
public static Task PopulateListViewAsync<T>(ListView listView, Func<T, ListViewItem> func,
IEnumerable<T> objects, IProgress<int> progress) where T : class, new()
{
return Task.Run(() => PopulateListView<T>(listView, func, objects, progress));
}
凍結意味着別的東西:它意味着對象(在這種情況下是元素的集合)在凍結時不會改變。在這種情況下,你馬上修改它! – 2010-07-21 12:16:07
凍結只是一個術語,用於解釋我的要求 – 2010-07-21 12:26:22