since two days I am trying to solve the following problem: I have a WPF control where a WrapPanel is bound to an ObservableCollection. An action changes the content of the ObservableCollection. The content is loaded in a BackgroundWorker. Immediately after the action that caused the content change, the new content is needed in a foreach-loop. The problem is that the loading of the content is slow, so it needs a bit to get ready.
My first attempt was to wait for the backgroundworker until the IsBusy property is set to false. But the IsBusy property never changed while waiting! Second attempt was to try to manipulate the ObservableCollection directly from the BackgroundWorker. Of course no success because the ObservableCollection is in another thread than the BackgroundWorker.
I read really really much about how to manipulate content in cross-thread-wide. But none of them worked. Tried solutions with Dispatcher, "ThreadSafeObservableCollection", .....
Might anyone tell me how I can solve that problem? Is there a simple way to edit content of the UI thread within another thread? Or how do I wait correctly for the BackgroundWorker to get finished?
EDIT: But how can I wait for the BackgroundWorker to get finished???
The BackgroundWorker can help you in two ways.
To update the collection while the BGWorker is running, use the ProgressChanged
event. The name of this event is misleading - while you can update the progress of a task, you can use actually use it for anything that needs to be done in the UI (calling) thread by passing an object using the UserState property of the ProgressChangedEventArgs.
The BGWorker also has an event when it finishes. Again, you can pass any information back to it that you'd like in the Result property of the RunWorkerCompletedEventArgs in the RunWorkerCompleted
event.
The following code is from another thread that I answered about 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();
}
Pushing the ObservableCollection.Add
to the dispatcher of the UI thread should work.
App.Current.Dispatcher.Invoke(new Action(() => collection.Add(item)));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With