Использование BackgroundWorker для обновления пользовательского интерфейса без зависаний ...? - PullRequest
3 голосов
/ 12 июня 2009

У меня есть следующий код для заполнения 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<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();

    }

}

К сожалению, это приводит к зависанию пользовательского интерфейса, в то время как в foreach ... есть ли способ обновить / заполнить ListView без замораживания основного пользовательского интерфейса?

Ответы [ 3 ]

11 голосов
/ 12 июня 2009

A) Вам, вероятно, не нужно использовать this.Invoke и вместо этого использовать this. BeginInvoke. Invoke блокирует текущий поток.

B) Вам не нужно определять своих собственных делегатов, вы можете использовать MethodInvoker

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

Это намного чище:)

5 голосов
/ 12 июня 2009

Вы используете Control.Invoke для выполнения практически всего, что означает, что этот код не является многопоточным.

Правильный способ (с участием Backgroundworker) будет использовать событие UpdateProgress для добавления элементов. Это уже синхронизировано.

Но так как вы скрываете элемент управления (или это форма?) Во время этого процесса, вы также можете создать список и по завершении добавить его в список. Этот кусок кода не должен занимать много времени.

Или какая-то комбинация, добавление небольших списков в событие обновления. И я удивляюсь мудрости Hide / Show, я ожидаю, что это просто заставит пользовательский интерфейс мерцать. Оставьте их или замените на SuspendLayout / Resumelayout.

0 голосов
/ 12 июня 2009

Откачайте события вручную с помощью

Application.DoEvents(); 
...