views:

117

answers:

3

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?

+1  A: 

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
In that article, they write Dispatcher.Invoke(DispatcherPriority.Normal, new Action<string>(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?
Mark
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.
Mark
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!
Mark
Try DispatcherPriority.Background or other lower priorities
Veer
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 :)
Mark
+2  A: 

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.

Mystere Man
+1, that sounds promising too.
John Weldon
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..
Mark
There's a bunch of samples here: http://rxwiki.wikidot.com/101samples
Mystere Man
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.
Mark
+1  A: 

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
Doesn't explain how to *Dequeue* elements from a collection :(
Mark
I re-read your question and I am unsure what exactly is your dequeuing problem. Can you explain in more detail?
wpfwannabe
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.
Mark