因为两天我试图解决以下问题: 我有一个WPF控件,其中WrapPanel绑定到ObservableCollection。操作会更改ObservableCollection的内容。内容将加载到BackgroundWorker中。在导致内容更改的操作之后,在foreach循环中需要新内容。问题是内容的加载速度很慢,所以需要一点准备。
我的第一次尝试是等待后台工作,直到IsBusy属性设置为false。但IsBusy的财产在等待期间从未改变过! 第二次尝试是尝试直接从BackgroundWorker操作ObservableCollection。当然没有成功,因为ObservableCollection在另一个线程中而不是BackgroundWorker。
我真的非常了解如何在跨线程范围内操作内容。但它们都没有奏效。使用Dispatcher,“ThreadSafeObservableCollection”,.....
尝试了解决方案有谁能告诉我如何解决这个问题? 是否有一种简单的方法来编辑另一个线程中的UI线程的内容? 或者我如何正确等待BackgroundWorker完成?
修改 但是我怎么能等待BackgroundWorker完成呢?
答案 0 :(得分:8)
BackgroundWorker可以通过两种方式为您提供帮助。
要在BGWorker运行时更新集合,请使用ProgressChanged
事件。此事件的名称具有误导性 - 当您可以更新任务的进度时,您可以使用它实际使用它来通过传递对象来在UI(调用)线程中完成任何操作ProgressChangedEventArgs的UserState属性。
BGWorker完成后也会有一个事件。同样,您可以在RunWorkerCompleted
事件中的RunWorkerCompletedEventArgs的Result属性中将任何信息传回给它。
以下代码来自another thread,我回答了有关BackgroundWorker的问题:
BackgroundWorker bgWorker = new BackgroundWorker();
ObservableCollection<int> mNumbers = new ObservableCollection<int>();
public Window1()
{
InitializeComponent();
bgWorker.DoWork +=
new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged +=
new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.WorkerReportsProgress = true;
btnGenerateNumbers.Click += (s, e) => UpdateNumbers();
this.DataContext = this;
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
progress.Visibility = Visibility.Collapsed;
lstItems.Opacity = 1d;
btnGenerateNumbers.IsEnabled = true;
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
List<int> numbers = (List<int>)e.UserState;
foreach (int number in numbers)
{
mNumbers.Add(number);
}
progress.Value = e.ProgressPercentage;
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
Random rnd = new Random();
List<int> numbers = new List<int>(10);
for (int i = 1; i <= 100; i++)
{
// Add a random number
numbers.Add(rnd.Next());
// Sleep from 1/8 of a second to 1 second
Thread.Sleep(rnd.Next(125, 1000));
// Every 10 iterations, report progress
if ((i % 10) == 0)
{
bgWorker.ReportProgress(i, numbers.ToList<int>());
numbers.Clear();
}
}
}
public ObservableCollection<int> NumberItems
{
get { return mNumbers; }
}
private void UpdateNumbers()
{
btnGenerateNumbers.IsEnabled = false;
mNumbers.Clear();
progress.Value = 0;
progress.Visibility = Visibility.Visible;
lstItems.Opacity = 0.5;
bgWorker.RunWorkerAsync();
}
答案 1 :(得分:5)
将ObservableCollection.Add
推送到UI线程的调度程序应该有效。
App.Current.Dispatcher.Invoke(new Action(() => collection.Add(item)));
答案 2 :(得分:2)
您可以在BackgroundWorker.RunWorkerCompleted事件处理程序中更新您的集合。它运行在你启动它的同一个同步上下文中,这通常是一个UI线程,因此你可以安全地使用任何与UI相关的东西。
答案 3 :(得分:1)
BackGroundWorker在事件结束时触发事件。 我在类似情况下一直在做的是:
我有一个不是observablecollecion的列表
所以与集合的所有交互都在同一个线程上 - 复制通常不是昂贵的部分。