BackgroundWorker从循环中进行UI更新

时间:2012-10-20 12:44:00

标签: c# wpf viewmodel backgroundworker observablecollection

  • 我在BackgroundWorker的DoWork中的循环中创建ViewModel对象
  • 我在每次迭代中报告进度,将新对象作为参数传递给ProgressChanged处理程序(它是UI线程的朋友)检索
  • 在该处理程序中,该对象被添加到ObservableCollection中,并且ListBox绑定到它。

MY ViewModel类包含两个字符串属性(Filename和ThumbnailPath),其DataTemplate包含绑定到这些属性的Label和Image。

void bw_DoWork (object sender, DoWorkEventArgs e) {
   List<string> files = e.Argument as List<string>;
   FileInfo fi; int percent;
   for (int i = 0; i < files.Count; i++) {
      FileViewModel newItem = new FileViewModel(files[i]);
      fi = new FileInfo(files[i]);
      percent = i / files.Count * 100;
      bwImportBrowserItems.ReportProgress(percent, newItem);
   }
}

void bw_ProgressChanged (object sender, ProgressChangedEventArgs e) {
    this.observableCollection.Add(e.UserState as FileViewModel);
}

典型数量的项目(30-50)的典型行为:UI冻结约2-3秒;大约一半的项目显示; UI会在较短的时间内再次冻结,其余部分会被添加。

现在我明白从循环中调用UI更新并不是最好的主意 - 我认为调用很频繁,用户界面没有时间回应它们,这就是为什么我们看到UI正在更新&#34;在组&#34;中,同时让它无响应。

我尝试添加Thread.Sleep(500)作为循环的最后一行。这有助于我说明一切正常,因为随着这种减速,项目被添加很好地一个接一个地没有任何无反应。

所以我尝试了不同的睡眠值,Thread.Sleep(25)确定了。这不是理想的,但是完全可以接受,并且非常接近它的样子。

我想在这种情况下询问Thread.Sleep是否是一种常见的解决方法,以及在这种情况下人们采用的一般解决方案是什么:更新UI从背景循环收集而没有任何反应迟钝。 我也提出了一些想法,我很感激你的意见。

我能想到的想法:

  1. 不要经常报告进展 - 将其限制为10次,或者每10次 新项目。
  2. 不要在循环中这样做。创建需要的项目列表 创建。在DoWork主体中,从列表中取出一个项目, 实例化并返回ViewModel实例。在RonWorkerCompleted上, 更新UI,检查我们的列表是否为空,如果没有, 再次运行RunWorkerAsync。

1 个答案:

答案 0 :(得分:1)

  

如果Thread.Sleep是这些情况下的常见解决方法

将它视为最后的手段。您正在增加总处理时间(而不是CPU负载)。

  

1)不要频繁报告ReportProgress - 将其限制为10次,或者每10个新项目。

这是基本的想法。收集新项目并通过ReportProgess发送List。调整列表的大小。

  

2)不要在循环中进行。

可能,但是每个Bgw的1件物品看起来要慢得多。它甚至可能表现出相同的症状。

3)通过ConcurrentQueue去耦。您可以让DoWork填充队列和Dispatcher.Timer可以清空它。还尝试使用该计时器处理批次。您可以调整计时器的优先级和批量大小。