How to avoid excessive property change events when setting large selection?

Grids for WPF Forum

Posted 1 month ago by Lars Klose
Version: 22.1.4
Avatar

We have a TreeList which may contain several thousand items and there are scenarios where a large part of those may be selected programatically. In general, this works fine as long as the number of selected items is not too big. The larger the number the more noticeable is the lag that is caused by the excessive number of PropertyChanged events for IsSelected.

What is the best way to avoid this and have the control establish the complete selection in one go when we know that setting the IsSelected property is done for all desired items?

We also observed that this strangely isn't a problem in the comparable scenario that the user simply presses Ctrl+A in the tree list control - there the selection is established and shown immediately without any lag. How does this work in comparison?

Comments (6)

Posted 1 month ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

We wrap our internal selection operations with a batch transaction that minimizes events.  There is a public TreeListBox.CreateSelectionBatch method you can use that returns an IDisposable. 

Put the result in a using statement and see if that helps, like this:

using (var batch = treeListBox.CreateSelectionBatch()) {
  ...
}


Actipro Software Support

Posted 1 month ago by Lars Klose
Avatar

I thought I read the docs thorougly, but didn't find that method. Thanks for the advice!

May I suggest to add that information to this general page on selection in tree controls:
https://www.actiprosoftware.com/docs/controls/wpf/grids/tree-control-features/selection

However, using the batch selection still doesn't cut it yet. I still see in the callstack that the PropertyChanged event for IsSelected results (through a chain of obfuscated Actipro methods) in a call to UpdateLayout() and even some methods which sound like a change of the visual tree was handled: 

PresentationFramework.dll!System.Windows.TreeWalkHelper.InvalidateOnTreeChange(System.Windows.FrameworkElement fe, System.Windows.FrameworkContentElement fce, System.Windows.DependencyObject parent, bool isAddOperation) Unknown
PresentationFramework.dll!System.Windows.FrameworkElement.OnVisualParentChanged(System.Windows.DependencyObject oldParent) Unknown
PresentationCore.dll!System.Windows.Media.Visual.FireOnVisualParentChanged(System.Windows.DependencyObject oldParent) Unknown
PresentationCore.dll!System.Windows.Media.Visual.AddVisualChild(System.Windows.Media.Visual child) Unknown
PresentationCore.dll!System.Windows.Media.VisualCollection.ConnectChild(int index, System.Windows.Media.Visual value) Unknown
PresentationCore.dll!System.Windows.Media.VisualCollection.Add(System.Windows.Media.Visual visual) Unknown
PresentationFramework.dll!System.Windows.Controls.UIElementCollection.AddInternal(System.Windows.UIElement element) Unknown
PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.InsertContainer(int childIndex, System.Windows.UIElement container, bool isRecycled) Unknown
PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.AddContainerFromGenerator(int childIndex, System.Windows.UIElement child, bool newlyRealized, bool isBeforeViewport) Unknown
PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureChild(ref System.Windows.Controls.Primitives.IItemContainerGenerator generator, ref System.Windows.Controls.Primitives.IContainItemStorage itemStorageProvider, ref System.Windows.Controls.Primitives.IContainItemStorage parentItemStorageProvider, ref object parentItem, ref bool hasUniformOrAverageContainerSizeBeenSet, ref double computedUniformOrAverageContainerSize, ref double computedUniformOrAverageContainerPixelSize, ref bool computedAreContainersUniformlySized, ref bool hasAnyContainerSpanChanged, ref System.Collections.IList items, ref object item, ref System.Collections.IList children, ref int childIndex, ref bool visualOrderChanged, ref bool isHorizontal, ref System.Windows.Size childConstraint, ref System.Windows.Rect viewport, ref System.Windows.Controls.VirtualizationCacheLength cacheSize, ref System.Windows.Controls.VirtualizationCacheLengthUnit cacheUnit, ref long scrollGeneration, ref bool foundFirstItemInViewport, ref double firstItemInViewportOffset, ref System.Windows.Size stackPixelSize, ref System.Windows.Size stackPixelSizeInViewport, ref System.Windows.Size stackPixelSizeInCacheBeforeViewport, ref System.Windows.Size stackPixelSizeInCacheAfterViewport, ref System.Windows.Size stackLogicalSize, ref System.Windows.Size stackLogicalSizeInViewport, ref System.Windows.Size stackLogicalSizeInCacheBeforeViewport, ref System.Windows.Size stackLogicalSizeInCacheAfterViewport, ref bool mustDisableVirtualization, bool isBeforeFirstItem, bool isAfterFirstItem, bool isAfterLastItem, bool skipActualMeasure, bool skipGeneration, bool isAncestorLookingForFirstItem, ref bool hasBringIntoViewContainerBeenMeasured, ref bool hasVirtualizingChildren) Unknown
PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverrideImpl(System.Windows.Size constraint, ref double? lastPageSafeOffset, ref System.Collections.Generic.List<double> previouslyMeasuredOffsets, ref double? lastPagePixelSize, bool remeasure) Unknown
PresentationFramework.dll!System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(System.Windows.Size constraint) Unknown
PresentationFramework.dll!System.Windows.FrameworkElement.MeasureCore(System.Windows.Size availableSize) Unknown
PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize) Unknown
PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.TreeListBox.Oob(ActiproSoftware.Internal.gX ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.TreeListBox.l3a(ActiproSoftware.Internal.gX , bool ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.TreeListBox.m3j(ActiproSoftware.Internal.gX , bool , bool ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Internal.gX.lNl(string ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Internal.gX.zNZ(object , System.ComponentModel.PropertyChangedEventArgs ) Unknown
Vector.ReportViewer.ViewModel!Vector.ReportViewer.ViewModel.NavigatorModels.QueryResultBase.NotifyPropertyChanged(string propertyName) Line 851 C#
Vector.ReportViewer.ViewModel!Vector.ReportViewer.ViewModel.NavigatorModels.QueryResultBase.IsSelected.set(bool value) Line 675 C#

Posted 1 month ago by Lars Klose
Avatar

Also, is there any recommended way to do the batch selection update when the selection is done on the model, where I don't have access to the tree control instance? E.g. any way to control the batch update through the adpter?

Posted 1 month ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Lars,

We'll add that code example to the docs for the next build.  The only way to apply the batch update is to have the control instance.  Perhaps your view model could use events handled by your view to notify when a bulk selection change starts and ends.

We are happy to look into the performance more if you'd like.  Please make a new simple sample project that shows the performance issue so that we can make sure we are seeing the same thing, and can debug that specific scenario.  Send that to our support address and in your email, reference this thread.  Exclude the bin and obj folders from the .zip you send so it doesn't get spam blocked.  Thanks!


Actipro Software Support

Posted 1 month ago by Lars Klose
Avatar

Extracting an executable sample would take some considerable time, therefore I'd like to first have a confirmation that the following part of the callstack is expected even though updates to IsSelected are wrapped in a using block with CreateSelectionBatch: like this:

using (var batch = TreeList.CreateSelectionBatch())
{
  Model.RestoreSelection();
}

I was assuming that the batch operation would inhibit exactly that immediate UpdateLayout and delay it until closure of the using block!?
Note that we use a TreeListView - if that is relevant.

PresentationCore.dll!System.Windows.UIElement.Measure(System.Windows.Size availableSize) Unknown
PresentationCore.dll!System.Windows.ContextLayoutManager.UpdateLayout() Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.TreeListBox.Oob(ActiproSoftware.Internal.gX ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.TreeListBox.l3a(ActiproSoftware.Internal.gX , bool ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Windows.Controls.Grids.TreeListBox.m3j(ActiproSoftware.Internal.gX , bool , bool ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Internal.gX.lNl(string ) Unknown
ActiproSoftware.Grids.Wpf.dll!ActiproSoftware.Internal.gX.zNZ(object , System.ComponentModel.PropertyChangedEventArgs ) Unknown
> Vector.ReportViewer.ViewModel!Vector.ReportViewer.ViewModel.NavigatorModels.QueryResultBase.NotifyPropertyChanged(string propertyName) Line 854 C#
Vector.ReportViewer.ViewModel!Vector.ReportViewer.ViewModel.NavigatorModels.QueryResultBase.IsSelected.set(bool value) Line 675 C#
Vector.ReportViewer.ViewModel!Vector.ReportViewer.ViewModel.NavigatorModels.NavigatorModel.RestoreSelection() Line 114 C#

[Modified 1 month ago]

Posted 1 month ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hello,

I believe the obfuscated stack there is for a BringTreeNodeIntoView method that could call UpdateLayout in a certain scenario.  When I tried building a test to reproduce it, that UpdateLayout call wasn't being made though.

For the sample we'd need from you, it doesn't need you to include any of your own business objects or data.  You could even just try altering one of our TreeListBox samples to have more nodes and add buttons to execute selection changes in the same way you do in your app.  Then we can debug with that to see what scenario is triggering that UpdateLayout call.


Actipro Software Support

The latest build of this product (v24.1.5) was released 4 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.