Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using Microsoft.Maui.Handlers;
using System.Threading.Tasks;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Automation.Peers;
using Xunit;

namespace Microsoft.Maui.DeviceTests
{
Expand All @@ -17,5 +22,85 @@ static int GetContentChildCount(ContentViewHandler contentViewHandler)
else
return 0;
}

static FrameworkElementAutomationPeer GetOrCreateAutomationPeer(ContentPanel contentPanel)
{
var peer = FrameworkElementAutomationPeer.FromElement(contentPanel);

if (peer == null)
{
peer = FrameworkElementAutomationPeer.CreatePeerForElement(contentPanel);
}

return (FrameworkElementAutomationPeer)peer;
}

[Fact(DisplayName = "ContentView With Description Prevents Duplicate Narrator Announcements")]
public async Task ContentViewWithDescriptionHidesChildrenFromNarrator()
{
SetupBuilder();

var label = new Label { Text = "Child Label Text" };
var contentView = new ContentView { Content = label };
SemanticProperties.SetDescription(contentView, "ContentView Description");

await CreateHandlerAndAddToWindow<ContentViewHandler>(contentView, async (handler) =>
{
var contentPanel = handler.PlatformView as ContentPanel;
Assert.NotNull(contentPanel);

// Call UpdateSemantics manually because handler.UpdateValue uses handler.VirtualView
// which doesn't have Semantics populated in the test context
var view = contentView as IView;
contentPanel.UpdateSemantics(view);

var peer = GetOrCreateAutomationPeer(contentPanel);
Assert.NotNull(peer);

var name = Microsoft.UI.Xaml.Automation.AutomationProperties.GetName(contentPanel);
Assert.Equal("ContentView Description", name);

// ControlType should be Text (not Group) to prevent Narrator from saying "group"
var controlType = peer.GetAutomationControlType();
Assert.Equal(AutomationControlType.Text, controlType);

// Children should be hidden to prevent duplicate announcements
var children = peer.GetChildren();
Assert.Null(children);

// LocalizedControlType should be empty to prevent suffix
var localizedControlType = peer.GetLocalizedControlType();
Assert.Equal(string.Empty, localizedControlType);

await Task.CompletedTask;
});
}

[Fact(DisplayName = "ContentView Without Description Shows Children Normally")]
public async Task ContentViewWithoutDescriptionShowsChildren()
{
SetupBuilder();

var label = new Label { Text = "Child Label Text" };
var contentView = new ContentView { Content = label };

await CreateHandlerAndAddToWindow<ContentViewHandler>(contentView, async (handler) =>
{
var contentPanel = handler.PlatformView as ContentPanel;
Assert.NotNull(contentPanel);

var peer = GetOrCreateAutomationPeer(contentPanel);
Assert.NotNull(peer);

var controlType = peer.GetAutomationControlType();
Assert.Equal(AutomationControlType.Custom, controlType);

var children = peer.GetChildren();
Assert.NotNull(children);
Assert.NotEmpty(children);

await Task.CompletedTask;
});
}
}
}
22 changes: 22 additions & 0 deletions src/Core/src/Platform/Windows/ContentPanel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Microsoft.Graphics.Canvas;
using Microsoft.Maui.Graphics;
Expand All @@ -9,6 +10,8 @@
#endif
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Hosting;
using Microsoft.UI.Xaml.Shapes;

Expand Down Expand Up @@ -73,6 +76,25 @@ public ContentPanel()
SizeChanged += ContentPanelSizeChanged;
}

// Custom automation peer prevents duplicate announcements when AutomationProperties.Name is set
protected override AutomationPeer OnCreateAutomationPeer() => new ContentPanelAutomationPeer(this);

partial class ContentPanelAutomationPeer : FrameworkElementAutomationPeer
{
internal ContentPanelAutomationPeer(ContentPanel owner) : base(owner) { }

bool HasDescription => !string.IsNullOrWhiteSpace(AutomationProperties.GetName(Owner));

protected override AutomationControlType GetAutomationControlTypeCore() =>
HasDescription ? AutomationControlType.Text : AutomationControlType.Custom;

protected override string GetLocalizedControlTypeCore() =>
HasDescription ? string.Empty : base.GetLocalizedControlTypeCore() ?? string.Empty;

protected override IList<AutomationPeer>? GetChildrenCore() =>
HasDescription ? null : base.GetChildrenCore();
}

void ContentPanelSizeChanged(object sender, SizeChangedEventArgs e)
{
if (_borderPath is null)
Expand Down
1 change: 1 addition & 0 deletions src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
#nullable enable
override Microsoft.Maui.Platform.ContentPanel.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!
Loading