@@ -299,6 +299,14 @@ private async System.Threading.Tasks.Task FetchGroupedPlansAsync(
299299
300300 LoadButton . IsEnabled = true ;
301301 SelectToggleButton . Content = "Select All" ;
302+
303+ // Auto-expand the first root row to the deepest level
304+ if ( rootRows . Count > 0 )
305+ {
306+ var first = rootRows [ 0 ] ;
307+ ExpandRowRecursive ( first ) ;
308+ }
309+
302310 UpdateStatusText ( ) ;
303311 UpdateBarRatios ( ) ;
304312
@@ -307,6 +315,29 @@ private async System.Threading.Tasks.Task FetchGroupedPlansAsync(
307315 _ = FetchGroupedWaitStatsAsync ( _slicerStartUtc . Value , _slicerEndUtc . Value , ct ) ;
308316 }
309317
318+ /// <summary>
319+ /// Recursively expands a row and all its children, inserting them into _filteredRows.
320+ /// </summary>
321+ private void ExpandRowRecursive ( QueryStoreRow row )
322+ {
323+ if ( ! row . HasChildren ) return ;
324+ row . IsExpanded = true ;
325+
326+ var idx = _filteredRows . IndexOf ( row ) ;
327+ if ( idx < 0 ) return ;
328+
329+ var insertAt = idx + 1 ;
330+ foreach ( var child in row . Children )
331+ {
332+ _filteredRows . Insert ( insertAt , child ) ;
333+ insertAt ++ ;
334+ }
335+
336+ // Recurse into each child that has children
337+ foreach ( var child in row . Children )
338+ ExpandRowRecursive ( child ) ;
339+ }
340+
310341 /// <summary>
311342 /// Fetches per-plan wait stats for all real plan IDs found in the grouped hierarchy,
312343 /// assigns them to leaf rows, then consolidates upward to intermediate and root rows.
@@ -650,19 +681,65 @@ private void ReorderColumnsForGroupBy()
650681 cols [ i ] . DisplayIndex = _savedColumnDisplayIndices [ i ] ;
651682 _savedColumnDisplayIndices = null ;
652683 }
684+ // Reset header colors
685+ ApplyGroupByHeaderColors ( ) ;
653686 return ;
654687 }
655688
656689 // Save original order if not yet saved
657690 _savedColumnDisplayIndices ??= cols . Select ( c => c . DisplayIndex ) . ToArray ( ) ;
658691
659- // Find the target column to promote
660- // cols[4] = QueryHash, cols[6] = Module (from AXAML definition order)
661- var targetColIndex = _groupByMode == QueryStoreGroupBy . QueryHash ? 4 : 6 ;
662- var targetCol = cols [ targetColIndex ] ;
692+ // Column definition indices (AXAML order):
693+ // 0=Expand, 1=Checkbox, 2=QueryId, 3=PlanId, 4=QueryHash, 5=PlanHash, 6=Module
694+ if ( _groupByMode == QueryStoreGroupBy . QueryHash )
695+ {
696+ // Order: Expand, Checkbox, QueryHash, PlanHash, QueryId, PlanId, ...
697+ cols [ 4 ] . DisplayIndex = 2 ; // QueryHash → 2
698+ cols [ 5 ] . DisplayIndex = 3 ; // PlanHash → 3
699+ cols [ 2 ] . DisplayIndex = 4 ; // QueryId → 4
700+ cols [ 3 ] . DisplayIndex = 5 ; // PlanId → 5
701+ }
702+ else // Module
703+ {
704+ // Order: Expand, Checkbox, Module, QueryHash, QueryId, PlanId, ...
705+ cols [ 6 ] . DisplayIndex = 2 ; // Module → 2
706+ cols [ 4 ] . DisplayIndex = 3 ; // QueryHash → 3
707+ cols [ 2 ] . DisplayIndex = 4 ; // QueryId → 4
708+ cols [ 3 ] . DisplayIndex = 5 ; // PlanId → 5
709+ }
663710
664- // Move it to DisplayIndex 2 (right after expand[0] and checkbox[1])
665- targetCol . DisplayIndex = 2 ;
711+ // Apply golden header colors for expandable columns
712+ ApplyGroupByHeaderColors ( ) ;
713+ }
714+
715+ /// <summary>
716+ /// Applies golden foreground to column headers that represent expandable/collapsible
717+ /// grouping levels in the current GroupBy mode, and resets others.
718+ /// </summary>
719+ private void ApplyGroupByHeaderColors ( )
720+ {
721+ // Column definition indices: 4=QueryHash, 5=PlanHash, 6=Module
722+ var goldenCols = _groupByMode switch
723+ {
724+ QueryStoreGroupBy . QueryHash => new HashSet < int > { 4 , 5 } , // QueryHash + PlanHash
725+ QueryStoreGroupBy . Module => new HashSet < int > { 6 , 4 } , // Module + QueryHash
726+ _ => new HashSet < int > ( ) ,
727+ } ;
728+
729+ var goldenBrush = new SolidColorBrush ( Color . FromRgb ( 0xFF , 0xD7 , 0x00 ) ) ; // Gold
730+
731+ for ( int i = 0 ; i < ResultsGrid . Columns . Count ; i ++ )
732+ {
733+ var col = ResultsGrid . Columns [ i ] ;
734+ if ( col . Header is not StackPanel sp ) continue ;
735+ var label = sp . Children . OfType < TextBlock > ( ) . LastOrDefault ( ) ;
736+ if ( label == null ) continue ;
737+
738+ if ( goldenCols . Contains ( i ) )
739+ label . Foreground = goldenBrush ;
740+ else
741+ label . ClearValue ( TextBlock . ForegroundProperty ) ;
742+ }
666743 }
667744
668745 private void ExpandRow_Click ( object ? sender , RoutedEventArgs e )
@@ -1006,15 +1083,57 @@ private void SelectToggle_Click(object? sender, RoutedEventArgs e)
10061083
10071084 private void LoadSelected_Click ( object ? sender , RoutedEventArgs e )
10081085 {
1009- var selected = _filteredRows . Where ( r => r . IsSelected ) . Select ( r => r . Plan ) . ToList ( ) ;
1086+ List < QueryStorePlan > selected ;
1087+ if ( _groupByMode != QueryStoreGroupBy . None )
1088+ {
1089+ // In grouped mode, expand selected grouped rows to their leaf plans
1090+ selected = _filteredRows
1091+ . Where ( r => r . IsSelected )
1092+ . SelectMany ( r => r . HasChildren ? CollectLeafPlans ( r ) : ( r . PlanId > 0 && r . QueryId > 0 ? [ r . Plan ] : [ ] ) )
1093+ . ToList ( ) ;
1094+ }
1095+ else
1096+ {
1097+ selected = _filteredRows . Where ( r => r . IsSelected ) . Select ( r => r . Plan ) . ToList ( ) ;
1098+ }
10101099 if ( selected . Count > 0 )
10111100 PlansSelected ? . Invoke ( this , selected ) ;
10121101 }
10131102
10141103 private void LoadHighlightedPlan_Click ( object ? sender , RoutedEventArgs e )
10151104 {
1016- if ( ResultsGrid . SelectedItem is QueryStoreRow row )
1105+ if ( ResultsGrid . SelectedItem is not QueryStoreRow row ) return ;
1106+
1107+ // In grouped mode, load all descendant leaf plans with real IDs
1108+ if ( _groupByMode != QueryStoreGroupBy . None && row . HasChildren )
1109+ {
1110+ var leafPlans = CollectLeafPlans ( row ) ;
1111+ if ( leafPlans . Count > 0 )
1112+ PlansSelected ? . Invoke ( this , leafPlans ) ;
1113+ }
1114+ else if ( row . PlanId > 0 && row . QueryId > 0 )
1115+ {
10171116 PlansSelected ? . Invoke ( this , new List < QueryStorePlan > { row . Plan } ) ;
1117+ }
1118+ }
1119+
1120+ /// <summary>
1121+ /// Recursively collects all leaf-level plans (PlanId > 0 and QueryId > 0) from a grouped row and its descendants.
1122+ /// </summary>
1123+ private static List < QueryStorePlan > CollectLeafPlans ( QueryStoreRow row )
1124+ {
1125+ var plans = new List < QueryStorePlan > ( ) ;
1126+ if ( row . Children . Count == 0 )
1127+ {
1128+ if ( row . PlanId > 0 && row . QueryId > 0 )
1129+ plans . Add ( row . Plan ) ;
1130+ }
1131+ else
1132+ {
1133+ foreach ( var child in row . Children )
1134+ plans . AddRange ( CollectLeafPlans ( child ) ) ;
1135+ }
1136+ return plans ;
10181137 }
10191138
10201139 private async void ViewHistory_Click ( object ? sender , RoutedEventArgs e )
0 commit comments