Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronously adding to ObservableCollection (or an alternative)

Here's what I have - a ListBox with an ItemsSource set to a ObservableCollection<T> - where T is my custom class representing a file, containing just 2 DependencyProperties: Filename and ThumbnailPath. - The listbox also has a custom DataTemplate defined, in order to nicely display a image and filename under it.

The purpose of the listbox is to display video files in the current folder (selected in a TreeView), with thumbnails (generated asynchronously; not part of this problem).

So when I change the folder in the TreeView, the ObservableCollection is cleared and filled up again, which is automatically reflected in the the ListBox items.

Here's the problem: The UI becomes unresponsive and it takes up to several seconds to update. Again, thumbnails do not have significance here (I tried disabling them). I think what takes the most time is the construction of 50-100 instances of my custom class, and their visual representation - it has to initialize an Image object for each one. But it's just my guess - could you please confirm or exclude the possibility?

I'm beginning to think ObservableCollection may not the way to go here, since from what I read and a little from what I tried, there's no way to add items asynchronously, at least if these items are DependencyObjects. I tried creating my class instances with a BackgroundWorker and adding them to the collection in the ProgressChanged event handler, but it throws an exception (some threading vs dependencyobjects problem).

Is there something that I'm missing? Or would I be better off by simply ditching the ObservableCollection and writing a good old async for loop to add the items?

like image 381
oli.G Avatar asked Oct 14 '12 10:10

oli.G


2 Answers

Since your ObservableCollection is bound to UI hence it gets generated on UI thread so any further updates (delete/add/clear) have to be on the same UI thread. It doesn't allow updates from another thread.

However, what you can do is to create an instance of your class (or all time consuming operation on background thread) and once you are done, add the object in ObservableCollection using Dispatcher of your UI thread like this -

App.Current.Dispatcher.BeginInvoke((Action)delegate()
                          {
                              observableCollection.Add(instanceOfYourClass);
                          });

What Dispatcher do is put the operation on its associated thread. Hence, the item will always be added on UI thread but can be created in background thread.

Here are few links which might get you going - Updating from BW and the other one is here

like image 115
Rohit Vats Avatar answered Sep 18 '22 21:09

Rohit Vats


With .net 4.5 you can use EnableCollectionSynchronization

 object lockObj = new object();
        BindingOperations.EnableCollectionSynchronization(yourObservableCollection, lockObj);
like image 32
Andreas Avatar answered Sep 18 '22 21:09

Andreas