使用BackgroundWorker更新UI而不冻结……?

我有以下代码用于从后台线程填充ListView(DoWork调用PopulateThread方法):

delegate void PopulateThreadCallBack(DoWorkEventArgs e); private void PopulateThread(DoWorkEventArgs e) { if (this.InvokeRequired) { PopulateThreadCallBack d = new PopulateThreadCallBack(this.PopulateThread); this.Invoke(d, new object[] { e }); } else { // Ensure there is some data if (this.DataCollection == null) { return; } this.Hide(); // Filter the collection based on the filters List resultCollection = this.ApplyFilter(); // Get the current Ids List previousIdList = this.GetUniqueIdList(listView); List usedIdList = new List(); foreach (ServiceCallEntity record in resultCollection) { if (e.Cancel) { this.Show(); return; } else { // Get the top level entities UserEntity userEntity = IvdSession.Instance.Collection.GetEngineerEntity(record.UserId); AssetEntity assetEntity = IvdSession.Instance.Collection.GetAssetEntity(record.AssetId); SiteEntity siteEntity = IvdSession.Instance.Collection.GetSiteEntity(record.SiteId); FaultEntity faultEntity = IvdSession.Instance.Collection.GetFaultEntity(record.FaultId); if (siteEntity == null || userEntity == null || faultEntity == null) { continue; } else { // Get the linked entities RegionEntity regionEntity = IvdSession.Instance.Collection.GetRegionEntity(siteEntity.RegionId); StatusEntity statusEntity = IvdSession.Instance.Collection.GetStatusEntity(record.ServiceCallStatus.StatusId); ListViewItem item = new ListViewItem(siteEntity.SiteName); item.SubItems.Add(siteEntity.Address); item.Tag = record; item.SubItems.Add(regionEntity.Description); // Handle if an Asset is involved if (record.AssetId > 0) item.SubItems.Add(assetEntity.AssetDisplay); else item.SubItems.Add("N/A"); item.SubItems.Add(faultEntity.Description); item.SubItems.Add(userEntity.UserDisplay); item.SubItems.Add("TODO: Claimed By"); item.SubItems.Add(record.DateTimeStamp.ToString()); IvdColourHelper.SetListViewItemColour(item, false); this.PopulateItem(item, ref usedIdList); } } } // Clean up the grid this.CleanListView(previousIdList, usedIdList); // Only autosize when allowed and when there are some items in the ListView if (this.AllowAutoSize && listView.Items.Count > 0) { rsListView.AutoSizeColumns(listView); this.AllowAutoSize = false; } this.Show(); } } 

不幸的是,这会导致UI在foreach中冻结…有没有办法更新/填充ListView而不冻结主UI?

A)你可能不需要使用this.Invoke而是使用this.BeginInvoke。 调用阻止当前线程。

B)您不需要定义自己的代理,您可以使用MethodInvoker

 if(this.InvokeRequired) { this.BeginInvoke(new MethodInvoker(() => PopulateThread(e))); return; } 

它更干净:)

您正在使用Control.Invoke执行几乎所有内容,这意味着此代码根本不是multithreading的。

正确的方法(涉及Backgroundworker)将使用UpdateProgress事件来添加元素。 它已经同步了。

但是,由于您在此过程中隐藏了控件(或者是Form?),因此您可以构建List并在完成时将其添加到Listview中。 这段代码不应该花很长时间。

或者某种组合,在更新事件中添加小列表。 我想知道隐藏/显示的智慧,我希望这只会让UI闪烁。 将它们保留或用SuspendLayout / Resumelayout替换。

用手动泵送事件

 Application.DoEvents();