1212using System . Windows . Data ;
1313using System . Windows . Input ;
1414using System . Windows . Media ;
15+ using System . Threading ;
16+ using System . Threading . Tasks ;
17+ using System . Windows . Threading ;
1518
1619namespace DaxStudio . Controls
1720{
@@ -34,6 +37,10 @@ public bool ShowDefaultContextMenu
3437 private readonly Dictionary < object , TreeGridRow < object > > _itemToRowMap = new Dictionary < object , TreeGridRow < object > > ( ) ;
3538 private readonly ObservableCollection < TreeGridRow < object > > _flattenedRows = new ObservableCollection < TreeGridRow < object > > ( ) ;
3639
40+ // Add these missing fields for async RefreshData
41+ private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim ( 1 , 1 ) ;
42+ private CancellationTokenSource _refreshCancellation ;
43+
3744 static TreeGrid ( )
3845 {
3946 DefaultStyleKeyProperty . OverrideMetadata ( typeof ( TreeGrid ) ,
@@ -51,6 +58,14 @@ public TreeGrid()
5158 this . GridLinesVisibility = DataGridGridLinesVisibility . None ;
5259 this . AlternatingRowBackground = new SolidColorBrush ( Color . FromArgb ( 25 , 0 , 0 , 0 ) ) ;
5360
61+ // Enable virtualization for better performance with large datasets
62+ this . EnableRowVirtualization = true ;
63+ this . EnableColumnVirtualization = true ;
64+
65+ // Use recycling mode for even better performance - Fixed syntax
66+ VirtualizingPanel . SetVirtualizationMode ( this , VirtualizationMode . Recycling ) ;
67+ VirtualizingPanel . SetScrollUnit ( this , ScrollUnit . Item ) ;
68+
5469 Loaded += OnLoaded ;
5570 //SelectionChanged += OnSelectionChanged;
5671 }
@@ -211,28 +226,34 @@ private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
211226 // Ensure thread safety with lock
212227 lock ( _selectionChangeLock )
213228 {
214- // Batch process selection changes
229+ // Prevent recursive calls during updates
230+ if ( _isUpdatingFlattenedRows )
231+ return ;
232+
215233 _isUpdatingFlattenedRows = true ;
216234 try
217235 {
218- // Process removed items first to avoid conflicts
236+ // ALWAYS clear previous selections - remove IsCollapsing check
219237 foreach ( TreeGridRow < object > row in e . RemovedItems )
220238 {
221- if ( ! row . IsCollapsing )
222- {
223- SetSelectedLineLevelRecursive ( row , row . Level , false ) ;
224- }
239+ ClearSelectedLineRecursive ( row ) ;
225240 }
226241
227- // Then process added items
242+ // Set new selections
228243 foreach ( TreeGridRow < object > row in e . AddedItems )
229244 {
245+ // Skip updating selection lines if row is collapsing
230246 if ( row . IsCollapsing )
231247 {
248+ Debug . WriteLine ( $ "Skipping selection update for collapsing row at level { row . Level } ") ;
249+ // Reset IsCollapsing flag but don't update selection lines
232250 row . IsCollapsing = false ;
233- continue ;
234251 }
235- SetSelectedLineLevelRecursive ( row , row . Level , true ) ;
252+ else
253+ {
254+ // Only update selection lines when NOT collapsing
255+ SetSelectedLineRecursive ( row , row . Level , true ) ;
256+ }
236257 }
237258 }
238259 finally
@@ -242,16 +263,47 @@ private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
242263 }
243264 }
244265
245- private void SetSelectedLineLevelRecursive ( TreeGridRow < object > row , int level , bool value )
266+ // Add this new method for complete cleanup
267+ private void ClearSelectedLineRecursive ( TreeGridRow < object > row )
268+ {
269+ // Clear all selection levels for this row's path
270+ if ( row . SelectedLineLevels != null )
271+ {
272+ for ( int i = 0 ; i < row . SelectedLineLevels . Count ; i ++ )
273+ {
274+ row . SelectedLineLevels [ i ] = false ;
275+ }
276+ }
277+ // Clear selection for all descendants
278+ foreach ( TreeGridRow < object > child in row . Children )
279+ {
280+ ClearSelectedLineRecursive ( child ) ;
281+ }
282+ }
283+
284+ // Improve the existing method
285+ private void SetSelectedLineRecursive ( TreeGridRow < object > row , int level , bool value )
246286 {
247- if ( row . SelectedLineLevels != null && level < row . SelectedLineLevels . Count )
287+
288+ if ( row . SelectedLineLevels != null )
248289 {
249- row . SelectedLineLevels [ level ] = value ;
290+ // Ensure the collection is large enough
291+ while ( row . SelectedLineLevels . Count <= level )
292+ {
293+ row . SelectedLineLevels . Add ( false ) ;
294+ }
295+
296+ // Only set the specific level, don't clear others unless explicitly needed
297+ if ( level < row . SelectedLineLevels . Count )
298+ {
299+ row . SelectedLineLevels [ level ] = value ;
300+ }
250301 }
251302
303+ // Propagate to children for the same level (ancestor line)
252304 foreach ( TreeGridRow < object > child in row . Children )
253305 {
254- SetSelectedLineLevelRecursive ( child , level , value ) ;
306+ SetSelectedLineRecursive ( child , level , value ) ;
255307 }
256308 }
257309
@@ -295,6 +347,17 @@ public bool ExpandOnLoad
295347 set => SetValue ( ExpandOnLoadProperty , value ) ;
296348 }
297349
350+ // Add this property
351+ public static readonly DependencyProperty EnableLazyLoadingProperty =
352+ DependencyProperty . Register ( nameof ( EnableLazyLoading ) , typeof ( bool ) , typeof ( TreeGrid ) ,
353+ new PropertyMetadata ( false ) ) ;
354+
355+ public bool EnableLazyLoading
356+ {
357+ get => ( bool ) GetValue ( EnableLazyLoadingProperty ) ;
358+ set => SetValue ( EnableLazyLoadingProperty , value ) ;
359+ }
360+
298361 private static void OnChildrenBindingPathChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
299362 {
300363 if ( d is TreeGrid grid )
@@ -388,18 +451,18 @@ private TreeGridRow<object> BuildHierarchy(object item, int level, TreeGridRow<o
388451 } ;
389452
390453 _itemToRowMap [ item ] = row ;
454+ if ( parent != null ) parent . AddChild ( row ) ; // Use the optimized AddChild method
391455
392- if ( parent != null )
456+ // Only build children if not using lazy loading or if expanded
457+ if ( ! EnableLazyLoading || row . IsExpanded )
393458 {
394- parent . Children . Add ( row ) ;
395- }
396-
397- var children = GetChildren ( item ) ;
398- if ( children != null )
399- {
400- foreach ( var child in children )
459+ var children = GetChildren ( item ) ;
460+ if ( children != null )
401461 {
402- BuildHierarchy ( child , level + 1 , row ) ;
462+ foreach ( var child in children )
463+ {
464+ BuildHierarchy ( child , level + 1 , row ) ;
465+ }
403466 }
404467 }
405468
@@ -414,24 +477,47 @@ private TreeGridRow<object> BuildHierarchy(object item, int level, TreeGridRow<o
414477 private int _lastRefreshRowCount = 0 ;
415478
416479 // Optimized RefreshData method
417- private void RefreshData ( )
480+ private async void RefreshData ( )
418481 {
419482 if ( _rootRows == null || _rootRows . Count == 0 || _isUpdatingFlattenedRows )
420483 return ;
421484
422- _refreshTimer . Restart ( ) ;
423- _isUpdatingFlattenedRows = true ;
485+ // Cancel any pending refresh
486+ _refreshCancellation ? . Cancel ( ) ;
487+ _refreshCancellation = new CancellationTokenSource ( ) ;
488+ var cancellationToken = _refreshCancellation . Token ;
489+
490+ if ( ! await _refreshSemaphore . WaitAsync ( 0 ) ) // Don't wait, just skip if busy
491+ return ;
492+
424493 try
425494 {
495+ _refreshTimer . Restart ( ) ;
496+ _isUpdatingFlattenedRows = true ;
497+
426498 // Build the new flattened structure more efficiently
427499 var newFlattenedRows = new List < TreeGridRow < object > > ( ) ;
428500 _visibleRowsSet . Clear ( ) ;
429501
502+ // Process in batches for large datasets
503+ const int batchSize = 1000 ;
504+ int processed = 0 ;
505+
430506 foreach ( var row in _rootRows )
431507 {
508+ if ( cancellationToken . IsCancellationRequested ) return ;
509+
432510 BuildVisibleRowsListOptimized ( row , newFlattenedRows ) ;
511+
512+ // Yield control every batch to keep UI responsive
513+ if ( ++ processed % batchSize == 0 )
514+ {
515+ await Dispatcher . Yield ( DispatcherPriority . Background ) ;
516+ }
433517 }
434518
519+ if ( cancellationToken . IsCancellationRequested ) return ;
520+
435521 // Early exit if no changes
436522 if ( newFlattenedRows . Count == _lastRefreshRowCount &&
437523 _flattenedRows . Count == _lastRefreshRowCount &&
@@ -444,13 +530,21 @@ private void RefreshData()
444530 _lastRefreshRowCount = newFlattenedRows . Count ;
445531 Debug . WriteLine ( $ "TreeGrid Visible Rows built: { newFlattenedRows . Count } rows at { _refreshTimer . ElapsedMilliseconds } ms") ;
446532
447- // Perform batch updates to _flattenedRows
448- UpdateFlattenedRowsCollectionOptimized ( newFlattenedRows ) ;
533+ // Perform batch updates to _flattenedRows on UI thread
534+ await Dispatcher . InvokeAsync ( ( ) =>
535+ {
536+ if ( ! cancellationToken . IsCancellationRequested )
537+ {
538+ UpdateFlattenedRowsCollectionOptimized ( newFlattenedRows ) ;
539+ }
540+ } , DispatcherPriority . Normal ) ;
541+
449542 Debug . WriteLine ( $ "TreeGrid Flattened Rows updated at: { _refreshTimer . ElapsedMilliseconds } ms") ;
450543 }
451544 finally
452545 {
453546 _isUpdatingFlattenedRows = false ;
547+ _refreshSemaphore . Release ( ) ;
454548 Debug . WriteLine ( $ "TreeGrid RefreshData completed in: { _refreshTimer . ElapsedMilliseconds } ms") ;
455549 Debug . WriteLine ( "====" ) ;
456550 }
@@ -582,7 +676,7 @@ public void ToggleItem(object item)
582676 if ( row . IsExpanded )
583677 {
584678 row . IsCollapsing = true ;
585- MarkDescendantsAsCollapsing ( row ) ;
679+ // MarkDescendantsAsCollapsing(row);
586680 }
587681
588682 row . IsExpanded = ! row . IsExpanded ;
@@ -591,21 +685,21 @@ public void ToggleItem(object item)
591685 // Update selection lines more efficiently
592686 if ( row . IsExpanded )
593687 {
594- SetSelectedLineLevelRecursive ( row , row . Level , true ) ;
688+ SetSelectedLineRecursive ( row , row . Level , true ) ;
595689 }
596690 }
597691 //}
598692 }
599693
600694 // Helper method to mark descendants as collapsing
601- private void MarkDescendantsAsCollapsing ( TreeGridRow < object > row )
602- {
603- foreach ( var child in row . Children )
604- {
605- child . IsCollapsing = true ;
606- MarkDescendantsAsCollapsing ( child ) ;
607- }
608- }
695+ // private void MarkDescendantsAsCollapsing(TreeGridRow<object> row)
696+ // {
697+ // foreach (var child in row.Children)
698+ // {
699+ // child.IsCollapsing = true;
700+ // MarkDescendantsAsCollapsing(child);
701+ // }
702+ // }
609703
610704 // Optimized ExpandAll/CollapseAll with batch operations
611705 public void ExpandAll ( )
0 commit comments