0

My question is quite simple I have a class which contains a (private) object of type ObservableCollection<T> and now I'd like to run some threads (in sequence, thus the ThreadPool and not BackgroundWorkers) which add new items to the ObservableCollection<T>.

If the ObservableCollection<T> would be, let's say, a ProgressBar, all would be quite easy (-> Invoke) but I have no idea how to invoke the ObservableCollection<T> object.

My goal is to have something like a BackgroundWorker which can be run sequence.

Thank you in advance.

Dänu
  • 5,791
  • 9
  • 43
  • 56

4 Answers4

5

I recommend using Task objects for the background operations and queueing updates to the UI thread using TaskScheduler.FromCurrentSynchronizationContext.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I took a look at http://msdn.microsoft.com/en-us/library/dd997394.aspx#Y500 but this describes how to use tasks in parallel, how can I "dynamically" generate a new task (i.e. on each button click) and put them in the queue? – Dänu Aug 15 '11 at 16:23
  • `Task.Factory.StartNew` creates a new task. Remember that there are two tasks involved: one doing the background operation and another one doing the actual update to the `ObservableCollection`; only the "update" task should use the `TaskScheduler`. – Stephen Cleary Aug 15 '11 at 16:43
2

Let's say you want to add a StatusItem item to an observable collection. In the .cs file of the user control, you might declare it like this:

ObservableCollection<StatusItem> _listStatus = 
       new ObservableCollection<StatusItem>();

You can then declare a method to add items like so:

private void AddStatus(StatusItem item, bool useDispatcher)
{
    if (useDispatcher) {
        Dispatcher.BeginInvoke(DispatcherPriority.Send, 
           new ThreadStart(() => AddStatus(item, false)));
        return;
    }

    if (_listStatus.Count > MaxItemsToHoldInList) {
        // maybe you have a more efficient way to do this
        for (int i = 0; i < BlockOfItems; i++ ) _listStatus.RemoveAt(0);
    }

    _listStatus.Add(item);

    if (_scrollIntoView) listStatus.ScrollIntoView(item);
}

I've taken a few liberties, so maybe you don't need all of this. Basically, if you want to call this method from inside a worker thread, you must use the dispatcher to postpone your modification until it hits the user thread (which sounds like what you want to do). You probably already figured out that if you weren't using a dispatcher thread that your updates weren't making it to the GUI. Of course, if you are actively in the user thread (responding to a mouse click, for instance), you can set useDispatcher = false when you call this method.

I personally use observable collections a lot for feedback to the user, and so, in my case, I limit the window that the user will be able to see. Removing items a bundle at a time is for performance when you are really zinging those status messages out. I also check to see if the user dropped the thumb at the bottom of the list control on which these items are shown. If they did, then I set _scrollIntoView to true to keep the bottom-most item in view.

Michael Hays
  • 6,878
  • 2
  • 21
  • 17
1

You can safely synchronize access to the collection like this:

lock (myCollection) {
    myCollection.InsertItem(0, myItem);
}

Only one thread can modify the collection at a time. Be sure to place any code using the collection inside of the lock block.

James Johnston
  • 9,264
  • 9
  • 48
  • 76
1

You cannot really do this without creating your own collection class. How do I update an ObservableCollection via a worker thread?

There are some implementations of thread-safe observabler collections available : http://www.deanchalk.me.uk/post/Thread-Safe-Dispatcher-Safe-Observable-Collection-for-WPF.aspx

Community
  • 1
  • 1
Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
  • Yeah I already took a look at those thread-safe observable collections, but there were some problems (either with my usage or with the implementation) if all else fails, I'll try harder with one of those. – Dänu Aug 15 '11 at 16:07