0

I am trying to load some data in a separate thread, then add the loaded data to an ObservableCollection and update the view through ba binding.

First, I was doing the following:

public OverviewViewModel()
{
    Thread thread = new Thread(new ThreadStart(delegate
    {
        TheTVDB theTvdb = new TheTVDB();
        foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter"))
        {
            this.Overview.Add(tvSeries);
        }
    }));
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
}

This gave the following error:

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

So I read on this forum that I should use the Dispatcher, so I put this.Overview.Add(tvSeries) into a call to the Dispatcher.

Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate
{
    this.Overview.Add(tvSeries);
},
DispatcherPriority.Normal);

Now, it doesn't crash anymore but the view is not updated. Nothing happens, the view is just empty. I have tested the functionality by running it on the main thread. Then the view is updated correctly.

Does anyone know why the view is not updated and how I can fix this?

UPDATE

The below approach seems to work and it seems to do everything asynchronously. Can anyone confirm that this is the right approach for doing things asyncronously?

Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate
{
    TheTVDB theTvdb = new TheTVDB();
    foreach (TVSeries tvSeries in theTvdb.SearchSeries("Dexter"))
    {
        this.Overview.Add(tvSeries);
    }
}),
DispatcherPriority.Background);
simonbs
  • 7,932
  • 13
  • 69
  • 115
  • You probably have, but just a check - do you have `INotifyPropertyChanged` implemented? – LukeHennerley Nov 02 '12 at 09:10
  • That has nothing to do with the `ObservableCollection` raising the `CollectionChanged` event... – khellang Nov 02 '12 at 09:12
  • Please consider adding a tag for either WPF or Silverlight (I guess). (And perhaps one for `Dispatcher`.) – stakx - no longer contributing Nov 02 '12 at 09:15
  • 1
    Is there any difference if you use `Invoke` instead of the asynchronous `BeginInvoke` like this: `Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new Action(() => this.Overview.Add(tvSeries)));`? – khellang Nov 02 '12 at 09:16
  • @LukeHennerley I do have the `INotifyPropertyChanged` correctly implemented. It works fine if I don't use another thread. @stakx You're right. I just added the tags. This is not Silverlight, though :-) @khellang When using `Invoke` instead of `BeginInvoke`, I get the following error: `This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.` – simonbs Nov 02 '12 at 09:21

3 Answers3

1

This is because you will need to tell WPF Dispatcher to handle the threading activity.

Check this article for more of the core details, and this one for some additional examples.

The reading can be a bit heavy, but if you're working with WPF, it's well worth learning about how the Dispatcher works.

EDIT: The second article actually explicitly mentions your problem.

Lastly, don't forget that ObservableCollection will only fire INPC events for add / remove actions, and not individual element changes. For that you'll need to implement INPC on the underlying items themselves.

Nick
  • 2,285
  • 2
  • 14
  • 26
  • I followed the approach from the second article and it seems to work! I have updated my original question to show how I do it now. Basically, I don't create any threads myself. Instead I only use the dispatcher. Can you confirm that this is the right approach for doing things asynchronously? – simonbs Nov 02 '12 at 10:10
  • Simon: Yes mate, that's the correct method. You're simply allowing the dispatcher to control the thread management, rather than spooling them yourself, which keeps the whole thing happy! – Nick Nov 02 '12 at 10:21
0

I would go for a thread-safe ObservableCollection<T> so you don't have to marshal the calls every time :)

Check out Sasha Barber's implementation: ThreadSafeObservableCollection « Sacha's blog.

khellang
  • 17,550
  • 6
  • 64
  • 84
  • It would be great if I could just use a thread safe observable collection. That would definitely be easier but when using Sacha Barbers implementation, I get the following error: `Must create DependencySource on same Thread as the DependencyObject.` I am unsure why this happens. I have removed the dispatcher. – simonbs Nov 02 '12 at 09:47
0

also try to change the method from BeginInvoke to Invoke.

edit

try it as:

this.Dispatcher.Invoke(new Action(()=>
{
    this.Overview.Add(tvSeries);
});
Tono Nam
  • 34,064
  • 78
  • 298
  • 470
  • I tried that but it gives me the following error: `This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.` – simonbs Nov 02 '12 at 09:28
  • Also make sure that your observableCollection is a public property and not a private field. – Tono Nam Nov 02 '12 at 09:48
  • Using the dispatcher like you do in the answer, throws the same exception as the one in my first comment. The ObservableCollection is private. However, you write `this.Dispatcher´ which requires me to initialize the dispatcher but the dispatcher does not have any constructor. Instead, I used `Dispatcher.CurrentDispatcher`. – simonbs Nov 02 '12 at 09:52
  • The variable has to be a property and public for it to update on the view when raising the INotifyPropertyChange event. ObservableCollection does that for you but you need to make sure that variable is public. Also what is the `this` keyworkord refering too? if you have a reference to the view (UserControl or window) then you should be able to call the Dispatcher.Invoke method on the view. – Tono Nam Nov 02 '12 at 14:02