Skip to content

Commit cec85d4

Browse files
author
msftbot[bot]
authored
Improve accessibility in InAppNotification (#3375)
The `InAppNotification` control is not fully accessible. It sends an automation event only when it contains text content and cannot easily be reached. ## PR Type What kind of change does this PR introduce? - Bugfix ## What is the current behavior? `InAppNotification` is sending automation notification only when it contains text content. The generated narrator strings are not properly localizable since they are concatenating a resource string with the content string but this may not be the appropriate way to do it in all cultures. The code is also forcing the focus on the notification control which may break the user flow. ## What is the new behavior? I've changed the automation integration: - The automation event has been changed to `LiveRegionChanged`. It will be sent for any kind of content. - The control has now a custom landmark so it can easily be reached through landmark navigation (caps lock + page up/down to select the landmark navigation mode then caps lock + left/right to navigate). - The notification control will be labelled by its presenter. This means that the narrator can automatically read the content accessible string as part of the landmark. This provides a better messaging to the user and reduce the need of custom code in the control itself. - The `peer.SetFocus()` code has been removed to not break the user flow by changing the focus to something that may only be transient (like a sync complete notification) - The dismiss button will now use a localized strings instead of its hardcoded one. ## PR Checklist Please check if your PR fulfills the following requirements: - [x] Tested code with current [supported SDKs](../readme.md#supported) - [ ] Pull Request has been submitted to the documentation repository [instructions](..\contributing.md#docs). Link: <!-- docs PR link --> - [ ] Sample in sample app has been added / updated (for bug fixes / features) - [ ] Icon has been created (if new sample) following the [Thumbnail Style Guide and templates](https://github.com/windows-toolkit/WindowsCommunityToolkit-design-assets) - [ ] Tests for the changes have been added (for bug fixes / features) (if applicable) - [ ] Header has been added to all new source files (run *build/UpdateHeaders.bat*) - [ ] Contains **NO** breaking changes I've removed or updated some resource strings.
2 parents 74b3dd7 + bc3e66d commit cec85d4

File tree

5 files changed

+31
-31
lines changed

5 files changed

+31
-31
lines changed

Microsoft.Toolkit.Uwp.UI.Controls/InAppNotification/InAppNotification.Events.cs

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6-
using Microsoft.Toolkit.Uwp.Extensions;
76
using Windows.UI.Xaml;
8-
using Windows.UI.Xaml.Automation;
97
using Windows.UI.Xaml.Automation.Peers;
108

119
namespace Microsoft.Toolkit.Uwp.UI.Controls
@@ -35,19 +33,6 @@ public partial class InAppNotification
3533
/// </summary>
3634
public event InAppNotificationClosedEventHandler Closed;
3735

38-
private static void AutomateTextNotification(AutomationPeer peer, string message)
39-
{
40-
if (peer != null)
41-
{
42-
peer.SetFocus();
43-
peer.RaiseNotificationEvent(
44-
AutomationNotificationKind.Other,
45-
AutomationNotificationProcessing.ImportantMostRecent,
46-
StringExtensions.GetLocalized("WindowsCommunityToolkit_InAppNotification_Events_NewNotificationMessage", "/Microsoft.Toolkit.Uwp.UI.Controls/Resources") + message,
47-
Guid.NewGuid().ToString());
48-
}
49-
}
50-
5136
private void DismissButton_Click(object sender, RoutedEventArgs e)
5237
{
5338
Dismiss(InAppNotificationDismissKind.User);
@@ -74,21 +59,23 @@ private void OnCurrentStateChanged(object sender, VisualStateChangedEventArgs e)
7459
private void OnNotificationVisible()
7560
{
7661
Opened?.Invoke(this, EventArgs.Empty);
77-
SetValue(AutomationProperties.NameProperty, StringExtensions.GetLocalized("WindowsCommunityToolkit_InAppNotification_NameProperty", "/Microsoft.Toolkit.Uwp.UI.Controls/Resources"));
78-
if (ContentTemplateRoot != null)
79-
{
80-
var peer = FrameworkElementAutomationPeer.CreatePeerForElement(ContentTemplateRoot);
81-
if (Content?.GetType() == typeof(string))
82-
{
83-
AutomateTextNotification(peer, Content.ToString());
84-
}
85-
}
8662
}
8763

8864
private void OnNotificationCollapsed()
8965
{
9066
Closed?.Invoke(this, new InAppNotificationClosedEventArgs(_lastDismissKind));
9167
Visibility = Visibility.Collapsed;
9268
}
69+
70+
private void RaiseAutomationNotification()
71+
{
72+
if (!AutomationPeer.ListenerExists(AutomationEvents.LiveRegionChanged))
73+
{
74+
return;
75+
}
76+
77+
var peer = FrameworkElementAutomationPeer.CreatePeerForElement(this);
78+
peer.RaiseAutomationEvent(AutomationEvents.LiveRegionChanged);
79+
}
9380
}
9481
}

Microsoft.Toolkit.Uwp.UI.Controls/InAppNotification/InAppNotification.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Linq;
8+
using Microsoft.Toolkit.Uwp.Extensions;
9+
using Microsoft.Toolkit.Uwp.UI.Extensions;
810
using Windows.UI.Xaml;
11+
using Windows.UI.Xaml.Automation;
912
using Windows.UI.Xaml.Controls;
1013

1114
namespace Microsoft.Toolkit.Uwp.UI.Controls
@@ -59,6 +62,7 @@ protected override void OnApplyTemplate()
5962
{
6063
_dismissButton.Visibility = ShowDismissButton ? Visibility.Visible : Visibility.Collapsed;
6164
_dismissButton.Click += DismissButton_Click;
65+
AutomationProperties.SetName(_dismissButton, StringExtensions.GetLocalized("WindowsCommunityToolkit_InAppNotification_DismissButton_AutomationName", "Microsoft.Toolkit.Uwp.UI.Controls/Resources"));
6266
}
6367

6468
if (_visualStateGroup != null)
@@ -72,6 +76,8 @@ protected override void OnApplyTemplate()
7276
UpdateContent(firstNotification);
7377
VisualStateManager.GoToState(this, StateContentVisible, true);
7478
}
79+
80+
AutomationProperties.SetLabeledBy(this, VisualTree.FindDescendant<ContentPresenter>(this));
7581
}
7682

7783
/// <summary>
@@ -240,6 +246,8 @@ private void UpdateContent(NotificationOptions notificationOptions)
240246
_contentProvider.Content = content;
241247
break;
242248
}
249+
250+
RaiseAutomationNotification();
243251
}
244252

245253
/// <summary>
@@ -276,11 +284,11 @@ private void Show(NotificationOptions notificationOptions)
276284

277285
if (shouldDisplayImmediately)
278286
{
279-
UpdateContent(notificationOptions);
280-
281287
Visibility = Visibility.Visible;
282288
VisualStateManager.GoToState(this, StateContentVisible, true);
283289

290+
UpdateContent(notificationOptions);
291+
284292
if (notificationOptions.Duration > 0)
285293
{
286294
_dismissTimer.Interval = TimeSpan.FromMilliseconds(notificationOptions.Duration);

Microsoft.Toolkit.Uwp.UI.Controls/InAppNotification/InAppNotification.xaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
<Setter Property="AnimationDuration" Value="0:0:0.100" />
2929
<Setter Property="VerticalOffset" Value="100" />
3030
<Setter Property="HorizontalOffset" Value="0" />
31+
<Setter Property="AutomationProperties.LandmarkType" Value="Custom" />
32+
<!-- The setter value is localized using x:Uid but we still need to set it explicitly to avoid a compiler warning -->
33+
<Setter x:Uid="WindowsCommunityToolkit_InAppNotification_LandmarkProperty" Property="AutomationProperties.LocalizedLandmarkType" Value="Notification" />
34+
<Setter Property="AutomationProperties.LiveSetting" Value="Assertive" />
3135
<Setter Property="Template" Value="{StaticResource MSEdgeNotificationTemplate}" />
3236
</Style>
3337

Microsoft.Toolkit.Uwp.UI.Controls/InAppNotification/Styles/MSEdgeNotificationStyle.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
BorderThickness="{StaticResource SystemControlMSEdgeNotificationButtonBorderThickness}"
5656
BorderBrush="{ThemeResource SystemControlMSEdgeNotificationButtonBorderBrush}">
5757
<ContentPresenter x:Name="Text"
58+
AutomationProperties.AccessibilityView="Raw"
5859
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
5960
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
6061
Content="{TemplateBinding Content}" />

Microsoft.Toolkit.Uwp.UI.Controls/Strings/en-US/Resources.resw

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,13 @@
205205
<value>GridSplitter</value>
206206
<comment>Narrator Resource for GridSplitter control</comment>
207207
</data>
208-
<data name="WindowsCommunityToolkit_InAppNotification_Events_NewNotificationMessage" xml:space="preserve">
209-
<value>New notification</value>
210-
<comment>Narrator resource for a new notification using the InAppNotification</comment>
208+
<data name="WindowsCommunityToolkit_InAppNotification_DismissButton_AutomationName" xml:space="preserve">
209+
<value>Dismiss</value>
210+
<comment>The automation name for the dismiss button of the InAppNotification control.</comment>
211211
</data>
212-
<data name="WindowsCommunityToolkit_InAppNotification_NameProperty" xml:space="preserve">
212+
<data name="WindowsCommunityToolkit_InAppNotification_LandmarkProperty.Value" xml:space="preserve">
213213
<value>Notification</value>
214-
<comment>Name property for InAppNotification</comment>
214+
<comment>The landmark name for the InAppNotification control. It is said by the narrator when using landmark navigation.</comment>
215215
</data>
216216
<data name="WindowsCommunityToolkit_TabView_CloseButton.[using:Windows.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
217217
<value>Close tab</value>

0 commit comments

Comments
 (0)