Fix screen reader button accessibility and category navigation (#4944)

* Fix UI automation peer wrapping and category focusability

* Add accessibility name to SettingsButton

* Avoid nesting ScrollViewer in generated panels with outer viewers

Inspect targetGrid and its parent hierarchy in Invoke-WPFUIElements to avoid adding an inner ScrollViewer when an outer ScrollViewer already exists.
This commit is contained in:
makhlwf
2026-08-09 18:02:53 -05:00
committed by GitHub
parent 53fc260cc0
commit afc3e1eec2
4 changed files with 86 additions and 46 deletions
+45 -26
View File
@@ -49,7 +49,7 @@ function Invoke-WPFUIElements {
# Add ColumnDefinitions to the target Grid
for ($i = 0; $i -lt $columncount; $i++) {
$colDef = New-Object Windows.Controls.ColumnDefinition
$colDef.Width = New-Object Windows.GridLength(1, [Windows.GridUnitType]::Star)
$colDef.Width = New-Object System.Windows.GridLength([double]1, [System.Windows.GridUnitType]::Star)
$targetGrid.ColumnDefinitions.Add($colDef) | Out-Null
}
@@ -113,36 +113,55 @@ function Invoke-WPFUIElements {
$dockPanelContainer = New-Object Windows.Controls.DockPanel
$border.Child = $dockPanelContainer
# Create an ItemsControl for application content
$itemsControl = New-Object Windows.Controls.ItemsControl
$itemsControl.HorizontalAlignment = 'Stretch'
$itemsControl.VerticalAlignment = 'Stretch'
# Create a StackPanel for application content controls
$stackPanelContainer = New-Object Windows.Controls.StackPanel
$stackPanelContainer.HorizontalAlignment = 'Stretch'
$stackPanelContainer.VerticalAlignment = 'Stretch'
# Set the ItemsPanel to a VirtualizingStackPanel
$itemsPanelTemplate = New-Object Windows.Controls.ItemsPanelTemplate
$factory = New-Object Windows.FrameworkElementFactory ([Windows.Controls.VirtualizingStackPanel])
$itemsPanelTemplate.VisualTree = $factory
$itemsControl.ItemsPanel = $itemsPanelTemplate
# Check if the target grid (or any ancestor) is already inside a ScrollViewer
$hasOuterScrollViewer = $false
$currentElement = $targetGrid
while ($null -ne $currentElement) {
if ($currentElement -is [System.Windows.Controls.ScrollViewer] -or $currentElement.GetType().Name -eq "ScrollViewer") {
$hasOuterScrollViewer = $true
break
}
$currentElement = $currentElement.Parent
}
# Set virtualization properties
$itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::IsVirtualizingProperty, $true)
$itemsControl.SetValue([Windows.Controls.VirtualizingStackPanel]::VirtualizationModeProperty, [Windows.Controls.VirtualizationMode]::Recycling)
if ($hasOuterScrollViewer) {
# Add StackPanel directly to DockPanel without nesting a ScrollViewer
[Windows.Controls.DockPanel]::SetDock($stackPanelContainer, [Windows.Controls.Dock]::Bottom)
$dockPanelContainer.Children.Add($stackPanelContainer) | Out-Null
}
else {
# Create a ScrollViewer for targets that do not already have an outer ScrollViewer
$scrollViewer = New-Object Windows.Controls.ScrollViewer
$scrollViewer.VerticalScrollBarVisibility = "Auto"
$scrollViewer.HorizontalScrollBarVisibility = "Disabled"
$scrollViewer.HorizontalAlignment = 'Stretch'
$scrollViewer.VerticalAlignment = 'Stretch'
$scrollViewer.Content = $stackPanelContainer
# Add the ItemsControl directly to the DockPanel
[Windows.Controls.DockPanel]::SetDock($itemsControl, [Windows.Controls.Dock]::Bottom)
$dockPanelContainer.Children.Add($itemsControl) | Out-Null
[Windows.Controls.DockPanel]::SetDock($scrollViewer, [Windows.Controls.Dock]::Bottom)
$dockPanelContainer.Children.Add($scrollViewer) | Out-Null
}
$panelcount++
# Now proceed with adding category labels and entries to $itemsControl
# Now proceed with adding category labels and entries to $stackPanelContainer
foreach ($category in ($organizedData[$panelKey].Keys | Sort-Object)) {
$count++
$label = New-Object Windows.Controls.Label
$label.Content = $category -replace ".*__", ""
$categoryCleanName = $category -replace ".*__", ""
$label.Content = $categoryCleanName
$label.Focusable = $true
$label.IsTabStop = $true
[System.Windows.Automation.AutomationProperties]::SetName($label, $categoryCleanName)
$label.SetResourceReference([Windows.Controls.Control]::FontSizeProperty, "HeaderFontSize")
$label.SetResourceReference([Windows.Controls.Control]::FontFamilyProperty, "HeaderFontFamily")
$label.UseLayoutRounding = $true
$itemsControl.Items.Add($label) | Out-Null
$stackPanelContainer.Children.Add($label) | Out-Null
$sync[$category] = $label
# Sort entries by type (checkboxes first, then buttons, then comboboxes, notes last) and then alphabetically by Content
@@ -177,7 +196,7 @@ function Invoke-WPFUIElements {
$label.SetResourceReference([Windows.Controls.Control]::ForegroundProperty, "MainForegroundColor")
$label.UseLayoutRounding = $true
$dockPanel.Children.Add($label) | Out-Null
$itemsControl.Items.Add($dockPanel) | Out-Null
$stackPanelContainer.Children.Add($dockPanel) | Out-Null
$sync[$entryInfo.Name] = $checkBox
$sync[$entryInfo.Name].IsChecked = (Get-WinUtilToggleStatus $entryInfo.Name)
@@ -215,7 +234,7 @@ function Invoke-WPFUIElements {
contentOff = if ($entryInfo.Content.Count -ge 2) { $entryInfo.Content[1] } else { $contentOn }
}
$itemsControl.Items.Add($toggleButton) | Out-Null
$stackPanelContainer.Children.Add($toggleButton) | Out-Null
$sync[$entryInfo.Name] = $toggleButton
@@ -295,7 +314,7 @@ function Invoke-WPFUIElements {
}
$horizontalStackPanel.Children.Add($comboBox) | Out-Null
$itemsControl.Items.Add($horizontalStackPanel) | Out-Null
$stackPanelContainer.Children.Add($horizontalStackPanel) | Out-Null
if ($entryInfo.Registry -and @($entryInfo.Registry)[0].Values) {
try {
@@ -387,7 +406,7 @@ function Invoke-WPFUIElements {
$button.Width = [math]::Max($baseWidth, 350)
}
[System.Windows.Automation.AutomationProperties]::SetName($button, $entryInfo.Content)
$itemsControl.Items.Add($button) | Out-Null
$stackPanelContainer.Children.Add($button) | Out-Null
$sync[$entryInfo.Name] = $button
@@ -414,7 +433,7 @@ function Invoke-WPFUIElements {
$radioButtonGroups[$entryInfo.GroupName] = $groupStackPanel
# Add the group container to the ItemsControl
$itemsControl.Items.Add($groupStackPanel) | Out-Null
$stackPanelContainer.Children.Add($groupStackPanel) | Out-Null
}
else {
# Retrieve the existing group container
@@ -459,7 +478,7 @@ function Invoke-WPFUIElements {
$textBlock.Inlines.Add($bulletBadge)
$textBlock.Inlines.Add($textRun)
$itemsControl.Items.Add($textBlock) | Out-Null
$stackPanelContainer.Children.Add($textBlock) | Out-Null
}
default {
@@ -519,7 +538,7 @@ function Invoke-WPFUIElements {
$sync[$textBlock.Name] = $textBlock
}
$itemsControl.Items.Add($horizontalStackPanel) | Out-Null
$stackPanelContainer.Children.Add($horizontalStackPanel) | Out-Null
$sync[$entryInfo.Name] = $checkBox
$sync[$entryInfo.Name].Add_Checked({