How to update ObservableCollection from inside BackgroundWorker using MVVM?

since two days I'm trying to solve the following problem: I have a WPF control where the WrapPanel is bound to an ObservableCollection. The action modifies the contents of the ObservableCollection. Content is uploaded to BackgroundWorker. Immediately after the action that caused the content change, new content is needed in the foreach loop. The problem is that content loading is slow, so it takes a little to prepare it.

My first attempt was to wait for the flashlight until the IsBusy property was set to false. But the IsBusy property never changed while waiting! The second attempt was to try to manipulate the ObservableCollection directly from BackgroundWorker. Of course, no success, because the ObservableCollection is in a different thread than BackgroundWorker.

I really read a lot about how to manipulate content in a cross-stream. But none of them worked. I tried the solutions with the dispatcher, "ThreadSafeObservableCollection", .....

Can someone tell me how can I solve this problem? Is there an easy way to edit the contents of a user interface thread in another thread? Or how to properly wait for BackgroundWorker to complete?

EDIT: But how can I wait for BackgroundWorker to complete ???

+5
source share
4 answers

BackgroundWorker can help you in two ways.

To update the collection while BGWorker is running, use the event ProgressChanged. The name of this event is misleading - while you can update the progress of a task, you can actually use it for everything you need to do in the user interface (call) thread by passing an object using the UserState property for ProgressChangedEventArgs.

BGWorker , . , , Result RunWorkerCompletedEventArgs RunWorkerCompleted.

, 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();
}
+8

ObservableCollection.Add .

App.Current.Dispatcher.Invoke(new Action(() => collection.Add(item)));
+4

BackgroundWorker.RunWorkerCompleted. , , , UI .

+2

BackGroundWorker , . , , :

, collecion

  • = false spinner
  • DoWork
  • RunWorkerCompleted spinner

- .

+1

All Articles