From db89b9f0f50034129a121c1b92ac304e6b0f0bed Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Tue, 30 Jun 2026 16:17:21 +0800 Subject: [PATCH 1/5] A11y: Announce category group names for overview page cards --- AIDevGallery/Pages/APIs/APIOverview.xaml | 2 ++ AIDevGallery/Pages/Scenarios/ScenarioOverviewPage.xaml | 3 +++ 2 files changed, 5 insertions(+) diff --git a/AIDevGallery/Pages/APIs/APIOverview.xaml b/AIDevGallery/Pages/APIs/APIOverview.xaml index 766e472fa..b8f30680c 100644 --- a/AIDevGallery/Pages/APIs/APIOverview.xaml +++ b/AIDevGallery/Pages/APIs/APIOverview.xaml @@ -108,11 +108,13 @@ Date: Wed, 1 Jul 2026 14:36:45 +0800 Subject: [PATCH 2/5] A11y: Announce control type for expandable navigation items --- .../Helpers/NavigationViewItemHelper.cs | 19 +++++++++++++++++++ .../Pages/APIs/APISelectionPage.xaml.cs | 2 ++ .../Scenarios/ScenarioSelectionPage.xaml.cs | 1 + 3 files changed, 22 insertions(+) diff --git a/AIDevGallery/Helpers/NavigationViewItemHelper.cs b/AIDevGallery/Helpers/NavigationViewItemHelper.cs index bd1a81cc8..c77565b1d 100644 --- a/AIDevGallery/Helpers/NavigationViewItemHelper.cs +++ b/AIDevGallery/Helpers/NavigationViewItemHelper.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Automation.Peers; using Microsoft.UI.Xaml.Controls; using System.Collections.Generic; using System.Linq; @@ -9,6 +11,23 @@ namespace AIDevGallery.Helpers; internal static class NavigationViewItemHelper { + public static void AppendControlTypeToName(this NavigationViewItem item, string label) + { + item.Loaded += (sender, _) => + { + if (sender is not NavigationViewItem navItem) + { + return; + } + + var controlType = FrameworkElementAutomationPeer.CreatePeerForElement(navItem)?.GetLocalizedControlType(); + if (!string.IsNullOrWhiteSpace(controlType)) + { + AutomationProperties.SetName(navItem, $"{label}, {controlType}"); + } + }; + } + public static IEnumerable Flatten(this IEnumerable source) { foreach (var item in source) diff --git a/AIDevGallery/Pages/APIs/APISelectionPage.xaml.cs b/AIDevGallery/Pages/APIs/APISelectionPage.xaml.cs index 3dc5355da..6983ae7e1 100644 --- a/AIDevGallery/Pages/APIs/APISelectionPage.xaml.cs +++ b/AIDevGallery/Pages/APIs/APISelectionPage.xaml.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using AIDevGallery.Helpers; using AIDevGallery.Models; using AIDevGallery.Samples; using AIDevGallery.Telemetry.Events; @@ -79,6 +80,7 @@ private void SetupAPIs() if (existingItem == null) { existingItem = new NavigationViewItem() { Content = CreateWrappedText(apiDefinition.Category), Icon = new FontIcon() { Glyph = "\uF0E2" }, SelectsOnInvoked = false, IsExpanded = true }; + existingItem.AppendControlTypeToName(apiDefinition.Category); NavView.MenuItems.Add(existingItem); } diff --git a/AIDevGallery/Pages/Scenarios/ScenarioSelectionPage.xaml.cs b/AIDevGallery/Pages/Scenarios/ScenarioSelectionPage.xaml.cs index d158a689b..dfb551b8b 100644 --- a/AIDevGallery/Pages/Scenarios/ScenarioSelectionPage.xaml.cs +++ b/AIDevGallery/Pages/Scenarios/ScenarioSelectionPage.xaml.cs @@ -111,6 +111,7 @@ private void SetUpScenarios(string? filter = null) foreach (var scenarioCategory in ScenarioCategoryHelpers.AllScenarioCategories) { var categoryMenu = new NavigationViewItem() { Content = scenarioCategory.Name, Icon = new FontIcon() { Glyph = scenarioCategory.Icon }, Tag = scenarioCategory }; + categoryMenu.AppendControlTypeToName(scenarioCategory.Name); ToolTip categoryToolTip = new() { Content = scenarioCategory.Name }; ToolTipService.SetToolTip(categoryMenu, categoryToolTip); From 40e629244d3aa63d8e55d2219d7b26a79063569b Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Thu, 2 Jul 2026 11:20:09 +0800 Subject: [PATCH 3/5] A11y: Avoid double popup announcement for performance details button --- AIDevGallery/Controls/SampleContainer.xaml | 13 ++----------- AIDevGallery/Controls/SampleContainer.xaml.cs | 7 +++++-- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/AIDevGallery/Controls/SampleContainer.xaml b/AIDevGallery/Controls/SampleContainer.xaml index 864dbbb3e..0f786b2ed 100644 --- a/AIDevGallery/Controls/SampleContainer.xaml +++ b/AIDevGallery/Controls/SampleContainer.xaml @@ -109,23 +109,14 @@ Visibility="Visible" /> diff --git a/AIDevGallery/Controls/SampleContainer.xaml.cs b/AIDevGallery/Controls/SampleContainer.xaml.cs index 9169eb7e7..ce8921c3f 100644 --- a/AIDevGallery/Controls/SampleContainer.xaml.cs +++ b/AIDevGallery/Controls/SampleContainer.xaml.cs @@ -342,12 +342,15 @@ public void ShowDebugInfo(string? contents) SampleDebugInfoButtonText.Visibility = Visibility.Collapsed; SampleDebugInfoButton.Visibility = Visibility.Collapsed; SampleDebugInfoButtonText.Text = string.Empty; - SampleDebugInfoContent.Text = string.Empty; + ToolTipService.SetToolTip(SampleDebugInfoButton, null); + Microsoft.UI.Xaml.Automation.AutomationProperties.SetFullDescription(SampleDebugInfoButton, string.Empty); return; } SampleDebugInfoButtonText.Text = contents.Split('\n')[0]; - SampleDebugInfoContent.Text = contents; + + ToolTipService.SetToolTip(SampleDebugInfoButton, contents); + Microsoft.UI.Xaml.Automation.AutomationProperties.SetFullDescription(SampleDebugInfoButton, contents); SampleDebugInfoButtonText.Visibility = Visibility.Visible; SampleDebugInfoButton.Visibility = Visibility.Visible; From 01e18f001ccedf64de5b36060789ef679cfe41b7 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Thu, 2 Jul 2026 16:52:26 +0800 Subject: [PATCH 4/5] A11y: Announce Semantic ComboBox result count for screen readers --- .../SharedCode/Controls/SemanticComboBox.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/AIDevGallery/Samples/SharedCode/Controls/SemanticComboBox.cs b/AIDevGallery/Samples/SharedCode/Controls/SemanticComboBox.cs index 9a0b2bf58..04a287508 100644 --- a/AIDevGallery/Samples/SharedCode/Controls/SemanticComboBox.cs +++ b/AIDevGallery/Samples/SharedCode/Controls/SemanticComboBox.cs @@ -88,11 +88,23 @@ private async void SuggestBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxT { // Since selecting an item will also change the text, // only listen to changes caused by user entering text. - if (args.Reason == AutoSuggestionBoxTextChangeReason.UserInput) + if (args.Reason != AutoSuggestionBoxTextChangeReason.UserInput) { - var searchResults = await Search(sender.Text); - sender.ItemsSource = searchResults.Select(item => item["Text"]); + return; } + + string query = sender.Text; + var searchResults = await Search(query); + + if (sender.Text != query) + { + return; + } + + var suggestions = searchResults.Select(item => item["Text"]).ToList(); + sender.ItemsSource = suggestions; + + NarratorHelper.Announce(sender, $"{suggestions.Count} result{(suggestions.Count == 1 ? string.Empty : "s")}", "SemanticComboBoxSuggestionsActivityId"); // } private static void OnEmbeddingGeneratorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) From 3dc74d21a5e141b5d96827e707bbb64a03dc2195 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Mon, 6 Jul 2026 14:59:26 +0800 Subject: [PATCH 5/5] A11y: Reuse automation peer and detach nav item Loaded handler --- AIDevGallery/Helpers/NavigationViewItemHelper.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/AIDevGallery/Helpers/NavigationViewItemHelper.cs b/AIDevGallery/Helpers/NavigationViewItemHelper.cs index c77565b1d..0fae3387a 100644 --- a/AIDevGallery/Helpers/NavigationViewItemHelper.cs +++ b/AIDevGallery/Helpers/NavigationViewItemHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Automation; using Microsoft.UI.Xaml.Automation.Peers; using Microsoft.UI.Xaml.Controls; @@ -13,19 +14,24 @@ internal static class NavigationViewItemHelper { public static void AppendControlTypeToName(this NavigationViewItem item, string label) { - item.Loaded += (sender, _) => + RoutedEventHandler? loadedHandler = null; + loadedHandler = (sender, _) => { if (sender is not NavigationViewItem navItem) { return; } - var controlType = FrameworkElementAutomationPeer.CreatePeerForElement(navItem)?.GetLocalizedControlType(); + var peer = FrameworkElementAutomationPeer.FromElement(navItem) ?? FrameworkElementAutomationPeer.CreatePeerForElement(navItem); + var controlType = peer?.GetLocalizedControlType(); if (!string.IsNullOrWhiteSpace(controlType)) { AutomationProperties.SetName(navItem, $"{label}, {controlType}"); + navItem.Loaded -= loadedHandler; } }; + + item.Loaded += loadedHandler; } public static IEnumerable Flatten(this IEnumerable source)