views:

2044

answers:

2

I have the following code for population a ListView from a background thread (DoWork calls the PopulateThread method):

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<ServiceCallEntity> resultCollection = this.ApplyFilter();

        // Get the current Ids
        List<Guid> previousIdList = this.GetUniqueIdList(listView);
        List<Guid> usedIdList = new List<Guid>();

        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();

    }

}

Unfortunately, this causes the UI to freeze whilst in the foreach... is there any way to update/populate the ListView without it freezing the main UI?

A: 

Pump the events manually with

Application.DoEvents();
Jared Updike
Perfect, thank you for the fast answer!
GenericTypeTea
-1, Sorry, but BgWorker->Invoke->DoEvents? Looks like patching one hole with another.
Henk Holterman
Good point, though I will say: you can get pretty far with Application.DoEvents instead of multiple threads if all you want is a moderately responsive GUI during computationally intensive tasks, just pump the message queue yourself. A lot of this machinery is just extra complexity with little gain (unless of course you really need the threads). For example, with multiple threads, debugging + exceptions = FAIL. But as you said in your answer this isn't multithreaded code at all. Concurrency in imperative languages is a PITA. Stay away unless you really need it.
Jared Updike
Jared, if you meant DoEvents instead of a BgWorker then I missed that. DoEvents is tricky though.
Henk Holterman
+7  A: 

A) You probably don't need to use this.Invoke and instead use this.BeginInvoke. Invoke blocks the current thread.

B) You don't need to define your own delegates you can use MethodInvoker

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

It's much cleaner :)

Qberticus
Thanks, I shall make some improvements.
GenericTypeTea