Adds automatic column resizing to ListViews

- Enables automatic horizontal and vertical scrollbars for ListViews in the UI to improve navigation.
- Introduces functions to dynamically calculate and apply column widths based on the visible content and header text.
- Triggers column auto-resizing across various modules whenever ListView data is updated or refreshed.
- Renames a path normalization function and updates an event handler parameter name for clarity.
This commit is contained in:
rbalsleyMSFT
2026-03-24 16:28:29 -07:00
parent bae29fd9c7
commit d6361dac4d
9 changed files with 251 additions and 8 deletions
+4 -4
View File
@@ -576,7 +576,7 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Applications ListView --> <!-- Applications ListView -->
<ListView x:Name="lstApplications" Grid.Column="0" Height="200" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"> <ListView x:Name="lstApplications" Grid.Column="0" Height="200" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
</ListView> </ListView>
<!-- Reorder Buttons --> <!-- Reorder Buttons -->
@@ -616,7 +616,7 @@
<Button x:Name="btnAddAppsScriptVariable" Content="Add Variable" HorizontalAlignment="Left" Margin="0,0,0,20" Padding="12,4" ToolTip="Add the key-value pair to the list"/> <Button x:Name="btnAddAppsScriptVariable" Content="Add Variable" HorizontalAlignment="Left" Margin="0,0,0,20" Padding="12,4" ToolTip="Add the key-value pair to the list"/>
<!-- ListView for AppsScriptVariables --> <!-- ListView for AppsScriptVariables -->
<ListView x:Name="lstAppsScriptVariables" Height="150" Margin="0,0,0,20" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"> <ListView x:Name="lstAppsScriptVariables" Height="150" Margin="0,0,0,20" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListView.View> <ListView.View>
<GridView> <GridView>
<GridViewColumn Header="Key" DisplayMemberBinding="{Binding Key}" Width="200"/> <GridViewColumn Header="Key" DisplayMemberBinding="{Binding Key}" Width="200"/>
@@ -938,7 +938,7 @@
<Button x:Name="btnRefreshAdditionalFFUs" Content="Refresh" DockPanel.Dock="Left" Padding="12,4" ToolTip="Refresh the list of FFU files from the capture folder"/> <Button x:Name="btnRefreshAdditionalFFUs" Content="Refresh" DockPanel.Dock="Left" Padding="12,4" ToolTip="Refresh the list of FFU files from the capture folder"/>
</DockPanel> </DockPanel>
<!-- ListView row --> <!-- ListView row -->
<ListView x:Name="lstAdditionalFFUs" Grid.Row="1" Margin="0" Height="150" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"> <ListView x:Name="lstAdditionalFFUs" Grid.Row="1" Margin="0" Height="150" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListView.View> <ListView.View>
<GridView> <GridView>
<GridViewColumn Header="FFU Name" DisplayMemberBinding="{Binding Name}" Width="300"/> <GridViewColumn Header="FFU Name" DisplayMemberBinding="{Binding Name}" Width="300"/>
@@ -965,7 +965,7 @@
<Button x:Name="btnCheckUSBDrives" Content="Check USB drives" DockPanel.Dock="Left" Padding="12,4"/> <Button x:Name="btnCheckUSBDrives" Content="Check USB drives" DockPanel.Dock="Left" Padding="12,4"/>
</DockPanel> </DockPanel>
<!-- ListView row --> <!-- ListView row -->
<ListView x:Name="lstUSBDrives" Grid.Row="1" Margin="0" Height="150" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}"> <ListView x:Name="lstUSBDrives" Grid.Row="1" Margin="0" Height="150" BorderThickness="1" BorderBrush="{DynamicResource {x:Static SystemColors.ActiveBorderBrushKey}}" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto">
<ListView.View> <ListView.View>
<GridView> <GridView>
@@ -182,6 +182,7 @@ function Add-BYOApplication {
# Refresh the ListView to show the changes # Refresh the ListView to show the changes
$listView.Items.Refresh() $listView.Items.Refresh()
Request-ListViewColumnAutoResize -ListView $listView
# Reset state # Reset state
$State.Data.editingBYOApplication = $null $State.Data.editingBYOApplication = $null
@@ -212,6 +213,7 @@ function Add-BYOApplication {
CopyStatus = "" CopyStatus = ""
} }
$listView.Items.Add($application) $listView.Items.Add($application)
Request-ListViewColumnAutoResize -ListView $listView
} }
# Clear form and update button states for both add and update operations # Clear form and update button states for both add and update operations
@@ -285,6 +287,7 @@ function Add-AppsScriptVariable {
} }
$State.Data.appsScriptVariablesDataList.Add($newItem) $State.Data.appsScriptVariablesDataList.Add($newItem)
$State.Controls.lstAppsScriptVariables.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray() $State.Controls.lstAppsScriptVariables.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstAppsScriptVariables
$State.Controls.txtAppsScriptKey.Clear() $State.Controls.txtAppsScriptKey.Clear()
$State.Controls.txtAppsScriptValue.Clear() $State.Controls.txtAppsScriptValue.Clear()
# Update the header checkbox state # Update the header checkbox state
@@ -311,6 +314,7 @@ function Remove-SelectedAppsScriptVariable {
$State.Data.appsScriptVariablesDataList.Remove($itemToRemove) $State.Data.appsScriptVariablesDataList.Remove($itemToRemove)
} }
$State.Controls.lstAppsScriptVariables.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray() $State.Controls.lstAppsScriptVariables.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstAppsScriptVariables
# Update the header checkbox state # Update the header checkbox state
if ($null -ne $State.Controls.chkSelectAllAppsScriptVariables) { if ($null -ne $State.Controls.chkSelectAllAppsScriptVariables) {
@@ -669,6 +669,7 @@ function Update-UIFromConfig {
} }
# Update the ListView's ItemsSource after populating the data list # Update the ListView's ItemsSource after populating the data list
$lstAppsScriptVars.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray() $lstAppsScriptVars.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray()
Request-ListViewColumnAutoResize -ListView $lstAppsScriptVars
# Update the header checkbox state # Update the header checkbox state
if ($null -ne $State.Controls.chkSelectAllAppsScriptVariables) { if ($null -ne $State.Controls.chkSelectAllAppsScriptVariables) {
Update-SelectAllHeaderCheckBoxState -ListView $lstAppsScriptVars -HeaderCheckBox $State.Controls.chkSelectAllAppsScriptVariables Update-SelectAllHeaderCheckBoxState -ListView $lstAppsScriptVars -HeaderCheckBox $State.Controls.chkSelectAllAppsScriptVariables
@@ -737,6 +738,7 @@ function Update-UIFromConfig {
} }
} }
$State.Controls.lstUSBDrives.Items.Refresh() $State.Controls.lstUSBDrives.Items.Refresh()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstUSBDrives
# Update the Select All header checkbox state # Update the Select All header checkbox state
$headerChk = $State.Controls.chkSelectAllUSBDrivesHeader $headerChk = $State.Controls.chkSelectAllUSBDrivesHeader
@@ -797,6 +799,7 @@ function Update-UIFromConfig {
} }
} }
$State.Controls.lstAdditionalFFUs.Items.Refresh() $State.Controls.lstAdditionalFFUs.Items.Refresh()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstAdditionalFFUs
$headerChk = $State.Controls.chkSelectAllAdditionalFFUs $headerChk = $State.Controls.chkSelectAllAdditionalFFUs
if ($null -ne $headerChk) { if ($null -ne $headerChk) {
Update-SelectAllHeaderCheckBoxState -ListView $State.Controls.lstAdditionalFFUs -HeaderCheckBox $headerChk Update-SelectAllHeaderCheckBoxState -ListView $State.Controls.lstAdditionalFFUs -HeaderCheckBox $headerChk
@@ -856,7 +859,7 @@ function Invoke-RestoreDefaults {
$rootPath = $State.FFUDevelopmentPath $rootPath = $State.FFUDevelopmentPath
# Normalize potential array values to single strings # Normalize potential array values to single strings
function Normalize-PathScalar { function Get-PathScalar {
param([object]$value) param([object]$value)
if ($null -eq $value) { return $null } if ($null -eq $value) { return $null }
if ($value -is [System.Array]) { if ($value -is [System.Array]) {
@@ -871,14 +874,14 @@ function Invoke-RestoreDefaults {
} }
$appsPath = Join-Path $rootPath 'Apps' $appsPath = Join-Path $rootPath 'Apps'
$driversRaw = Normalize-PathScalar -value $State.Controls.txtDriversFolder.Text $driversRaw = Get-PathScalar -value $State.Controls.txtDriversFolder.Text
if ([string]::IsNullOrWhiteSpace($driversRaw)) { if ([string]::IsNullOrWhiteSpace($driversRaw)) {
$driversPath = Join-Path $rootPath 'Drivers' $driversPath = Join-Path $rootPath 'Drivers'
} }
else { else {
$driversPath = $driversRaw $driversPath = $driversRaw
} }
$ffuCaptureRaw = Normalize-PathScalar -value $State.Controls.txtFFUCaptureLocation.Text $ffuCaptureRaw = Get-PathScalar -value $State.Controls.txtFFUCaptureLocation.Text
$ffuCapturePath = if ([string]::IsNullOrWhiteSpace($ffuCaptureRaw)) { Join-Path $rootPath 'FFU' } else { $ffuCaptureRaw } $ffuCapturePath = if ([string]::IsNullOrWhiteSpace($ffuCaptureRaw)) { Join-Path $rootPath 'FFU' } else { $ffuCaptureRaw }
$captureISOPath = Join-Path $rootPath 'WinPECaptureFFUFiles\WinPE-Capture.iso' $captureISOPath = Join-Path $rootPath 'WinPECaptureFFUFiles\WinPE-Capture.iso'
@@ -1060,6 +1063,7 @@ function Import-ConfigSupplementalAssets {
}) })
} }
$State.Controls.lstWingetResults.ItemsSource = $appsBuffer.ToArray() $State.Controls.lstWingetResults.ItemsSource = $appsBuffer.ToArray()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstWingetResults
$loadedWinget = $true $loadedWinget = $true
if ($null -ne $State.Controls.wingetSearchPanel) { if ($null -ne $State.Controls.wingetSearchPanel) {
$State.Controls.wingetSearchPanel.Visibility = 'Visible' $State.Controls.wingetSearchPanel.Visibility = 'Visible'
@@ -1194,6 +1198,7 @@ function Import-ConfigSupplementalAssets {
} }
} }
$State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels $State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels
Request-ListViewColumnAutoResize -ListView $State.Controls.lstDriverModels
if (Get-Command -Name Update-SelectAllHeaderCheckBoxState -ErrorAction SilentlyContinue) { if (Get-Command -Name Update-SelectAllHeaderCheckBoxState -ErrorAction SilentlyContinue) {
$headerChk = $State.Controls.chkSelectAllDriverModels $headerChk = $State.Controls.chkSelectAllDriverModels
if ($null -ne $headerChk) { if ($null -ne $headerChk) {
@@ -373,6 +373,7 @@ function Search-DriverModels {
} }
# The view will automatically refresh. No need to call .Refresh() explicitly for filtering. # The view will automatically refresh. No need to call .Refresh() explicitly for filtering.
Request-ListViewColumnAutoResize -ListView $State.Controls.lstDriverModels
$filteredCount = 0 $filteredCount = 0
if ($null -ne $collectionView) { if ($null -ne $collectionView) {
foreach ($item in $collectionView) { $filteredCount++ } foreach ($item in $collectionView) { $filteredCount++ }
@@ -715,6 +716,7 @@ function Import-DriversJson {
# Update the UI and apply any existing filter # Update the UI and apply any existing filter
$State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels $State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels
Request-ListViewColumnAutoResize -ListView $State.Controls.lstDriverModels
Search-DriverModels -filterText $State.Controls.txtModelFilter.Text -State $State Search-DriverModels -filterText $State.Controls.txtModelFilter.Text -State $State
$message = "Driver import complete.`nNew models added: $newModelsAdded`nExisting models updated: $existingModelsUpdated" $message = "Driver import complete.`nNew models added: $newModelsAdded`nExisting models updated: $existingModelsUpdated"
@@ -786,6 +788,7 @@ function Invoke-GetModels {
# Update the UI ItemsSource to point to the new list and clear the filter # Update the UI ItemsSource to point to the new list and clear the filter
$State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels $State.Controls.lstDriverModels.ItemsSource = $State.Data.allDriverModels
Request-ListViewColumnAutoResize -ListView $State.Controls.lstDriverModels
$State.Controls.txtModelFilter.Text = "" $State.Controls.txtModelFilter.Text = ""
if ($State.Data.allDriverModels.Count -gt 0) { if ($State.Data.allDriverModels.Count -gt 0) {
@@ -24,7 +24,7 @@ function Register-EventHandlers {
# Define a handler to validate pasted text, ensuring it's only integers # Define a handler to validate pasted text, ensuring it's only integers
$integerPastingHandler = { $integerPastingHandler = {
param($sender, $pastingEventArgs) param($eventSource, $pastingEventArgs)
if ($pastingEventArgs.DataObject.GetDataPresent([string])) { if ($pastingEventArgs.DataObject.GetDataPresent([string])) {
$pastedText = $pastingEventArgs.DataObject.GetData([string]) $pastedText = $pastingEventArgs.DataObject.GetData([string])
# Check if the pasted text consists ONLY of one or more digits. # Check if the pasted text consists ONLY of one or more digits.
@@ -345,6 +345,7 @@ function Register-EventHandlers {
$driveObject | Add-Member -MemberType NoteProperty -Name 'IsSelected' -Value $false -Force $driveObject | Add-Member -MemberType NoteProperty -Name 'IsSelected' -Value $false -Force
$localState.Controls.lstUSBDrives.Items.Add($driveObject) $localState.Controls.lstUSBDrives.Items.Add($driveObject)
} }
Request-ListViewColumnAutoResize -ListView $localState.Controls.lstUSBDrives
if ($localState.Controls.lstUSBDrives.Items.Count -gt 0) { if ($localState.Controls.lstUSBDrives.Items.Count -gt 0) {
$localState.Controls.lstUSBDrives.SelectedIndex = 0 $localState.Controls.lstUSBDrives.SelectedIndex = 0
} }
@@ -591,6 +591,9 @@ function Initialize-DynamicUIElements {
} }
) )
# Keep driver model columns sized to the current visible content.
Enable-ListViewColumnAutoResize -ListView $State.Controls.lstDriverModels -FixedColumnIndexes @(0)
# Winget Search ListView setup # Winget Search ListView setup
$wingetGridView = New-Object System.Windows.Controls.GridView $wingetGridView = New-Object System.Windows.Controls.GridView
$State.Controls.lstWingetResults.View = $wingetGridView # Assign GridView to ListView first $State.Controls.lstWingetResults.View = $wingetGridView # Assign GridView to ListView first
@@ -750,6 +753,9 @@ function Initialize-DynamicUIElements {
} }
) )
# Keep Winget result columns sized to the current visible content.
Enable-ListViewColumnAutoResize -ListView $State.Controls.lstWingetResults -FixedColumnIndexes @(0)
# BYO Applications ListView setup # BYO Applications ListView setup
$byoAppsGridView = New-Object System.Windows.Controls.GridView $byoAppsGridView = New-Object System.Windows.Controls.GridView
$State.Controls.lstApplications.View = $byoAppsGridView $State.Controls.lstApplications.View = $byoAppsGridView
@@ -773,6 +779,9 @@ function Initialize-DynamicUIElements {
Add-SortableColumn -gridView $byoAppsGridView -header "Ignore Exit Codes" -binding "IgnoreExitCodes" -width 120 -headerHorizontalAlignment Left Add-SortableColumn -gridView $byoAppsGridView -header "Ignore Exit Codes" -binding "IgnoreExitCodes" -width 120 -headerHorizontalAlignment Left
Add-SortableColumn -gridView $byoAppsGridView -header "Copy Status" -binding "CopyStatus" -width 150 -headerHorizontalAlignment Left Add-SortableColumn -gridView $byoAppsGridView -header "Copy Status" -binding "CopyStatus" -width 150 -headerHorizontalAlignment Left
# Keep BYO application columns sized to the current visible content.
Enable-ListViewColumnAutoResize -ListView $State.Controls.lstApplications -FixedColumnIndexes @(0)
# Apps Script Variables ListView setup # Apps Script Variables ListView setup
# Bind ItemsSource to the data list # Bind ItemsSource to the data list
$State.Controls.lstAppsScriptVariables.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray() $State.Controls.lstAppsScriptVariables.ItemsSource = $State.Data.appsScriptVariablesDataList.ToArray()
@@ -826,6 +835,9 @@ function Initialize-DynamicUIElements {
} }
} }
) )
# Keep apps script variable columns sized to the current visible content.
Enable-ListViewColumnAutoResize -ListView $State.Controls.lstAppsScriptVariables -FixedColumnIndexes @(0)
} }
else { else {
WriteLog "Warning: lstAppsScriptVariables.View is not a GridView. Selectable column not added, and sorting cannot be enabled." WriteLog "Warning: lstAppsScriptVariables.View is not a GridView. Selectable column not added, and sorting cannot be enabled."
@@ -899,6 +911,9 @@ function Initialize-DynamicUIElements {
} }
} }
) )
# Keep USB drive columns sized to the current visible content.
Enable-ListViewColumnAutoResize -ListView $State.Controls.lstUSBDrives -FixedColumnIndexes @(0)
} }
else { else {
WriteLog "Warning: lstUSBDrives.View is not a GridView. Selectable column not added, and sorting cannot be enabled." WriteLog "Warning: lstUSBDrives.View is not a GridView. Selectable column not added, and sorting cannot be enabled."
@@ -945,6 +960,9 @@ function Initialize-DynamicUIElements {
} }
} }
) )
# Keep additional FFU columns sized to the current visible content.
Enable-ListViewColumnAutoResize -ListView $State.Controls.lstAdditionalFFUs -FixedColumnIndexes @(0)
} }
else { else {
WriteLog "Warning: lstAdditionalFFUs.View is not a GridView. Selectable column not added, and sorting cannot be enabled." WriteLog "Warning: lstAdditionalFFUs.View is not a GridView. Selectable column not added, and sorting cannot be enabled."
@@ -20,6 +20,7 @@ function Update-ListViewPriorities {
} }
} }
$ListView.Items.Refresh() $ListView.Items.Refresh()
Request-ListViewColumnAutoResize -ListView $ListView
} }
# Function to move selected item to the top # Function to move selected item to the top
@@ -133,6 +134,7 @@ function Update-ListViewItemStatus {
if ($null -ne $itemToUpdate) { if ($null -ne $itemToUpdate) {
$itemToUpdate.$StatusProperty = $StatusValue $itemToUpdate.$StatusProperty = $StatusValue
$ListView.Items.Refresh() # Refresh the view to show the change $ListView.Items.Refresh() # Refresh the view to show the change
Request-ListViewColumnAutoResize -ListView $ListView
} }
else { else {
# Log if item not found (for debugging) # Log if item not found (for debugging)
@@ -494,6 +496,209 @@ function Add-SelectableGridViewColumn {
WriteLog "Add-SelectableGridViewColumn: Successfully added selectable column to '$($ListView.Name)'." WriteLog "Add-SelectableGridViewColumn: Successfully added selectable column to '$($ListView.Name)'."
} }
# Function to request a deferred GridView column auto-size pass
function Request-ListViewColumnAutoResize {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[System.Windows.Controls.ListView]$ListView
)
# Skip startup calls until the visual tree has finished loading.
if (-not $ListView.IsLoaded) {
return
}
# Ensure the ListView has registered auto-resize metadata before scheduling work.
$autoResizeStateKey = 'FFU.ListViewColumnAutoResizeState'
if (-not $ListView.Resources.Contains($autoResizeStateKey)) {
return
}
if (-not ($ListView.View -is [System.Windows.Controls.GridView])) {
return
}
$autoResizeState = $ListView.Resources[$autoResizeStateKey]
if ($autoResizeState.ResizePending) {
return
}
$autoResizeState.ResizePending = $true
$previousErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = 'Stop'
$gridView = [System.Windows.Controls.GridView]$ListView.View
$fixedColumnIndexes = @($autoResizeState.FixedColumnIndexes)
$visibleItems = [System.Collections.Generic.List[object]]::new()
if ($null -ne $ListView.ItemsSource) {
$collectionView = [System.Windows.Data.CollectionViewSource]::GetDefaultView($ListView.ItemsSource)
if ($null -ne $collectionView) {
foreach ($visibleItem in $collectionView) {
$visibleItems.Add($visibleItem)
}
}
}
else {
foreach ($visibleItem in $ListView.Items) {
$visibleItems.Add($visibleItem)
}
}
$ListView.UpdateLayout()
$columnIndex = 0
foreach ($column in $gridView.Columns) {
if ($fixedColumnIndexes -contains $columnIndex) {
$columnIndex++
continue
}
if ($null -eq $column) {
$columnIndex++
continue
}
$headerText = ""
$propertyName = $null
if ($null -ne $column.DisplayMemberBinding -and $null -ne $column.DisplayMemberBinding.Path) {
$propertyName = [string]$column.DisplayMemberBinding.Path.Path
}
if ($column.Header -is [System.Windows.Controls.GridViewColumnHeader]) {
if (-not [string]::IsNullOrWhiteSpace([string]$column.Header.Content)) {
$headerText = [string]$column.Header.Content
}
if ([string]::IsNullOrWhiteSpace($propertyName) -and -not [string]::IsNullOrWhiteSpace([string]$column.Header.Tag)) {
$propertyName = [string]$column.Header.Tag
}
}
elseif (-not [string]::IsNullOrWhiteSpace([string]$column.Header)) {
$headerText = [string]$column.Header
}
if ([string]::IsNullOrWhiteSpace($headerText)) {
$headerText = $propertyName
}
$headerMeasureBlock = New-Object System.Windows.Controls.TextBlock
$headerMeasureBlock.Text = if ([string]::IsNullOrWhiteSpace($headerText)) { ' ' } else { $headerText }
$headerMeasureBlock.FontFamily = $ListView.FontFamily
$headerMeasureBlock.FontSize = $ListView.FontSize
$headerMeasureBlock.FontStyle = $ListView.FontStyle
$headerMeasureBlock.FontWeight = $ListView.FontWeight
$headerMeasureBlock.FontStretch = $ListView.FontStretch
$headerMeasureBlock.Measure([System.Windows.Size]::new([double]::PositiveInfinity, [double]::PositiveInfinity))
$calculatedWidth = [math]::Ceiling($headerMeasureBlock.DesiredSize.Width + 36)
foreach ($item in $visibleItems) {
if ($null -eq $item -or [string]::IsNullOrWhiteSpace($propertyName)) {
continue
}
$itemProperty = $null
if ($null -ne $item.PSObject -and $null -ne $item.PSObject.Properties) {
$matchedProperties = $item.PSObject.Properties.Match($propertyName)
if ($null -ne $matchedProperties -and $matchedProperties.Count -gt 0) {
$itemProperty = $matchedProperties | Select-Object -First 1
}
}
if ($null -eq $itemProperty) {
continue
}
$itemText = [string]$itemProperty.Value
$extraWidth = 28
switch ($propertyName) {
'Architecture' {
$extraWidth = 52
}
'AdditionalExitCodes' {
$extraWidth = 44
}
'IgnoreNonZeroExitCodes' {
$itemText = ' '
$extraWidth = 48
}
'IgnoreExitCodes' {
$extraWidth = 28
}
}
$itemMeasureBlock = New-Object System.Windows.Controls.TextBlock
$itemMeasureBlock.Text = if ([string]::IsNullOrWhiteSpace($itemText)) { ' ' } else { $itemText }
$itemMeasureBlock.FontFamily = $ListView.FontFamily
$itemMeasureBlock.FontSize = $ListView.FontSize
$itemMeasureBlock.FontStyle = $ListView.FontStyle
$itemMeasureBlock.FontWeight = $ListView.FontWeight
$itemMeasureBlock.FontStretch = $ListView.FontStretch
$itemMeasureBlock.Measure([System.Windows.Size]::new([double]::PositiveInfinity, [double]::PositiveInfinity))
$itemWidth = [math]::Ceiling($itemMeasureBlock.DesiredSize.Width + $extraWidth)
if ($itemWidth -gt $calculatedWidth) {
$calculatedWidth = $itemWidth
}
}
if ($propertyName -eq 'IgnoreNonZeroExitCodes') {
$calculatedWidth = [math]::Max($calculatedWidth, 120)
}
$column.Width = [math]::Max($calculatedWidth, 40)
$columnIndex++
}
}
catch {
WriteLog "Request-ListViewColumnAutoResize: Failed for '$($ListView.Name)': $($_.Exception.Message)"
if (-not [string]::IsNullOrWhiteSpace($_.InvocationInfo.PositionMessage)) {
WriteLog $_.InvocationInfo.PositionMessage
}
if (-not [string]::IsNullOrWhiteSpace($_.ScriptStackTrace)) {
WriteLog $_.ScriptStackTrace
}
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
$autoResizeState.ResizePending = $false
}
}
# Function to enable reusable auto-resizing for GridView-backed ListViews
function Enable-ListViewColumnAutoResize {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[System.Windows.Controls.ListView]$ListView,
[int[]]$FixedColumnIndexes = @()
)
$autoResizeStateKey = 'FFU.ListViewColumnAutoResizeState'
# Only GridView-backed lists can participate in column auto-sizing.
if (-not ($ListView.View -is [System.Windows.Controls.GridView])) {
WriteLog "Enable-ListViewColumnAutoResize: ListView '$($ListView.Name)' is not using a GridView. Skipping registration."
return
}
if ($ListView.Resources.Contains($autoResizeStateKey)) {
return
}
$autoResizeState = [PSCustomObject]@{
FixedColumnIndexes = @($FixedColumnIndexes)
ResizePending = $false
}
$ListView.Resources[$autoResizeStateKey] = $autoResizeState
}
# Function to update the IsChecked state of a "Select All" header CheckBox # Function to update the IsChecked state of a "Select All" header CheckBox
function Update-SelectAllHeaderCheckBoxState { function Update-SelectAllHeaderCheckBoxState {
param( param(
@@ -573,6 +778,7 @@ function Invoke-ListViewItemToggle {
# Toggle the IsSelected property # Toggle the IsSelected property
$selectedItem.IsSelected = -not $selectedItem.IsSelected $selectedItem.IsSelected = -not $selectedItem.IsSelected
$ListView.Items.Refresh() $ListView.Items.Refresh()
Request-ListViewColumnAutoResize -ListView $ListView
# Update the 'Select All' header checkbox state # Update the 'Select All' header checkbox state
$headerChk = $State.Controls[$HeaderCheckBoxKeyName] $headerChk = $State.Controls[$HeaderCheckBoxKeyName]
@@ -743,6 +949,8 @@ function Invoke-ListViewSort {
$newView.Filter = $existingFilter $newView.Filter = $existingFilter
} }
} }
Request-ListViewColumnAutoResize -ListView $listView
} }
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@@ -1043,6 +1251,7 @@ function Clear-ListViewContent {
} }
$ListViewControl.Items.Refresh() $ListViewControl.Items.Refresh()
Request-ListViewColumnAutoResize -ListView $ListViewControl
# Clear any specified textboxes # Clear any specified textboxes
if ($null -ne $TextBoxesToClear) { if ($null -ne $TextBoxesToClear) {
@@ -59,6 +59,7 @@ function Search-WingetApps {
# Update the ListView's ItemsSource using the passed-in State object # Update the ListView's ItemsSource using the passed-in State object
$State.Controls.lstWingetResults.ItemsSource = $finalAppList.ToArray() $State.Controls.lstWingetResults.ItemsSource = $finalAppList.ToArray()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstWingetResults
# Update status text # Update status text
$statusText = "" $statusText = ""
@@ -178,6 +179,7 @@ function Import-WingetList {
} }
$State.Controls.lstWingetResults.ItemsSource = $newAppListForItemsSource.ToArray() $State.Controls.lstWingetResults.ItemsSource = $newAppListForItemsSource.ToArray()
Request-ListViewColumnAutoResize -ListView $State.Controls.lstWingetResults
$State.Controls.txtAppListJsonPath.Text = $ofd.FileName $State.Controls.txtAppListJsonPath.Text = $ofd.FileName
[System.Windows.MessageBox]::Show("Winget app list imported successfully.", "Success", "OK", "Information") [System.Windows.MessageBox]::Show("Winget app list imported successfully.", "Success", "OK", "Information")
@@ -268,6 +268,7 @@ function Update-AdditionalFFUList {
foreach ($it in $items) { $listView.Items.Add($it) | Out-Null } foreach ($it in $items) { $listView.Items.Add($it) | Out-Null }
WriteLog "Additional FFUs: Found $($listView.Items.Count) FFU files in $ffuFolder." WriteLog "Additional FFUs: Found $($listView.Items.Count) FFU files in $ffuFolder."
} }
Request-ListViewColumnAutoResize -ListView $listView
$headerChk = $State.Controls.chkSelectAllAdditionalFFUs $headerChk = $State.Controls.chkSelectAllAdditionalFFUs
if ($null -ne $headerChk) { if ($null -ne $headerChk) {
Update-SelectAllHeaderCheckBoxState -ListView $listView -HeaderCheckBox $headerChk Update-SelectAllHeaderCheckBoxState -ListView $listView -HeaderCheckBox $headerChk