From f09c98906af90bcc5a737861e851da51aacfadce Mon Sep 17 00:00:00 2001 From: rbalsleyMSFT <53497092+rbalsleyMSFT@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:16:01 -0800 Subject: [PATCH] Scopes select-all to visible filtered list items Enhances grid view selection to optionally target only visible items when a filter is applied, preventing accidental selection or deselection of hidden rows via the header checkbox. Updates the save logic to process the unfiltered master dataset, ensuring that previously selected but currently hidden items are safely preserved during save operations. --- .../FFUUI.Core/FFUUI.Core.Drivers.psm1 | 5 +- .../FFUUI.Core/FFUUI.Core.Initialize.psm1 | 4 +- .../FFUUI.Core/FFUUI.Core.Shared.psm1 | 80 +++++++++++++++---- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 index 361bdf4..40c207d 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Drivers.psm1 @@ -387,7 +387,10 @@ function Save-DriversJson { [psobject]$State ) WriteLog "Save-DriversJson function called." - $selectedDrivers = @($State.Controls.lstDriverModels.Items | Where-Object { $_.IsSelected }) + + # Save from the master model list so filtered-out selected rows are preserved. + $driverSelectionSource = if ($null -ne $State.Data.allDriverModels) { $State.Data.allDriverModels } else { $State.Controls.lstDriverModels.Items } + $selectedDrivers = @($driverSelectionSource | Where-Object { $_.IsSelected }) if (-not $selectedDrivers) { [System.Windows.MessageBox]::Show("No drivers selected to save.", "Save Drivers", [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Information) diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 index e43e6b9..350bfff 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Initialize.psm1 @@ -366,8 +366,8 @@ function Initialize-DynamicUIElements { $driverModelsGridView = New-Object System.Windows.Controls.GridView $State.Controls.lstDriverModels.View = $driverModelsGridView # Assign GridView to ListView first - # Add the selectable column using the new function - Add-SelectableGridViewColumn -ListView $State.Controls.lstDriverModels -State $State -HeaderCheckBoxKeyName "chkSelectAllDriverModels" -ColumnWidth 70 + # Add the selectable column and scope header select-all to visible filtered rows. + Add-SelectableGridViewColumn -ListView $State.Controls.lstDriverModels -State $State -HeaderCheckBoxKeyName "chkSelectAllDriverModels" -ColumnWidth 70 -HeaderSelectionAffectsVisibleItemsOnly # Add other sortable columns with left-aligned headers Add-SortableColumn -gridView $driverModelsGridView -header "Make" -binding "Make" -width 100 -headerHorizontalAlignment Left diff --git a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Shared.psm1 b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Shared.psm1 index 0914251..a120d32 100644 --- a/FFUDevelopment/FFUUI.Core/FFUUI.Core.Shared.psm1 +++ b/FFUDevelopment/FFUUI.Core/FFUUI.Core.Shared.psm1 @@ -329,7 +329,8 @@ function Add-SelectableGridViewColumn { [string]$HeaderCheckBoxKeyName, [Parameter(Mandatory)] [double]$ColumnWidth, - [string]$IsSelectedPropertyName = "IsSelected" + [string]$IsSelectedPropertyName = "IsSelected", + [switch]$HeaderSelectionAffectsVisibleItemsOnly ) # Ensure the ListView has a GridView @@ -343,10 +344,11 @@ function Add-SelectableGridViewColumn { $headerCheckBox = New-Object System.Windows.Controls.CheckBox $headerCheckBox.HorizontalAlignment = [System.Windows.HorizontalAlignment]::Center - # MODIFICATION: Store the actual ListView object in the header's Tag + # Store header metadata, including whether select-all should only affect visible rows. $headerTagObject = [PSCustomObject]@{ - PropertyName = $IsSelectedPropertyName - ListViewControl = $ListView + PropertyName = $IsSelectedPropertyName + ListViewControl = $ListView + HeaderSelectionAffectsVisibleItemsOnly = [bool]$HeaderSelectionAffectsVisibleItemsOnly } $headerCheckBox.Tag = $headerTagObject @@ -356,8 +358,24 @@ function Add-SelectableGridViewColumn { $localPropertyName = $tagData.PropertyName $actualListView = $tagData.ListViewControl - $collectionToUpdate = if ($null -ne $actualListView.ItemsSource) { $actualListView.ItemsSource } else { $actualListView.Items } - if ($null -ne $collectionToUpdate) { + # Select either visible view items only (filtered scope) or the full backing list. + $collectionToUpdate = @() + if ($tagData.HeaderSelectionAffectsVisibleItemsOnly -and $null -ne $actualListView.ItemsSource) { + $collectionView = [System.Windows.Data.CollectionViewSource]::GetDefaultView($actualListView.ItemsSource) + if ($null -ne $collectionView) { + foreach ($visibleItem in $collectionView) { + $collectionToUpdate += $visibleItem + } + } + } + elseif ($null -ne $actualListView.ItemsSource) { + $collectionToUpdate = @($actualListView.ItemsSource) + } + elseif ($actualListView.HasItems) { + $collectionToUpdate = @($actualListView.Items) + } + + if ($collectionToUpdate.Count -gt 0) { foreach ($item in $collectionToUpdate) { $item.$($localPropertyName) = $true } $actualListView.Items.Refresh() } @@ -370,8 +388,24 @@ function Add-SelectableGridViewColumn { $localPropertyName = $tagData.PropertyName $actualListView = $tagData.ListViewControl - $collectionToUpdate = if ($null -ne $actualListView.ItemsSource) { $actualListView.ItemsSource } else { $actualListView.Items } - if ($null -ne $collectionToUpdate) { + # Clear either visible view items only (filtered scope) or the full backing list. + $collectionToUpdate = @() + if ($tagData.HeaderSelectionAffectsVisibleItemsOnly -and $null -ne $actualListView.ItemsSource) { + $collectionView = [System.Windows.Data.CollectionViewSource]::GetDefaultView($actualListView.ItemsSource) + if ($null -ne $collectionView) { + foreach ($visibleItem in $collectionView) { + $collectionToUpdate += $visibleItem + } + } + } + elseif ($null -ne $actualListView.ItemsSource) { + $collectionToUpdate = @($actualListView.ItemsSource) + } + elseif ($actualListView.HasItems) { + $collectionToUpdate = @($actualListView.Items) + } + + if ($collectionToUpdate.Count -gt 0) { foreach ($item in $collectionToUpdate) { $item.$($localPropertyName) = $false } $actualListView.Items.Refresh() } @@ -446,24 +480,38 @@ function Update-SelectAllHeaderCheckBoxState { [System.Windows.Controls.CheckBox]$HeaderCheckBox ) - $collectionToInspect = $null - if ($null -ne $ListView.ItemsSource) { + # Determine whether this header should evaluate only visible (filtered) rows. + $inspectVisibleItemsOnly = $false + if ($null -ne $HeaderCheckBox.Tag -and $null -ne $HeaderCheckBox.Tag.PSObject.Properties['HeaderSelectionAffectsVisibleItemsOnly']) { + $inspectVisibleItemsOnly = [bool]$HeaderCheckBox.Tag.HeaderSelectionAffectsVisibleItemsOnly + } + + # Build the collection to inspect based on scope (visible view vs full source). + $collectionToInspect = @() + if ($inspectVisibleItemsOnly -and $null -ne $ListView.ItemsSource) { + $collectionView = [System.Windows.Data.CollectionViewSource]::GetDefaultView($ListView.ItemsSource) + if ($null -ne $collectionView) { + foreach ($visibleItem in $collectionView) { + $collectionToInspect += $visibleItem + } + } + } + elseif ($null -ne $ListView.ItemsSource) { $collectionToInspect = @($ListView.ItemsSource) } elseif ($ListView.HasItems) { - # Check if Items collection has items and ItemsSource is null $collectionToInspect = @($ListView.Items) } - # If no items to inspect (either ItemsSource was null and Items was empty, or ItemsSource was empty) - if ($null -eq $collectionToInspect -or $collectionToInspect.Count -eq 0) { + # If no items are available in the selected scope, force unchecked. + if ($collectionToInspect.Count -eq 0) { $HeaderCheckBox.IsChecked = $false return } $selectedCount = ($collectionToInspect | Where-Object { $_.IsSelected }).Count WriteLog "Update-SelectAllHeaderCheckBoxState: Selected count is $selectedCount for ListView '$($ListView.Name)'." - $totalItemCount = $collectionToInspect.Count # Get the total count from the collection being inspected + $totalItemCount = $collectionToInspect.Count WriteLog "Update-SelectAllHeaderCheckBoxState: Total item count is $totalItemCount for ListView '$($ListView.Name)'." if ($totalItemCount -eq 0) { @@ -597,7 +645,7 @@ function Invoke-ListViewSort { $val = $_.$property if ($null -eq $val) { '' } else { $val } } - Ascending = $State.Flags.lastSortAscending + Ascending = $State.Flags.lastSortAscending } $sortCriteria = [System.Collections.Generic.List[hashtable]]::new() @@ -644,7 +692,7 @@ function Invoke-ListViewSort { $val = Invoke-Command -ScriptBlock $expressionScriptBlock -ArgumentList $_ if ($null -eq $val) { '' } else { $val } } - Ascending = $true + Ascending = $true } $sortCriteria.Add($secondarySortDefinition) }