views:

168

answers:

1

I need to display lots of rows in a grid added at a pretty high frequency (up to 10 rows per second in some cases) I chose ListView because I assume is the fastest grid control in WPF. (certainly a lot faster than GridView)

CPU utilization gets pretty high after couple hundred thousand items were added and they continue to come in. This is rather surprising, as ListView renders only the visible rows, so it should not matter how many are added in total.

My first approach was binding it to ObservableCollection but after a while, the CPU utilization goes up, and the whole Window get jittery.

Than i tried binding it to a regular List, which appears to be faster, however i do need to call the .Refresh() on the list often, which after a while slams the CPU as well.

Than i attempted subclassing ObservableCollection to chunk inserts hoping that batching them would improve performance/decrease cpu workload, but this approach appears to require calling CollectionView.Refresh which is the same as calling Reset() on the collection, and also ineficient when there are lots of items in the collection.

Clearing the observablecollection and than calling myListView.Items.Refresh() to bring it back down to 0 brings cpu usage back to the starting point.

Starting to run out of ideas here.. Again, my aim here, is to add/display lots of items and display 8 column grid, in the most performant manner.. ListView seems good, there just have to be some ways i could further tweak it..

UPDATE

after profiling, ObservableCollection 800k rows in the grid, the most cpu intensive work is done by :

  • (75%) System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget)
  • (20%) ObservableCollection.OnCollectionChanged(NotifyCoolectionChanged..)

although depending on session those numbers vary greatly..

UPDATE 2 .. ok BindingList appears to be the clear winner here.

here are results (in ticks) side by side with 1 million rows each (and adding 10 items per second):

ObservableCollection: http://i.imgur.com/7ZoSv.png

BindingList http://i.imgur.com/jm5qF.png

you can see overall drop in CPU activity, and about half the ticks required to process the tree in Binding List case! My thanks to Akash for this great idea.

+5  A: 

Instead of using ObservableCollection I will suggest BindingList class, you can do something like this..

BindingList<string> list = new BindingList<string>();

list.AllowEdit = true;
list.AllowNew = true;
list.AllowRemove = true;

// set the list as items source
itemCollection.ItemsSource = list;

// add many items...

// disable UI updation
list.RaiseListChangedEvents = false;

for each(string s in MyCollection){
   list.Add(s);
}

// after all.. update the UI with following
list.RaiseListChangedEvents = true;
list.ResetBindings(); // this forces update of entire list

You can enable/disable updating even in batches, instead of adding everything at one shot, BindingList has been functioning better then ObservableCollection in all my UI, I wonder why everywhere people talk more about ObservableCollection when BindingList really superseds ObservableCollection.

Akash Kava
Akash, thanks for this. I will test this collection shortly. One thing i am curious, is if there is a way to do batch inserts w/out refreshing the whole list. i believe that is a source for much jitter and cpu work, when 800k items are refreshed etc.
Sonic Soul
I dont think it will refresh 800k items, because the Virtualized Panels will only refresh limited items if they are on the screen. However another way would be to Page your items with some sort of pager, in my apps, I only load 0-20 on first and let user navigate rest one by one, also they can filter using some sort of search
Akash Kava
have you tried filtering your binding list? with ObservableCollection, i was using CollectionViewSource because that provides a filter, that filter executes on each added item. That filter is what would likely execute on all rows after refresh. So i will need to find another way to add filtering.
Sonic Soul
Sorry, I guess filtering is not possible, but you can try filtering UI, ItemsCollection of ItemsControl does allow filtering, also you can filter your data from model when you load and then pass it on to control.
Akash Kava
You can also create your own version of Binding List, you have to make a class derived or containing any list and implement IBindingList interface !! for great performance.
Akash Kava