2

I'm writing a download manager using C#/WPF, and I just encountered this error:

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

The basic flow of my program is that a few web pages/downloads are enqueued at the start, and then they're downloaded asynchronously. When an HTML page has completed downloading, I parse it and look for more stuff to download, then enqueue it directly from within the worker thread.

I get that error when trying to send out the CollectionChanged event on my customized queue class. However, I need to fire that event so that the GUI can get updated.

What are my options?

mpen
  • 272,448
  • 266
  • 850
  • 1,236

3 Answers3

2

You might want to look into the Reactive Extensions from Microsoft, they're designed for just this sort of thing. They create "observable collections" and are useful in paralell and multi-core scenarios.

UPDATE:

You may also find the SyncronizationContext useful as well. That's a Windows Forms sample, but there are variations for WPF as well.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • Looks interesting... but I have no idea to use it, and there doesn't seem to be any documentation aside from a few videos which don't directly address my issue. Hrm.. – mpen May 07 '10 at 05:07
  • From what I grasp, I should basically convert an enumerable into an observable... but I really have no idea how that works. Observables are supposed to fire events when an item is changed/removed, but enumerables don't do that... collections do. How is Rx clever enough to know how to fire the appropriate events then? Baffles me. – mpen May 09 '10 at 07:16
1

You just need to make sure that you use the Dispatcher for the relevant thread to invoke any code that updates the collection, or fires the event.

http://msdn.microsoft.com/en-us/magazine/cc163328.aspx

Update

The Dispatcher is a property on all of the classes that derive from DispatcherObject, which includes all DependencyObjects, Visuals, etc.

So, your GUI objects will all have a Dispatcher property.

John Weldon
  • 39,849
  • 11
  • 94
  • 127
  • In that article, they write Dispatcher.Invoke(DispatcherPriority.Normal, new Action(SetStatus), "From Other Thread"); which looks like it might be what I need to do for queuing. But where is this `Dispatcher` defined? And how does it know what thread to sync with? – mpen May 07 '10 at 03:45
  • Well...it gets slightly farther before crashing now. Turns out my `DownloadManager` itself does some cross-thread enqueues, but it's created through the XAML and I don't have access to a dispatcher from inside it. Not quite sure what to do now. – mpen May 07 '10 at 05:05
  • Actually, if I derive from DispatcherObject and then put `private Dispatcher dispatcher = Dispatcher.CurrentDispatcher;` in my class, and use that it runs for a good 10 seconds before it crashes w/out exception or stack trace! Hurray! – mpen May 07 '10 at 05:16
  • Try DispatcherPriority.Background or other lower priorities – Amsakanna May 07 '10 at 06:49
  • Naw, I'm sure there's a problem somewhere in my code... and I don't want it to be lower priority. It's an important operation :) – mpen May 08 '10 at 02:12
1

Whenever I had to deal with collection change notifications across threads, I always turned to How can I propagate changes across threads blog article for help.

I am not sure from the context of your question if this can indeed help you, but if your collection is something you directly control, you can benefit from this.

wpfwannabe
  • 14,587
  • 16
  • 78
  • 129
  • Doesn't explain how to *Dequeue* elements from a collection :( – mpen May 08 '10 at 02:12
  • I re-read your question and I am unsure what exactly is your dequeuing problem. Can you explain in more detail? – wpfwannabe May 08 '10 at 09:13
  • The problem was that in that blog post, she only covers methods that return `void`. `BeginInvoke` doesn't work so well when you actually need to return a value. – mpen May 09 '10 at 07:13