0

My problem is that my task is changing an observable collection that is data-bound to a UI element. my task will report progress and this data will be updated to the ui via the observable collection:

async private void getProductData()
{
    await Task.Run(() => getTableDataFromDb(new Progress<string>(
       data => _prc.Add(new ProductList() { Taxe = data })
       ), "List"));
}
private void getTableDataFromDb(IProgress<string> progress, string tabelName)
{
   //simple exemple
   for(var i =0; i < 100; i++)
     progress.Report( "test"+i);
}

if i run this code i get this error:

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

How can i access the main thread to update this collections? Thanks

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
Arh Hokagi
  • 170
  • 5
  • 21
  • 2
    http://stackoverflow.com/questions/2091988/how-do-i-update-an-observablecollection-via-a-worker-thread – Mike Marynowski Jul 21 '16 at 04:04
  • my app is using async-await not backgroundworker, thanks – Arh Hokagi Jul 21 '16 at 12:00
  • It's the same problem...collection being changed on a background thread. – Mike Marynowski Jul 21 '16 at 12:28
  • @MikeMarynowski: your link is very useful and definitely relevant to the _general_ scenario. However, in this case the OP had already implemented what they thought should address the cross-thread issue -- the `Progress` class is designed for that very purpose and is a great way to handle it using the `async`/`await` pattern. It's just that the implementation wasn't quite right initially, and wasn't working as expected. – Peter Duniho Jul 21 '16 at 15:47
  • Ohhh, sorry, I thought progress was just a class in this project. Never heard of this before, good to know! – Mike Marynowski Jul 21 '16 at 18:55

1 Answers1

3

The problem is that you are creating the Progress<string> object in the wrong thread. It's being created in the same worker thread that's actually doing the work, which means it doesn't capture the correct SynchronizationContext object to marshal the work back to the thread you want.

Instead, you want something like this:

async private void getProductData()
{
    IProgress<string> progress = new Progress<string>(
       data => _prc.Add(new ProductList() { Taxe = data }));

    await Task.Run(() => getTableDataFromDb(progress, "List"));
}

That will create the Progress<string> object in the thread that calls the getProductData(), which is presumably your UI thread. Then when the IProgress<string>.Report() method is called, the delegate invocation will be marshalled back to the UI thread as intended.

Peter Duniho
  • 68,759
  • 7
  • 102
  • 136