Skip to content

Commit 7236fb4

Browse files
committed
2 parents e0fffcd + b9e2e99 commit 7236fb4

File tree

5 files changed

+246
-54
lines changed

5 files changed

+246
-54
lines changed

DaxStudio.Controls.Example/QueryPlanTreeExample.xaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@
9898
IndentWidth="16"
9999
ExpanderTemplate="{StaticResource SnoopExpanderTemplate}"
100100
LineStroke="#AAAAAA"
101-
101+
SelectedLineStroke="Red"
102102
LineThickness="0.8">
103103
</ctrl:TreeGridTreeColumn>
104104

DaxStudio.Controls/Controls/TreeGrid/TreeGrid.cs

Lines changed: 131 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
using System.Windows.Data;
1313
using System.Windows.Input;
1414
using System.Windows.Media;
15+
using System.Threading;
16+
using System.Threading.Tasks;
17+
using System.Windows.Threading;
1518

1619
namespace 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()

DaxStudio.Controls/Controls/TreeGrid/TreeGridTreeColumn.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public Brush LineStroke
4848
public Brush SelectedLineStroke
4949
{
5050
get => (Brush)GetValue(SelectedLineStrokeProperty);
51-
set => SetValue(LineStrokeProperty, value);
51+
set => SetValue(SelectedLineStrokeProperty, value); // Fixed: was setting LineStrokeProperty
5252
}
5353

5454
/// <summary>
@@ -238,6 +238,10 @@ private void OnExpanderPreviewMouseDownEvent(object sender, RoutedEventArgs e)
238238
if (e.Source is TreeGridTreeCell cell)
239239
{
240240
var context = cell.DataContext as TreeGridRow<object>;
241+
if (context == null) {
242+
//e.Handled = true;
243+
return;
244+
}
241245
if (context.IsExpanded) context.IsCollapsing = true;
242246
}
243247
}

0 commit comments

Comments
 (0)