C# WPF refresh ListView with lot of items hangs - c#

I have some kind of file explorer using a ListView to show the files. When i open a folder containing a few thousands of files, the UI hangs around 5 seconds while the ListView is refreshing.
Is there any way to avoid that ?
Thanks

Virtualisation is a must for large collections like these. Without it, WPF will have to create a visual tree for EACH item in your list ahead of time.
Just a hunch, but are you using an ObservableCollection to bind the collection of files to the ListBox?
I've had performance problems with large collections and the ObservableCollection. You can only add/remove items on the UI thread - if you're iterating through your file results, and adding them to the observable collection then you're going to experience this kind of lag.
Best solution in that case is to compile your file list as a separate list, and create a new ObservableCollection, passing the list in through the constructor. This minimizes the hit on the UI thread.

Add the items to your ListView's datasource with DispatcherPriority set to Background.
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => AddNewITemToList(newItem));

The performance problem you have now is related to a fact that control creates UI elements for all thousand of files found, even if renders only small amount of them.
You need at least use ListView.VirtualMode to implement kind of paging in your control.
In practise what happens is that in this mode, the control's ListViewItem is created dynamicaly , so you can use ItemsCollection of it, but you can control the creation of every single item required by UI in single moment, so improve performance too.
There is also an option of Data Virtualization, where the data bound to your control is virtualized too, and not only UI of the control.

Related

Why is WPD Datagrid Usercontrol bound to row datacontext not updating?

I have a DataGrid bound via a ListCollectionView to an ObservableCollection of Objects with type Job, say. Each cell in the DataGrid contains a UserControl which has a dependency property named Job which is bound to the DataGridRow.DataContext (using Mode=TwoWay). Everything displays correctly.
The problem is that I have a background process which mutates objects referenced by the Job object and those get displayed by the UserControl. Obviously, Job does not change so the view does not change.
How can I get the user controls in each cell to update themselves with the new data?
With lists there are 3 kinds of ChangeNotification you have to take care of:
the one for each property of the Job Class.
the one if elements are added or removed from the collection. That is the only part ObservableCollection takes care off.
the one on the property exposing the list/CollectionView/whatever. ObservableCollection lacks a AddRange function so on large operations (like a rebuild of the list) it would spam the UI with changes. The solution is to build a new list in the background, then expose it in this property.
One particular issue might be the Background Process too, if it is Multithreading. GUI's are protected against being written from the wrong Thread. But threads are also notoriously good at swallowing Exceptions. Usually you need to write a faulty Catch, but they do it for free. As a result, your might run int oa CrossThread exception and never notice it.
For a better answer, we need some actuall code. Like teh job class, the BackgroundProcess, etc.

Windows 8.1 XAML ListView Programmatic Reorder

With a list view containing items I can reorder which gives a nice UI effect.
I have my item source for the list view hooked up to an observable collection. Is there anyway with the framework I could programatically simulate a reorder and give the same effect you would have if you actually reordering?
The only way I've been able to slightly achieve this is to literally clear all the items from my data source then repopulate after shuffling the items, however it doesn't look nice.
Well, you could sort the items in code-behind in a temporary helper array and then according to the order they are in in the sorted one start periodically moving items one-by-one in a small time interval (with DispatcherTimer) withing the collection - remove at it's index and insert somewhere it belongs. I think there is no built-in way to do it in a simpler manner.

Where should you be instantiating UI controls from data objects when not an ItemsControl subclass?

TLDR version...
We're trying to create as basic Panel subclass with an observable Items property. The control then uses those data items to create one or more related child UI objects per data item.
Our first thought naturally was to simply subclass ItemsControl, but that doesn't seem to fit because it uses an ItemContainerGenerator which only generates one 'container' per item whereas again, we need to potentially create several (which aren't containers anyway.) Plus, all the created items have to be direct children on the panel, not held in a container which is why we can't go the route of data templates.
As such, I'm just using a standard Control and I'm trying to find the proper place/event where I should be instantiating/destroying the resulting child UI elements in response to changes in the Items collection.
Now the details...
First things first. If there was something like a ItemMultiContainerGenerator, that would be perfect, but I know of no such thing.
Ok, so simply monitor the collection for changes and put the UI generation in the CollectionChanged event! Right? That was our first guess too. The problem there is for every new 'Add' or 'Remove', we have to spin through all existing controls to 'defrag' certain indexing properties on them (think along the lines of a Grid.Row or ZIndex property) meaning if you add ten items, you run the defrag ten times, not once at the end.
Plus, that change event may come in on a different thread. If you attempted to dispatch to the main thread, your performance takes a nose-dive.
Our other attempt was to use MeasureOverride, since that was called only once in response to an InvalidateMeasure call, regardless of how many children we added or removed. The issues (there are many) with this approach is we lose context of whether something was added or removed, meaning we had to throw away all children and re-add back all new ones making this extremely inefficient. Plus, mucking around with the visual tree or setting bindings could cause the layout pass to execute multiple times since we were changing something that affects the layout, namingly the panels children.
What I'm trying to find is something that happens as part of the overall rendering process (i.e. from the time the control is told its invalid until it renders), but before the Measure/Layout passes are called. That way I can cache the adds/removes in the CollectionChanged event and simply mark the control as invalid, wait for this mystery event, then process the changes en mass, then send off the results to the layout engine and be done with it.
Using Reflector, I've tried to find out where the ItemsControl adds its children to the panel, but I didn't get too far considering the complexity of the control/ItemContainerGenerator pairing.
So where is the best place to create/add UI elements to a control based on data item changes?
I think you're going to have to listen to collection changes manually. There are a few reasons:
Changing your visual children will invalidate your layout. If you change your children in measure or arrange, you will have an infinite loop.
Rendering happens after layout. By changing children after layout you are asking for an infinite loop.
Hopefully you won't have to support any collections other than ObservableCollection, because it is obviously easier to deal with the one type of collection. But, I think you can definitely make a responsive control that will do these things. Here are some tips
Implement a custom Collection that has support for AddRange and RemoveRange (the INotifyCollectionChanged event supports multiple items being added or removed) so you don't have to do the same work 10 times for 10 new items.
In your collection change handler, use the e.AddedItems and e.RemovedItems instead of accessing the underlying collection. This will prevent you from receiving exceptions due to the collection changing while you're enumerating it.
Use BeginInvoke to prevent blocking the producer thread when dispatching back to the UI thread
To address your initialization concerns, implement ISupportInitialize, and use it to suspend your "defrag" process if you have to add or remove multiple items one at a time. WPF will automatically add the Begin/End calls for you when your control is created in XAML.
Derive from FrameworkElement if you don't want a ControlTemplate. Lower overhead.
If this still isn't working because the speed at which your underlying collection changes is too fast, and children are fairly simple, perhaps you should draw them in OnRender
One other option that just occurred to me is that you can schedule all these operations on the Dispatcher, so that if multiple changes happen, you would only need to do it once. Basically, you would store a reference to the operation like so:
private DispatcherOperation pendingDefragOperation;
protected void ScheduleDefrag()
{
if (pendingDefragOperation == null)
{
pendingDefragOperation = Dispatcher.BeginInvoke(
DispatcherPriority.Render, // You may want to play around with this
new Action(Defrag));
}
}
And you would call this on a CollectionChanged. And in your Defrag call, you'd set pendingDefragOperation to null.

How to add thousands of items to a binded collection without locking GUI

I have a setup where potentially thousands of items (think 3000-5000) will be added to an ObservableCollection that is binded to some visual interface. Currently, the process of adding them is quite slow (approx. 4 seconds/1000 items), and of course the GUI is unresponsive during that time. What is a good method to handle moving that many items at once into a collection without worrying about the system locking up? I've looked at DispatcherTimer but I'm not sure if it will provide everything I need it to.
Another question - Is there something I can do to speed up the creation of these objects so that it doesn't take so long to add them to the collection? Currently I use them like so: Collection.Add(new Item(<params>)) Would generating the items beforehand, in a background thread probably, decrease the time it takes to add them by a noticeable amount?
Edit: Virtualization is not possible. The requirements specify a WrapPanel look, so the display is actually a ListBox which has a templated ItemsPanel
Edit2: According to the stopwatch, the bottleneck is actually putting items into my ObservableCollection. I will try changing that collection type and doing my own notification to see if that speeds it up substantially.
Edit3: So the answer is in one place - I solved this issue (with help from below) by creating a class which inherits from ObservableCollection. This class did two things - expose a method to add collections at one time, and added the ability to suppress the CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.
You've said 1000, so I'll stick to that number just for instance.
IIRC, the observable collection has a small drawback - if you add the items one by one, it raises notifies once per each item. That means that you have 1000 notifications for 1000 of items and the UI thread will run at deadly speed just to keep up with redrawing the screen.
Do you need to redraw ASAP? Maybe you can batch the additions? Split the 1000 of items into a few packed of 100 items, or a little more packets of 50 or 20 items. Then, instead of putting all items one by one, put them in packets. But beware: you have to use some methods like AddRange implemented by the collection it self, not by LINQ, or else you will again have one-by-one insertion. If you find such method, it should cut the number of events significantly, because the collection should raise the Changed event only once per AddRange call.
If observable collection does not have AddRange, either use different collection, or write your own, just a wrapper will probably be sufficient. The goal is to NOT raise Changed event at every single Add(), but after a reasonable count of them, or - maybe just skip raising Changed when items are added and raise Changed at some regular time intervals? This would be beneficial especially, if your data "flows in" indefinitely at a constant rate.
Of course, at that number of items coming onto the screen, you may just as well be held at the rendering it self. If your ItemTemplates are complicated, a 1000 of objects times 1000 of instances of visual layers/properties may simply kill the user experience. Have you simplified the ItemTemplates to the bare minimum?
Last thing: consider using virtualizing StackPanels as the ItemPanels in your ItemsControl/ListBoxes. It can greatly reduce the memory footprint and the number of items drawn at a single point of time. This will not necessarily help in the number or events raised, but it may help greatly when you have complex item templates!
Edit: you are using ObservableCollection, so I've assumed WPF/Silverlight.. update the question if this is not correct
WPF Binding supports concurrency for this reason. Try setting Binding.IsAsync to true. In addition.
Don't use ObservableCollection<T>, it is slow for this because each time an item is added it raises events. Use something faster like List<T> and raise your property change notification after all your items are added.
Pre-create your items in a background thread, then push them into your collection.
Check other parts of code involved to see if there is bloat, and trim.
By request, here is how I solved this issue. I started by creating a class which inherits from ObservableCollection. This class did two things - expose a method to add entire collections at once, and added the ability to suppress the CollectionChanged Event. With these changes the time it takes to add 3000 items is roughly .4 seconds (97% improvement). This link details the implementation of these changes.
Another thing you can try: subclass ObservableCollection and make it support bulk loading (AddRange). Here is an article:
AddRange and ObservableCollection
for second question
if in your GUI you are using WPF technologie, you can increase performance using VirualizingStackPanel allowing you to create only visible items

C#, WinForms: Which view type for periodically updated list?

I'm having an application, that periodically polls a web service (about every 10 seconds). In my application logic I'm having a List<Message> holding the messages. All messages have an id, and might be received out of order. Therefore the class implements the Comparable Interface.
What WinForm control would fit to be regurarly updated (with the items in order). I plan to hold the last 500 messages. Should I sort the list and then update the whole form? Or is data binding approriate (concerning performance)?
If you are working with WinForms, I shall recommend a ListView-control with View-property set to Details along with a BackgroundWorker-control.
If the list is going to be changing each time, and your order is not coming in consistently, I'd just sort your list and reupdate the entire UI.
You should be able to do the list fetching + sorting in a background thread, so the main "UI" blocking will just be redrawing the UI. With 500 elements, this won't be fast, but it should be tolerable.
i'd use databinding with the BindingList in System.ComponentModel wrapping your List.
BindingList in this case, would allow you to update your List object with new data and will automatically re-bind your data to the control that you are using for display.
which control you use would depend on how you want it to look. any control that supports databinding to a collection would work, though.

Categories

Resources