UI thread stalls on loading and updating a RadGridView object
Afternoon all,
I'm currently profiling a rather large C# WPF app, trying to eliminate a few performance issues. One involves a 5-8 second stall that occurs when switching to a particular (rather large) UserControl. I've narrowed this down to a RadGridView contained in this UserControl that's taking a long time to load and update itself, stalling the UI thread and making the entire application unresponsive. Obviously, we'd like to eliminate this stall if possible.
I have tried stripping away any custom styles and DataTriggers on the grid, but while this acted to reduce load on the UI thread in general, the stall still remained, seemingly undiminished. Through ANTS Profiler, it seems that the measuring and layout of the grid is mostly to blame, along with some loading of XAML templates. With no grid rows, the UserControls loads significantly faster, and it seems that adding just a small number of rows is enough to bring about this stall. The grid has virtualization enabled for rows and columns, but this doesn't seem to help. The call graph is tremendously deep when examined, and it seems to be calls that raise update notifications, update layout, load XAML and, above all, measure child FrameworkElements that are to blame.
For a couple of potential solutions, I'm thinking about keeping the UserControl in memory but hidden to reduce the costs of switching to it, or populating the grid, perhaps incrementally, well after the UserControl has loaded. There might be a lot of work with the former, as the control subscribes to a number of things, which would need to be connected to, disconnected from and reconnected to as appropriate. The latter might also involve a fair bit of work, but might be a better solution, because then a开发者_Python百科t least we could try to mitigate the stalling ourselves, or at least warn the user when it was occurring.
If the problem persists, we're likely to ask Telerik to have a look at it, but I thought I'd ask here first in case anyone has encoutered such an issue before (not necessarily with RadGridView, even) and found a solution of some description.
Cheers.
The problem is you need a collection that implements ICollectionChanged properly.
Try this after adding all records to your source collection (one which should NOT raise any CollectionChanged events after each add), it works:
public void AddRange(List<TValue> values, int startingIndex)
{
// add all the items to your internal list, but avoid raising events.
// now raise CollectionChanged with an IList type.
CollectionChanged?.Invoke(collection, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, addedValues, startingIndex));
}
Notice we're passing IList<TValue>
, NOT TValue
. When it sees a list, it adds them ALL before processing. The startingValue is usually the .Count of the list before you started adding items.
Steps:
- Subclass a collection type that doesn't already implement INotifyCollectionChanged. Plenty of open source examples are out there.
- Add the method above and use it after you've added your items.
- My RadGridView is binding against a
QueryableCollectionView
which uses this list as its SourceCollection. I don't know if you can bind against this list directly.
One strange behavior with RadGridView is that if you add say, a million records this way, it WILL be quick, but all the new rows will be blank, and you can watch RadGridView slowly populate them with data from the topmost new item down. My solution to this is to bind against QueryableCollection and set its PageSize sufficiently small so the users don't see this.
精彩评论