Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes#31159
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR updates the Label Feature Matrix test sample and reference images following iOS performance improvements from PR #30864. The changes modify the test framework to support more granular testing of formatted text scenarios and update the UI test automation accordingly.
- Updated Label Feature Matrix sample to separate simple formatted text from complex scenarios
- Modified test automation to include new SimpleFormattedText interaction steps
- Updated reference images across platforms (Windows, Android, iOS) to reflect current rendering
Reviewed Changes
Copilot reviewed 4 out of 31 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
LabelFeatureTests.cs |
Added SimpleFormattedText constant and updated test methods to interact with new checkbox option |
LabelViewModel.cs |
Removed default FormattedText initialization and set LineHeight default to -1 |
LabelOptionsPage.xaml.cs |
Added event handler for SimpleFormattedText checkbox to create basic formatted text |
LabelOptionsPage.xaml |
Added UI checkbox for "Single Line Formatted Text" option with proper AutomationId |
Comments suppressed due to low confidence (1)
src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/LabelFeatureTests.cs:66
- Removing the WaitForElement check for "This is a Basic Label" may cause test instability. Since this text is now created via the SimpleFormattedText checkbox, the test should either wait for this element after tapping the checkbox or verify the UI state is ready before proceeding.
{
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
albyrock87
left a comment
There was a problem hiding this comment.
I see there are still a lot of failures in the CI regarding Entry, which is what I was experiencing locally with my PR.
In all those use cases I was wondering about the expectations, like sometimes I really can't tell which is the expected behavior.
ca1d952 to
109383b
Compare
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
@jsuarezruiz, it looks like the drop folder is still unavailable because the build hasn’t completed yet. Could you please check this and do the needful? |
|
/rebase |
c8d5b8c to
7c6f58a
Compare
|
/azp run MAUI-UITests-public |
1 similar comment
|
/azp run MAUI-UITests-public |
|
Azure Pipelines successfully started running 1 pipeline(s). |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts that emerged when I retargeted to inflight/current?
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts that emerged when I retargeted to inflight/current?
@kubaflo , I have rebased the PR and resolved the conflicts. |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
@kubaflo, I have resolved the build errors |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…net#34527) <!-- Please let the below note in for people that 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 if this change resolves your issue. Thank you! ### Issue Details: Horizontalspacing / Verticalspacing is not not applied to the first column in GridItemLayout using CollectionView on Android platform. ### Root Cause: The grid spacing was not being distributed symmetrically across the active layout implementations, so edge items did not fully participate when spacing changed at runtime. ### Description of Change: - On Android, the fix in MauiRecyclerView.cs changes how RecyclerView padding is handled for GridItemsLayout. Android was already using SpacingItemDecoration, which applies half-spacing on all four sides of each item. Previously, negative RecyclerView padding canceled that spacing at the control edges. The branch keeps that negative-padding behavior for non-grid layouts, but disables it for GridItemsLayout, allowing the grid’s half-spacing to remain visible at the outer perimeter. This makes the first row and first column visually respond when spacing changes, but it also changes the grid behavior from spacing only between items to spacing around the outside edges as well. **Tested the behavior in the following platforms:** - [x] Android - [x] Windows - [ ] iOS - [ ] Mac ### Reference: N/A ### Issues Fixed: Fixes dotnet#34257 ### Screenshots | Before | After | |---------|--------| | <Video src="https://github.com/user-attachments/assets/578dda69-1d60-474c-a6d8-23b3f9d29a50" Width="300" Height="600"> | <Video src="https://github.com/user-attachments/assets/7f3826e6-5922-4b6f-a6b9-de581b7db6c3" Width="300" Height="600"> |
…ing ToPlatform and subsequent property changes - Updated Label and Entry Feature Matrix test samples and scripts - Re-saved snapshot images for iOS, Android, Mac, and Windows - Adjusted default property values in test samples - Performance improvements for Label mapper on iOS Co-authored-by: Tamilarasan-Paranthaman <93904422+Tamilarasan-Paranthaman@users.noreply.github.com> Co-authored-by: albyrock87 <albyrock87@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
@kubaflo, I have resolved the conflicts. |
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
🤖 AI Summary
📊 Review Session —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ ButtonFeatureTests ButtonFeatureTests |
✅ FAIL — 1098s | ❌ FAIL — 1112s |
🖥️ EntryFeatureTests EntryFeatureTests |
||
🖥️ LabelFeatureTests LabelFeatureTests |
||
🖥️ Material3LabelFeatureTests Material3LabelFeatureTests |
🔴 Without fix — 🖥️ ButtonFeatureTests: FAIL ✅ · 1098s
(truncated to last 15,000 chars)
n_SetContentAndTextColor_VerifyVisualState [8 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetContentAndTextColor_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetContentAndTextColor_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetContentAndTextColor_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 173
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:15:10 Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Start
>>>>> 04/30/2026 23:15:18 Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Stop
>>>>> 04/30/2026 23:15:18 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState [8 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 79
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:15:18 Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:15:27 Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Stop
>>>>> 04/30/2026 23:15:27 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState [8 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 268
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:15:27 Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Start
>>>>> 04/30/2026 23:15:36 Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Stop
>>>>> 04/30/2026 23:15:36 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState [9 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 97
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:15:36 Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:15:45 Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Stop
>>>>> 04/30/2026 23:15:45 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState [9 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 286
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:15:47 FixtureSetup for RadioButtonFeatureTests(Android)
>>>>> 04/30/2026 23:15:57 RadioButton_Checking_Default_Configuration_VerifyVisualState Start
>>>>> 04/30/2026 23:15:59 RadioButton_Checking_Default_Configuration_VerifyVisualState Stop
Passed RadioButton_Checking_Default_Configuration_VerifyVisualState [2 s]
>>>>> 04/30/2026 23:15:59 RadioButton_Checking_Initial_Configuration_VerifyVisualState Start
>>>>> 04/30/2026 23:16:02 RadioButton_Checking_Initial_Configuration_VerifyVisualState Stop
Passed RadioButton_Checking_Initial_Configuration_VerifyVisualState [2 s]
>>>>> 04/30/2026 23:16:02 RadioButton_FlowDirectionAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:16:09 RadioButton_FlowDirectionAndContent_VerifyVisualState Stop
Passed RadioButton_FlowDirectionAndContent_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:16:09 RadioButton_IsEnabledAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:16:16 RadioButton_IsEnabledAndContent_VerifyVisualState Stop
Passed RadioButton_IsEnabledAndContent_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:16:16 RadioButton_IsVisibleAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:16:22 RadioButton_IsVisibleAndContent_VerifyVisualState Stop
Passed RadioButton_IsVisibleAndContent_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:16:22 RadioButton_SetContentAndCharacterSpacing_VerifyVisualState Start
>>>>> 04/30/2026 23:16:30 RadioButton_SetContentAndCharacterSpacing_VerifyVisualState Stop
Passed RadioButton_SetContentAndCharacterSpacing_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:16:30 RadioButton_SetContentAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:16:37 RadioButton_SetContentAndFontAttributes_VerifyVisualState Stop
Passed RadioButton_SetContentAndFontAttributes_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:16:37 RadioButton_SetContentAndFontSize_VerifyVisualState Start
>>>>> 04/30/2026 23:16:44 RadioButton_SetContentAndFontSize_VerifyVisualState Stop
Passed RadioButton_SetContentAndFontSize_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:16:44 RadioButton_SetContentAndTextColor_VerifyVisualState Start
>>>>> 04/30/2026 23:16:50 RadioButton_SetContentAndTextColor_VerifyVisualState Stop
Passed RadioButton_SetContentAndTextColor_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:16:51 RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Start
>>>>> 04/30/2026 23:16:56 RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Stop
Passed RadioButton_SetFontAttributesAndTextColor_VerifyVisualState [5 s]
>>>>> 04/30/2026 23:16:56 RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:17:02 RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Stop
Passed RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:17:02 RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Start
>>>>> 04/30/2026 23:17:09 RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Stop
Passed RadioButton_SetFontFamilyAndFontSize_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:17:09 RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:17:16 RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Stop
Passed RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:17:16 RadioButton_SetGroupAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:17:23 RadioButton_SetGroupAndContent_VerifyVisualState Stop
Passed RadioButton_SetGroupAndContent_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:17:23 RadioButton_SetSelectedValueAndContent Start
>>>>> 04/30/2026 23:17:28 RadioButton_SetSelectedValueAndContent Stop
Passed RadioButton_SetSelectedValueAndContent [4 s]
>>>>> 04/30/2026 23:17:30 FixtureSetup for VisualStateManager_ButtonFeatureTests(Android)
>>>>> 04/30/2026 23:17:39 VerifyVSM_Button_InitialState Start
>>>>> 04/30/2026 23:17:43 VerifyVSM_Button_InitialState Stop
Passed VerifyVSM_Button_InitialState [3 s]
>>>>> 04/30/2026 23:17:43 VerifyVSM_Button_Disable Start
>>>>> 04/30/2026 23:17:45 VerifyVSM_Button_Disable Stop
Passed VerifyVSM_Button_Disable [1 s]
>>>>> 04/30/2026 23:17:45 VerifyVSM_Button_Reset Start
>>>>> 04/30/2026 23:17:47 VerifyVSM_Button_Reset Stop
Passed VerifyVSM_Button_Reset [1 s]
>>>>> 04/30/2026 23:17:47 VerifyVSM_Button_ResetWhileDisabled Start
>>>>> 04/30/2026 23:17:49 VerifyVSM_Button_ResetWhileDisabled Stop
Passed VerifyVSM_Button_ResetWhileDisabled [2 s]
>>>>> 04/30/2026 23:17:50 VerifyVSM_Button_DisableAndEnable Start
>>>>> 04/30/2026 23:17:52 VerifyVSM_Button_DisableAndEnable Stop
Passed VerifyVSM_Button_DisableAndEnable [2 s]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Failed.
Total tests: 80
Passed: 69
Failed: 11
Total time: 9.2390 Minutes
🟢 With fix — 🖥️ ButtonFeatureTests: FAIL ❌ · 1112s
(truncated to last 15,000 chars)
n_SetContentAndTextColor_VerifyVisualState [8 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetContentAndTextColor_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetContentAndTextColor_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetContentAndTextColor_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 173
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:33:45 Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Start
>>>>> 04/30/2026 23:33:52 Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Stop
>>>>> 04/30/2026 23:33:52 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState [7 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontAttributesAndTextColor_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 79
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:33:53 Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:34:00 Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Stop
>>>>> 04/30/2026 23:34:01 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState [8 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 268
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:34:01 Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Start
>>>>> 04/30/2026 23:34:10 Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Stop
>>>>> 04/30/2026 23:34:10 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState [9 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontFamilyAndFontSize_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 97
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:34:11 Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:34:19 Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Stop
>>>>> 04/30/2026 23:34:19 Log types: logcat, bugreport, server
Failed Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState [8 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Baseline snapshot not yet created: /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/snapshots/android/Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png
Ensure new snapshot is correct: /home/vsts/work/1/a/Controls.TestCases.Shared.Tests/snapshots-diff/android/Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState.png
and if it is, push a change to add it to the 'snapshots' directory.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 296
at Microsoft.Maui.TestCases.Tests.Material3RadioButtonFeatureTests.Material3RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/FeatureMatrix/Material3RadioButtonFeatureTests.cs:line 286
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
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)
>>>>> 04/30/2026 23:34:21 FixtureSetup for RadioButtonFeatureTests(Android)
>>>>> 04/30/2026 23:34:31 RadioButton_Checking_Default_Configuration_VerifyVisualState Start
>>>>> 04/30/2026 23:34:33 RadioButton_Checking_Default_Configuration_VerifyVisualState Stop
Passed RadioButton_Checking_Default_Configuration_VerifyVisualState [2 s]
>>>>> 04/30/2026 23:34:33 RadioButton_Checking_Initial_Configuration_VerifyVisualState Start
>>>>> 04/30/2026 23:34:36 RadioButton_Checking_Initial_Configuration_VerifyVisualState Stop
Passed RadioButton_Checking_Initial_Configuration_VerifyVisualState [3 s]
>>>>> 04/30/2026 23:34:36 RadioButton_FlowDirectionAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:34:44 RadioButton_FlowDirectionAndContent_VerifyVisualState Stop
Passed RadioButton_FlowDirectionAndContent_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:34:44 RadioButton_IsEnabledAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:34:51 RadioButton_IsEnabledAndContent_VerifyVisualState Stop
Passed RadioButton_IsEnabledAndContent_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:34:51 RadioButton_IsVisibleAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:34:57 RadioButton_IsVisibleAndContent_VerifyVisualState Stop
Passed RadioButton_IsVisibleAndContent_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:34:57 RadioButton_SetContentAndCharacterSpacing_VerifyVisualState Start
>>>>> 04/30/2026 23:35:05 RadioButton_SetContentAndCharacterSpacing_VerifyVisualState Stop
Passed RadioButton_SetContentAndCharacterSpacing_VerifyVisualState [8 s]
>>>>> 04/30/2026 23:35:05 RadioButton_SetContentAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:35:12 RadioButton_SetContentAndFontAttributes_VerifyVisualState Stop
Passed RadioButton_SetContentAndFontAttributes_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:35:12 RadioButton_SetContentAndFontSize_VerifyVisualState Start
>>>>> 04/30/2026 23:35:19 RadioButton_SetContentAndFontSize_VerifyVisualState Stop
Passed RadioButton_SetContentAndFontSize_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:35:19 RadioButton_SetContentAndTextColor_VerifyVisualState Start
>>>>> 04/30/2026 23:35:26 RadioButton_SetContentAndTextColor_VerifyVisualState Stop
Passed RadioButton_SetContentAndTextColor_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:35:26 RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Start
>>>>> 04/30/2026 23:35:32 RadioButton_SetFontAttributesAndTextColor_VerifyVisualState Stop
Passed RadioButton_SetFontAttributesAndTextColor_VerifyVisualState [5 s]
>>>>> 04/30/2026 23:35:32 RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:35:37 RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState Stop
Passed RadioButton_SetFontFamilyAndFontAttributes_VerifyVisualState [5 s]
>>>>> 04/30/2026 23:35:38 RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Start
>>>>> 04/30/2026 23:35:45 RadioButton_SetFontFamilyAndFontSize_VerifyVisualState Stop
Passed RadioButton_SetFontFamilyAndFontSize_VerifyVisualState [7 s]
>>>>> 04/30/2026 23:35:45 RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Start
>>>>> 04/30/2026 23:35:51 RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState Stop
Passed RadioButton_SetFontSizeAndFontAttributes_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:35:52 RadioButton_SetGroupAndContent_VerifyVisualState Start
>>>>> 04/30/2026 23:35:58 RadioButton_SetGroupAndContent_VerifyVisualState Stop
Passed RadioButton_SetGroupAndContent_VerifyVisualState [6 s]
>>>>> 04/30/2026 23:35:58 RadioButton_SetSelectedValueAndContent Start
>>>>> 04/30/2026 23:36:03 RadioButton_SetSelectedValueAndContent Stop
Passed RadioButton_SetSelectedValueAndContent [4 s]
>>>>> 04/30/2026 23:36:05 FixtureSetup for VisualStateManager_ButtonFeatureTests(Android)
>>>>> 04/30/2026 23:36:15 VerifyVSM_Button_InitialState Start
>>>>> 04/30/2026 23:36:19 VerifyVSM_Button_InitialState Stop
Passed VerifyVSM_Button_InitialState [3 s]
>>>>> 04/30/2026 23:36:19 VerifyVSM_Button_Disable Start
>>>>> 04/30/2026 23:36:21 VerifyVSM_Button_Disable Stop
Passed VerifyVSM_Button_Disable [2 s]
>>>>> 04/30/2026 23:36:21 VerifyVSM_Button_Reset Start
>>>>> 04/30/2026 23:36:24 VerifyVSM_Button_Reset Stop
Passed VerifyVSM_Button_Reset [2 s]
>>>>> 04/30/2026 23:36:24 VerifyVSM_Button_ResetWhileDisabled Start
>>>>> 04/30/2026 23:36:26 VerifyVSM_Button_ResetWhileDisabled Stop
Passed VerifyVSM_Button_ResetWhileDisabled [2 s]
>>>>> 04/30/2026 23:36:26 VerifyVSM_Button_DisableAndEnable Start
>>>>> 04/30/2026 23:36:28 VerifyVSM_Button_DisableAndEnable Stop
Passed VerifyVSM_Button_DisableAndEnable [2 s]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Failed.
Total tests: 80
Passed: 69
Failed: 11
Total time: 9.2272 Minutes
🔴 Without fix — 🖥️ EntryFeatureTests: ⚠️ ENV ERROR · 0s
Log file empty
🟢 With fix — 🖥️ EntryFeatureTests: ⚠️ ENV ERROR · 0s
Log file empty
🔴 Without fix — 🖥️ LabelFeatureTests: ⚠️ ENV ERROR · 0s
Log file empty
🟢 With fix — 🖥️ LabelFeatureTests: ⚠️ ENV ERROR · 0s
Log file empty
🔴 Without fix — 🖥️ Material3LabelFeatureTests: ⚠️ ENV ERROR · 0s
Log file empty
🟢 With fix — 🖥️ Material3LabelFeatureTests: ⚠️ ENV ERROR · 0s
Log file empty
⚠️ Failure Details (7 tests)
⚠️ EntryFeatureTests without fix:Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"⚠️ LabelFeatureTests without fix:Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"⚠️ Material3LabelFeatureTests without fix:Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"- ❌ ButtonFeatureTests FAILED with fix (should pass)
Device tests: 11 of 80 failed
⚠️ EntryFeatureTests with fix:Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"⚠️ LabelFeatureTests with fix:Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"⚠️ Material3LabelFeatureTests with fix:Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"
📁 Fix files reverted (113 files)
eng/pipelines/ci-copilot.ymlsrc/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xamlsrc/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapPinsGallery.xaml.cssrc/Controls/src/Core/BindableObject.cssrc/Controls/src/Core/BindableProperty.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFragmentContainer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellToolbarTracker.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellFlyoutLayoutManager.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellPageRendererTracker.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellSectionRootRenderer.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/ShellTableViewController.cssrc/Controls/src/Core/Entry/Entry.Mapper.cssrc/Controls/src/Core/Entry/Entry.iOS.cssrc/Controls/src/Core/Handlers/Items/Android/GridLayoutSpanSizeLookup.cssrc/Controls/src/Core/Handlers/Items/Android/MauiCarouselRecyclerView.cssrc/Controls/src/Core/Handlers/Items/CarouselViewHandler.Windows.cssrc/Controls/src/Core/Handlers/Items/iOS/ItemsViewController.cssrc/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cssrc/Controls/src/Core/Handlers/Items2/iOS/CarouselViewController2.cssrc/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cssrc/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cssrc/Controls/src/Core/Handlers/Items2/iOS/StructuredItemsViewController2.cssrc/Controls/src/Core/Handlers/Shell/Windows/ShellView.cssrc/Controls/src/Core/Label/Label.Mapper.cssrc/Controls/src/Core/Label/Label.iOS.cssrc/Controls/src/Core/NavigationPage/NavigationPage.cssrc/Controls/src/Core/Platform/AlertManager/AlertManager.Android.cssrc/Controls/src/Core/Platform/Android/BottomNavigationViewUtils.cssrc/Controls/src/Core/Platform/Android/TabbedPageManager.cssrc/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.iOS.cssrc/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cssrc/Controls/src/Core/Platform/iOS/Extensions/LabelExtensions.cssrc/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txtsrc/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txtsrc/Controls/src/Core/RadioButton/RadioButton.cssrc/Controls/src/Core/RadioButton/RadioButtonGroup.cssrc/Controls/src/Core/RadioButton/RadioButtonGroupController.cssrc/Controls/src/Core/Shadow.cssrc/Controls/src/Core/Shapes/Shape.cssrc/Controls/src/Core/Shell/Shell.cssrc/Controls/src/Core/Shell/ShellNavigationManager.cssrc/Controls/src/Core/TabbedPage/TabbedPage.Windows.cssrc/Controls/src/Core/VisualElement/VisualElement.cssrc/Controls/src/Core/VisualStateManager.cssrc/Controls/src/Xaml/ApplyPropertiesVisitor.cssrc/Controls/src/Xaml/MarkupExtensions/OnIdiomExtension.cssrc/Controls/src/Xaml/MarkupExtensions/StaticResourceExtension.cssrc/Core/maps/src/Handlers/Map/MapHandler.Android.cssrc/Core/maps/src/Handlers/MapPin/MapPinHandler.Android.cssrc/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Core/src/Handlers/Button/ButtonHandler.Android.cssrc/Core/src/Handlers/Button/ButtonHandler.cssrc/Core/src/Handlers/Button/ButtonHandler.iOS.cssrc/Core/src/Handlers/DatePicker/DatePickerHandler.MacCatalyst.cssrc/Core/src/Handlers/Editor/EditorHandler.iOS.cssrc/Core/src/Handlers/Entry/EntryHandler.cssrc/Core/src/Handlers/Entry/EntryHandler.iOS.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Standard.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.Tizen.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHandler.cssrc/Core/src/Handlers/Label/LabelHandler.cssrc/Core/src/Handlers/Label/LabelHandler.iOS.cssrc/Core/src/Handlers/RadioButton/RadioButtonHandler.cssrc/Core/src/Handlers/RadioButton/RadioButtonHandler.iOS.cssrc/Core/src/Handlers/SearchBar/SearchBarHandler.iOS.cssrc/Core/src/Handlers/ShapeView/ShapeViewHandler.Standard.cssrc/Core/src/Handlers/ShapeView/ShapeViewHandler.cssrc/Core/src/Handlers/Switch/SwitchHandler.iOS.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.Android.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.Windows.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.cssrc/Core/src/Handlers/TimePicker/TimePickerHandler.iOS.cssrc/Core/src/Hosting/EssentialsMauiAppBuilderExtensions.cssrc/Core/src/Platform/Android/ContainerView.cssrc/Core/src/Platform/Android/MauiSwipeView.cssrc/Core/src/Platform/Android/MauiWebView.cssrc/Core/src/Platform/Android/TimePickerExtensions.cssrc/Core/src/Platform/Windows/ContentPanel.cssrc/Core/src/Platform/Windows/MauiPasswordTextBox.cssrc/Core/src/Platform/Windows/MauiToolbar.xaml.cssrc/Core/src/Platform/Windows/RootNavigationView.cssrc/Core/src/Platform/Windows/ScrollViewerExtensions.cssrc/Core/src/Platform/Windows/TimePickerExtensions.cssrc/Core/src/Platform/iOS/ButtonExtensions.cssrc/Core/src/Platform/iOS/LayerExtensions.cssrc/Core/src/Platform/iOS/MauiSwipeView.cssrc/Core/src/Platform/iOS/MauiView.cssrc/Core/src/Platform/iOS/TimePickerExtensions.cssrc/Core/src/Platform/iOS/WrapperView.cssrc/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txtsrc/Core/src/PublicAPI/net-ios/PublicAPI.Unshipped.txtsrc/Core/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txtsrc/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txtsrc/Core/src/ViewExtensions.cssrc/Core/src/WindowExtensions.cssrc/Essentials/src/AssemblyInfo/AssemblyInfo.shared.cssrc/Essentials/src/FileSystem/FileSystemUtils.android.cssrc/Essentials/src/FileSystem/FileSystemUtils.shared.cssrc/Essentials/src/MainThread/MainThread.netstandard.cssrc/Essentials/src/MainThread/MainThread.shared.cssrc/Essentials/src/MediaPicker/MediaPicker.ios.cssrc/Graphics/src/Graphics/Platforms/Android/PlatformGraphicsView.cssrc/Graphics/src/Graphics/Platforms/MaciOS/PlatformCanvas.cssrc/Graphics/src/Graphics/Platforms/Windows/PlatformGraphicsView.cssrc/Graphics/src/Graphics/Platforms/iOS/PlatformGraphicsView.cs
New files (not reverted):
src/Controls/src/Core/Handlers/Items/iOS/IScrollTrackingDelegator.cssrc/Core/src/Handlers/HybridWebView/HybridWebViewHelper.cssrc/Core/src/ScreenshotDispatch.cs
🧪 UI Tests — Category Detection
Detected UI test categories: Button,Entry,Label,Material3,ViewBaseTests
🔍 Pre-Flight — Context & Validation
Pre-Flight Summary: PR #31159
Title: Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes
Author: Tamilarasan-Paranthaman (partner/syncfusion)
Base branch: inflight/current
Target milestone: .NET 10.0 SR8
Labels: p/0, community ✨, partner/syncfusion, s/agent-gate-failed
Gate result: ❌ FAILED — Android
1. Context: Problem Being Solved
When a Label, Entry, or Button handler connects to its virtual view for the first time, the framework maps all properties in sequence (the "connecting" phase). Several mapper methods — particularly those that apply NSAttributedString formatting (font, character spacing, text color, line height) — call subsidiary mappers like MapFormatting() that re-apply other already-queued properties. This causes redundant, order-dependent work during initialization and can result in:
- Performance overhead from repeated attributed-string construction during initial mapping.
- Incorrect visual state if a subsequent mapping step overwrites an earlier one (e.g.,
Textmapping triggersMapFormattingbeforeCharacterSpacing/Font/TextColorhave been mapped, then those properties are mapped again, but the initial attributed string built duringMapTextmay not have had the correct style values).
The fix strategy is: skip "lazy" formatting work when IsConnectingHandler() is true (the handler is in its initial connection phase), because all remaining property mappers will fire anyway and will correctly set those attributes.
IsConnectingHandler() is defined in InternalElementExtensions.cs as:
internal static bool IsConnectingHandler(this IElement element) =>
(element.Handler as IElementHandlerStateExhibitor)?.State.HasFlag(ElementHandlerState.Connecting) ?? false;It returns true only during the initial ConnectHandler → property-mapping phase, not during subsequent individual property updates.
2. Key Changes
Label.Mapper.cs
MapFormattedText: skips work whenIsConnectingHandler()— avoids redundant attributed-string build during init.MapFont: addsIsConnectingHandler()guard alongside existingHasFormattedTextSpanscheck.MapTextColor: no guard added (see Risks §3.2).
Label.iOS.cs
MapText: skipsMapFormatting()call whenIsConnectingHandler()— same rationale as Entry.
FormattedStringExtensions.cs (iOS)
- Added
defaultLineBreakModeparameter to two public overloads, previously hard-coded toWordWrap. - Added macOS conditional for
NSLineBreakModevsUILineBreakModeenum (compiler compatibility).
Entry.Mapper.cs
TextTransformmapping replaced by newMapTextTransformwhich skips to no-op whenIsConnectingHandler(), then delegates toMapText. Ensures transform is applied on update but not redundantly during init.
Entry.iOS.cs / EntryHandler.iOS.cs
MapText(both the Controls-layer override and the Core handler) skip theMapFormatting()call duringIsConnectingHandler().MapFormattingtriggersMaxLength,CharacterSpacing, andHorizontalTextAlignmentupdates — skipping it during init avoids triple-mapping those properties at startup.
EntryHandler.cs (core)
- New
TextMapper(private) establishes explicit ordering:ClearButtonVisibility → HorizontalTextAlignment → VerticalTextAlignment → **Text** → MaxLength → Font → CharacterSpacing → TextColor. - Main
Mapperis composed fromTextMapper+ViewHandler.ViewMapper, withText/TextColorremoved from the main mapper to prevent duplication. - Comment documents intent: "Ensure Text is mapped before LineHeight/Decorations/CharacterSpacing/…/Font".
ButtonHandler.cs / ButtonHandler.iOS.cs
Textmoved to first inTextButtonMapper.MapTextskipsMapFormatting()whenIsConnectingHandler().
Tests
- Feature Matrix host pages (Label, Entry, Button) gain a "tap MainLabel to reload" gesture to verify handler re-initialization produces correct visual output.
LabelOptionsPage: new "Single Line Formatted Text" option.LabelFeatureTests,EntryFeatureTests,ButtonFeatureTests: new test variants that tapMainLabelafter configuration and take a second screenshot to verify post-reconnect rendering.
3. Potential Risks
3.1 Duplicate MaxLength mapping in EntryHandler
EntryPriorityMapper already contains MaxLength. The new TextMapper also contains MaxLength. During initial connection both mappers are applied, meaning MaxLength is mapped twice. While this is currently idempotent, it adds unnecessary work and is a maintenance hazard — future changes to MaxLength mapping behaviour could cause subtle bugs.
3.2 Asymmetric IsConnectingHandler() guards in Label.Mapper.cs
MapFont has the guard; MapTextColor does not. If MapTextColor internally triggers attributed-string reconstruction (as it does for NSAttributedString-based text), the redundancy the PR aims to eliminate will still occur for the color path. The asymmetry may be intentional (TextColor doesn't rebuild attributed string on Android) but should be confirmed and documented.
3.3 EntryHandler2 / Material3 asymmetry
EntryHandler2.Mapper still maps TextTransform directly to MapText rather than the new MapTextTransform. This means the IsConnectingHandler() skip for TextTransform updates does not apply to the Material3 handler — different behaviour between default and Material handlers.
3.4 IsConnectingHandler() skips and property ordering dependency
The guard approach relies on the mapper ordering being complete and correct. If any refactoring changes the order so that formatting-dependent properties are mapped before the "format once at the end" step, the skip could leave a control in an incorrect visual state. The new explicit ordering in TextMapper mitigates this for Entry, but Label and Button rely on implicit framework ordering.
3.5 Missing newline at end of file
Several changed files end without a trailing newline. This is a minor hygiene issue but causes noisy diffs in future PRs and violates common .editorconfig rules.
3.6 FormattedStringExtensions public API change
Adding a new parameter (defaultLineBreakMode) to existing public overloads of ToNSAttributedString is a binary-compatible change (new overload, not parameter injection into existing signature — confirm this is an overload, not a default-parameter change on a previously-existing method). If it's a default parameter on an existing public method, it could be a source-breaking change for callers who reference the method by delegate.
4. Android Gate Failure
The gate failed on Android with the test VerifyTextWhenAlingnedVertically in EntryFeatureTests.
What the test does (lines 228–240)
- Opens Options panel, selects
VEnd(VerticalTextAlignment.End), taps Apply. - Waits for the Entry (
TestEntry), clears focus on Windows. - Takes a screenshot.
- Taps
MainLabel— the new "tap to reload" gesture added by this PR — which reconnects the handler. - Takes a second screenshot expecting it to match the first.
Why it likely fails on Android
The new tap gesture on MainLabel is intended to trigger handler re-initialization so the test verifies post-reconnect rendering. On Android, after the handler reconnects, the vertical text alignment (VEnd) may not be correctly re-applied because:
VerticalTextAlignmentis not in theTextMapperpriority group (it is inTextMapperfor Entry on iOS/Core, but Android has its own handler path).- Or the baseline screenshot used for comparison was captured before the "tap to reload" feature existed, so the new second screenshot step now has no matching baseline image → the test fails because the reference image is missing or shows different state.
Either way, this is a real test failure introduced by this PR, not a pre-existing flake. The gate correctly flagged it.
5. Questions for Consideration
-
Is the
MaxLengthduplication inEntryPriorityMapper+TextMapperintentional? If so, a comment explaining why it needs to appear in both places would help future maintainers. -
Why does
MapTextColorinLabel.Mapper.csnot get anIsConnectingHandler()guard? Is this because Android'sUpdateTextColorfor plain text doesn't touch the attributed string? If so, this should be documented with a comment. -
Has
EntryHandler2(Material3) been intentionally excluded from theMapTextTransformoptimization? The asymmetry could lead to different performance profiles or subtle render differences between Material and default Entry. -
Are baseline screenshots for the new "tap to reload" test variants checked in? The Android gate failure strongly suggests either they are missing or they were generated on a different platform state, causing an image-diff failure rather than a logic failure.
-
Is the
defaultLineBreakModeparameter addition toFormattedStringExtensionsa public API surface change? If it is aninternalmethod or the parameter has a default value on a new overload (not modifying the existing method signature), this is fine — but it should be confirmed againstPublicAPI.Unshipped.txt. -
Does the
IsConnectingHandler()guard interact correctly with hot-reload / live visual tree updates? Hot-reload may not go through theConnectingstate, so the guard should be transparent in that path — but this should be verified.
🔧 Fix — Analysis & Comparison
Try-Fix Aggregate — PR #31159
Overview
Four independent fix candidates were generated. Each explored a different domain of the PR's changes. All candidates agree the PR's direction is correct but the execution has issues that caused the Android gate failure.
Try-Fix-1: Handler Lifecycle & Property Mapper Ordering Fix
Domain: Android SetInputType ordering vs. gravity alignment
Root Cause Identified: HorizontalTextAlignment and VerticalTextAlignment were placed in the new TextMapper which is processed BEFORE IsPassword, IsReadOnly, and Keyboard in the mapper chain. On Android, all three call SetInputType() which can reset the EditText Gravity flags in some AppCompat configurations. With the original code, alignment was mapped after InputType operations — the PR broke this invariant.
Proposed Fix:
- Remove
HorizontalTextAlignmentandVerticalTextAlignmentfromTextMapper - Add them to
Mapper's own dict afterKeyboard(the last InputType-setting operation) - Update comment to accurately describe only the iOS AttributedText dependencies
- Cleans up
TextMapperto contain only properties that genuinely need Text-first ordering on iOS
Strength: Addresses the real architectural issue; conservative about ordering (safer long-term)
Weakness: Still requires new Android baseline snapshots to be captured
Try-Fix-2: iOS Label Formatting Correctness Verification
Domain: iOS IsConnectingHandler() guard safety for Label rendering
Finding: NO REGRESSION. The IsConnectingHandler() guards in MapText and MapFormattedText are safe.
For FormattedText labels: LabelExtensions.UpdateText calls label.ToNSAttributedString() which bakes all label properties (Font, TextColor, LineHeight, CharacterSpacing, LineBreakMode, HorizontalTextAlignment) into the NSAttributedString in one shot. Since the virtual view's properties are already at final values before the handler connects, the initial render is complete and correct after just MapText.
For plain text labels: Setting UILabel.Text produces a synthesized AttributedText on iOS. Subsequent individual property mappers (Font, LineHeight, TextDecorations, CharacterSpacing) all mutate this attributed string correctly.
Key insight: The old code was actually doing double work — MapFormatting was called from within MapText before Font was even applied, then all properties were applied again by the mapper individually. The PR correctly eliminates this redundancy.
Verdict for this candidate: Validation only — no code fix needed for iOS path.
Try-Fix-3: Android Entry Vertical Alignment Root Cause
Domain: Android snapshot test infrastructure
Root Cause Identified: The PR added a second VerifyScreenshot call after App.Tap(MainLabel) in VerifyTextWhenAlingnedVertically without:
- A
App.WaitForElement("TestEntry")guard to ensure page recreation is fully settled before the screenshot - Committed Android baseline images for the new screenshot assertion
The SetInputType() / gravity reset hypothesis (try-fix-1) may not apply since prior test runs showed the old order also had alignment mapped before some SetInputType callers (like IsReadOnly, Keyboard) and still passed.
Proposed Fix (minimal):
App.Tap(MainLabel);
App.WaitForElement("TestEntry"); // ADD: wait for page recreation
VerifyScreenshot(cropBottom: CropBottomValue);Plus capture and commit new Android baseline.
Strength: Minimal, targeted fix; doesn't alter mapper architecture
Weakness: Doesn't address the mapper ordering concern for long-term correctness
Try-Fix-4: Material3 & Cross-Control Consistency Fix
Domain: Android Material3 EntryHandler2 missing IsConnectingHandler guard
Root Cause Identified: EntryHandler2.Mapper.ReplaceMapping<Entry, EntryHandler2>(nameof(TextTransform), MapText) — Material3 Entry still uses MapText directly for TextTransform, not the new MapTextTransform with IsConnectingHandler() guard. This defeats the PR's performance optimization for all Android Material3 Entry users.
Proposed Fix:
Add MapTextTransform(EntryHandler2 handler, Entry entry) overload in Entry.Android.cs and update the mapping in Entry.Mapper.cs to use it.
Other findings:
handler.IsConnectingHandler()andelement.IsConnectingHandler()are equivalent (same state flag)- Button iOS guard is correct —
MapFormattingis not needed during connect since CharacterSpacing/Font/TextColor are applied individually by the mapper - The gate failure is primarily a snapshot issue, not a code regression
Strength: Clean, targeted fix for the Material3 omission
Weakness: Doesn't address the mapper ordering issue for non-Material3 Entry
Consensus Findings Across All Candidates
| Issue | try-fix-1 | try-fix-2 | try-fix-3 | try-fix-4 |
|---|---|---|---|---|
| Gate failure root cause | Mapper ordering (gravity) | N/A | Missing baselines + timing | Missing baselines |
| iOS Label formatting regression | Not a bug | Not a bug | Not a bug | Not a bug |
| Material3 TextTransform omission | Real bug | Real bug | Real bug | Fixed ✅ |
| MapTextColor missing guard | Cosmetic issue | Cosmetic issue | N/A | N/A |
| EntryPriorityMapper dead code | Real issue | N/A | N/A | N/A |
| Alignment in TextMapper | Architectural issue, Fixed ✅ | N/A | Timing fix only | N/A |
📋 Report — Final Recommendation
PR #31159 — Multi-Candidate Comparative Analysis
PR: "Improve label mapping performance and ensure complete coverage including ToPlatform and subsequent property changes"
Gate: ❌ FAILED (Android)
Platform under test: Android
Candidates Evaluated
| Candidate | Description |
|---|---|
pr |
Raw PR as submitted |
pr-plus-reviewer |
PR + expert reviewer feedback applied (MapTextColor guard, dead code cleanup, Material3 fix, EOF newlines) |
try-fix-1 |
Moves HAlign/VAlign out of TextMapper to fix Android gravity ordering; most architectural cleanup |
try-fix-2 |
Validation only — confirms iOS Label path is correct; no code fix |
try-fix-3 |
Minimal: adds WaitForElement after App.Tap(MainLabel) in test + baseline notes |
try-fix-4 |
Fixes Material3 EntryHandler2 TextTransform omission |
Comparative Analysis
pr — Raw submission
Strengths:
- Correct core direction:
IsConnectingHandler()guards eliminate redundantMapFormattingcalls during initial connection LineBreakModepropagation inFormattedStringExtensions.csis a genuine bug fixTextMapperseparation with Text-first ordering addresses a real iOSAttributedTextdependency- macOS
NSLineBreakModeconditional is required for correctness - Tests added with "tap-to-reload" mechanism test the initial connection path
Blocking Issues (7 found by expert review):
- 🔴 Android gate failure — new
VerifyScreenshotat line 237 has no committed Android baseline - 🟠
EntryPriorityMapperis now dead code (declared but not in any chain) - 🟠
MaxLengthinitialization order regression —InputFilterinstalled after initial text on Android - 🟠 Material3
EntryHandler2TextTransform not updated to useMapTextTransform - 🟡
MapTextColormissingIsConnectingHandler()guard (asymmetric withMapFont) - 🟡 TextMapper comment contradicts its own property order (
HAlign/VAlignlisted beforeText) - ⚪ Missing EOF newlines in multiple files
Verdict: Not ready. Multiple code correctness and architecture issues.
pr-plus-reviewer — PR + reviewer feedback
Addresses issues #2, #3 (with comment), #4, #5, #7. The blocking issue (#1 — Android baselines) cannot be resolved by code changes alone; it requires capturing and committing new Android snapshots. This candidate improves code quality significantly but doesn't fix the gate.
Verdict: Better than pr, but gate still fails.
try-fix-1 — Alignment ordering fix (most comprehensive)
What it fixes over pr:
- Removes
HorizontalTextAlignmentandVerticalTextAlignmentfromTextMapper→ placed inMapperafterKeyboard TextMappernow exclusively contains properties with genuine iOSAttributedTextordering dependencies- Eliminates the
EntryPriorityMapperdead code - Fixes MaxLength ordering concern
- Adds explanatory comments for the Android ordering invariant
- Combines well with try-fix-4's Material3 fix
Why alignment doesn't belong in TextMapper:
- On iOS,
UpdateHorizontalTextAlignmentsetstextField.TextAlignment(a native property, NOT part ofAttributedText). No Text ordering dependency. - On iOS,
UpdateVerticalTextAlignmentsetstextField.VerticalAlignment. No Text ordering dependency. - On Android, alignment is gravity flags — no dependency on Text or InputType (in theory), but the correct/safe invariant is to apply after InputType to avoid any platform-version-specific gravity reset behavior.
Residual: Still needs Android baselines committed. But architecturally cleaner than the PR.
try-fix-2 — iOS FormattedText validation
Validates that the IsConnectingHandler() guards in the iOS Label path are safe and correct. No code changes needed. The old code double-applied formatting; the new code is strictly better.
Not a winner on its own — doesn't address any of the blocking issues.
try-fix-3 — Minimal test timing fix
Proposes adding App.WaitForElement("TestEntry") after App.Tap(MainLabel) to ensure page recreation is complete before the screenshot. Identifies the gate failure as primarily a snapshot mismatch rather than a code regression.
Weakness: Doesn't address the architectural concerns or Material3 omission. The Appium timing fix is valid but secondary to the snapshot baseline issue.
try-fix-4 — Material3 consistency fix
Correctly adds MapTextTransform(EntryHandler2 handler, Entry entry) overload with IsConnectingHandler() guard and updates the Material3 mapping. Clean, targeted fix for a real omission.
Best as an additive fix on top of try-fix-1, not a standalone winner.
Winner: try-fix-1
Rationale:
-
Most architecturally correct:
TextMappershould only contain properties with a genuine Text-before-styling dependency on iOS (i.e., properties applied toAttributedText). Alignment properties set native view attributes independently — they don't need to be ordered relative to Text. Moving them out ofTextMappermakes the intent clear and avoids any platform-specific ordering risks. -
Fixes the deepest issue: The
EntryPriorityMapperdead code andMaxLengthordering regression are real design problems. try-fix-1 cleanly resolves both. -
Gate failure root cause: Whether the Android failure is strictly a gravity-reset bug or a snapshot timing issue, try-fix-1's fix is defensive and correct — aligning after all InputType operations is the right invariant for long-term correctness across Android API levels.
-
Android baselines: No candidate can provide baseline images (those require device test runs). try-fix-1 is the code fix that will produce the correct rendering that new baselines should capture.
-
Combines with other fixes: try-fix-4's Material3 fix and the expert reviewer's
MapTextColorguard should be applied alongside try-fix-1 for a complete fix.
Key Remaining Action for PR Author
- Run Android UI tests locally after applying try-fix-1 + try-fix-4 fixes
- Capture and commit new Android baseline images for
VerifyTextWhenAlingnedVerticallyand other affected tests - Add
App.WaitForElement("TestEntry")afterApp.Tap(MainLabel)(per try-fix-3) for robustness
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 is the strongest candidate: it correctly moves HorizontalTextAlignment and VerticalTextAlignment out of TextMapper and into Mapper after all InputType-setting operations (IsPassword, IsReadOnly, Keyboard), eliminating the Android gravity-reset risk that caused the gate failure. It also incorporates the Material3 TextTransform guard (try-fix-4) and adds the missing IsConnectingHandler guard on MapTextColor (expert reviewer issue #5). The remaining PR changes are architecturally sound as confirmed by try-fix-2 iOS validation.
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/src/Core/Entry/Entry.Android.cs b/src/Controls/src/Core/Entry/Entry.Android.cs
index 0f90bf2ca3..864401ca05 100644
--- a/src/Controls/src/Core/Entry/Entry.Android.cs
+++ b/src/Controls/src/Core/Entry/Entry.Android.cs
@@ -58,5 +58,17 @@ namespace Microsoft.Maui.Controls
Platform.EditTextExtensions.UpdateText(handler.PlatformView.EditText, entry);
}
+
+ // TODO: Material3: Make it public in .NET 11
+ internal static void MapTextTransform(EntryHandler2 handler, Entry entry)
+ {
+ if (entry.IsConnectingHandler())
+ {
+ // If we're connecting the handler, we don't want to map the text multiple times.
+ return;
+ }
+
+ MapText(handler, entry);
+ }
}
}
diff --git a/src/Controls/src/Core/Entry/Entry.Mapper.cs b/src/Controls/src/Core/Entry/Entry.Mapper.cs
index 1d3d9285c7..ba84cb5e35 100644
--- a/src/Controls/src/Core/Entry/Entry.Mapper.cs
+++ b/src/Controls/src/Core/Entry/Entry.Mapper.cs
@@ -18,7 +18,7 @@ namespace Microsoft.Maui.Controls
EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(PlatformConfiguration.iOSSpecific.Entry.AdjustsFontSizeToFitWidthProperty.PropertyName, MapAdjustsFontSizeToFitWidth);
#endif
EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(nameof(Text), MapText);
- EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(nameof(TextTransform), MapText);
+ EntryHandler.Mapper.ReplaceMapping<Entry, IEntryHandler>(nameof(TextTransform), MapTextTransform);
// Material3 Entry Handler mappings
#if ANDROID
@@ -26,7 +26,7 @@ namespace Microsoft.Maui.Controls
{
EntryHandler2.Mapper.ReplaceMapping<Entry, EntryHandler2>(PlatformConfiguration.AndroidSpecific.Entry.ImeOptionsProperty.PropertyName, MapImeOptions);
EntryHandler2.Mapper.ReplaceMapping<Entry, EntryHandler2>(nameof(Text), MapText);
- EntryHandler2.Mapper.ReplaceMapping<Entry, EntryHandler2>(nameof(TextTransform), MapText);
+ EntryHandler2.Mapper.ReplaceMapping<Entry, EntryHandler2>(nameof(TextTransform), MapTextTransform);
EntryHandler2.Mapper.AppendToMapping(nameof(VisualElement.IsFocused), InputView.MapIsFocused);
EntryHandler2.Mapper.AppendToMapping(nameof(VisualElement.IsVisible), InputView.MapIsVisible);
EntryHandler2.CommandMapper.PrependToMapping(nameof(IEntry.Focus), InputView.MapFocus);
@@ -42,5 +42,16 @@ namespace Microsoft.Maui.Controls
EntryHandler.CommandMapper.PrependToMapping(nameof(IEntry.Focus), InputView.MapFocus);
#endif
}
+
+ static void MapTextTransform(IEntryHandler handler, Entry entry)
+ {
+ if (entry.IsConnectingHandler())
+ {
+ // If we're connecting the handler, we don't want to map the text multiple times.
+ return;
+ }
+
+ MapText(handler, entry);
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/Controls/src/Core/Entry/Entry.iOS.cs b/src/Controls/src/Core/Entry/Entry.iOS.cs
index 25a6281483..e03bfd1974 100644
--- a/src/Controls/src/Core/Entry/Entry.iOS.cs
+++ b/src/Controls/src/Core/Entry/Entry.iOS.cs
@@ -16,7 +16,14 @@ namespace Microsoft.Maui.Controls
public static void MapText(IEntryHandler handler, Entry entry)
{
Platform.TextExtensions.UpdateText(handler.PlatformView, entry);
- EntryHandler.MapFormatting(handler, entry);
+
+ if (!handler.IsConnectingHandler())
+ {
+ // If we're not connecting the handler, we need to update the text formatting
+ // This is because the text may have changed, and we need to ensure that
+ // any attributed string formatting is applied correctly.
+ EntryHandler.MapFormatting(handler, entry);
+ }
}
public static void MapCursorColor(EntryHandler handler, Entry entry) =>
@@ -28,4 +35,4 @@ namespace Microsoft.Maui.Controls
public static void MapText(EntryHandler handler, Entry entry) =>
MapText((IEntryHandler)handler, entry);
}
-}
+}
\ No newline at end of file
diff --git a/src/Controls/src/Core/Label/Label.Mapper.cs b/src/Controls/src/Core/Label/Label.Mapper.cs
index 19524d4f16..7ea1b90a46 100644
--- a/src/Controls/src/Core/Label/Label.Mapper.cs
+++ b/src/Controls/src/Core/Label/Label.Mapper.cs
@@ -61,7 +61,10 @@ namespace Microsoft.Maui.Controls
static void MapFormattedText(ILabelHandler handler, Label label)
{
if (label.IsConnectingHandler())
+ {
+ // If we're connecting the handler, we don't want to map the text multiple times.
return;
+ }
MapText(handler, label);
}
@@ -141,10 +144,10 @@ namespace Microsoft.Maui.Controls
static void MapFont(ILabelHandler handler, Label label, Action<IElementHandler, IElement> baseMethod)
{
- if (label.HasFormattedTextSpans)
+ // if there is formatted text,
+ // then we re-apply the whole formatted text
+ if (label.HasFormattedTextSpans && !handler.IsConnectingHandler())
{
- // if there is formatted text,
- // then we re-apply the whole formatted text
handler.UpdateValue(nameof(FormattedText));
}
else if (label.TextType == TextType.Text || !IsDefaultFont(label))
@@ -157,7 +160,7 @@ namespace Microsoft.Maui.Controls
static void MapTextColor(ILabelHandler handler, Label label, Action<IElementHandler, IElement> baseMethod)
{
- if (label.HasFormattedTextSpans)
+ if (label.HasFormattedTextSpans && !handler.IsConnectingHandler())
{
// if there is formatted text,
// then we re-apply the whole formatted text
@@ -198,4 +201,4 @@ namespace Microsoft.Maui.Controls
return true;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Controls/src/Core/Label/Label.iOS.cs b/src/Controls/src/Core/Label/Label.iOS.cs
index 1ad5c69abe..3463430f7d 100644
--- a/src/Controls/src/Core/Label/Label.iOS.cs
+++ b/src/Controls/src/Core/Label/Label.iOS.cs
@@ -1,5 +1,4 @@
#nullable disable
-using System;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using UIKit;
@@ -23,7 +22,11 @@ namespace Microsoft.Maui.Controls
{
Platform.LabelExtensions.UpdateText(handler.PlatformView, label);
- MapFormatting(handler, label);
+ if (!handler.IsConnectingHandler())
+ {
+ // Any text update requires that we update any attributed string formatting
+ MapFormatting(handler, label);
+ }
}
public static void MapLineBreakMode(ILabelHandler handler, Label label)
diff --git a/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs b/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
index 22c370c947..f51048aabe 100644
--- a/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
+++ b/src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs
@@ -39,8 +39,9 @@ namespace Microsoft.Maui.Controls.Platform
TextAlignment defaultHorizontalAlignment = TextAlignment.Start,
Font? defaultFont = null,
Color? defaultColor = null,
- TextTransform defaultTextTransform = TextTransform.Default)
- => formattedString.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, LineBreakMode.WordWrap, defaultCharacterSpacing: 0d);
+ TextTransform defaultTextTransform = TextTransform.Default,
+ LineBreakMode defaultLineBreakMode = LineBreakMode.WordWrap)
+ => formattedString.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, defaultLineBreakMode, defaultCharacterSpacing: 0d);
internal static NSAttributedString ToNSAttributedString(
this FormattedString formattedString,
@@ -81,8 +82,9 @@ namespace Microsoft.Maui.Controls.Platform
TextAlignment defaultHorizontalAlignment = TextAlignment.Start,
Font? defaultFont = null,
Color? defaultColor = null,
- TextTransform defaultTextTransform = TextTransform.Default)
- => span.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, LineBreakMode.WordWrap, defaultCharacterSpacing: 0d);
+ TextTransform defaultTextTransform = TextTransform.Default,
+ LineBreakMode defaultLineBreakMode = LineBreakMode.WordWrap)
+ => span.ToNSAttributedString(fontManager, defaultLineHeight, defaultHorizontalAlignment, defaultFont, defaultColor, defaultTextTransform, defaultLineBreakMode, defaultCharacterSpacing: 0d);
internal static NSAttributedString ToNSAttributedString(
this Span span,
@@ -92,7 +94,7 @@ namespace Microsoft.Maui.Controls.Platform
Font? defaultFont,
Color? defaultColor,
TextTransform defaultTextTransform,
- LineBreakMode lineBreakMode,
+ LineBreakMode defaultLineBreakMode,
double defaultCharacterSpacing = 0d)
{
var defaultFontSize = defaultFont?.Size ?? fontManager.DefaultFontSize;
@@ -123,16 +125,27 @@ namespace Microsoft.Maui.Controls.Platform
_ => UITextAlignment.Left
};
- style.LineBreakMode = lineBreakMode switch
+ #if !MACOS
+ style.LineBreakMode = defaultLineBreakMode switch
{
LineBreakMode.NoWrap => UILineBreakMode.Clip,
- LineBreakMode.WordWrap => UILineBreakMode.WordWrap,
LineBreakMode.CharacterWrap => UILineBreakMode.CharacterWrap,
LineBreakMode.HeadTruncation => UILineBreakMode.HeadTruncation,
- LineBreakMode.TailTruncation => UILineBreakMode.TailTruncation,
LineBreakMode.MiddleTruncation => UILineBreakMode.MiddleTruncation,
+ LineBreakMode.TailTruncation => UILineBreakMode.TailTruncation,
_ => UILineBreakMode.WordWrap
};
+ #else
+ style.LineBreakMode = defaultLineBreakMode switch
+ {
+ LineBreakMode.NoWrap => NSLineBreakMode.Clipping,
+ LineBreakMode.CharacterWrap => NSLineBreakMode.CharWrapping,
+ LineBreakMode.HeadTruncation => NSLineBreakMode.TruncatingHead,
+ LineBreakMode.MiddleTruncation => NSLineBreakMode.TruncatingMiddle,
+ LineBreakMode.TailTruncation => NSLineBreakMode.TruncatingTail,
+ _ => NSLineBreakMode.ByWordWrapping
+ };
+ #endif
var font = span.GetEffectiveFont(defaultFontSize, defaultFont);
var hasUnderline = false;
diff --git a/src/Core/src/Handlers/Button/ButtonHandler.cs b/src/Core/src/Handlers/Button/ButtonHandler.cs
index 89e0ce92b1..76e5fe4c51 100644
--- a/src/Core/src/Handlers/Button/ButtonHandler.cs
+++ b/src/Core/src/Handlers/Button/ButtonHandler.cs
@@ -27,10 +27,10 @@ namespace Microsoft.Maui.Handlers
public static IPropertyMapper<ITextButton, IButtonHandler> TextButtonMapper = new PropertyMapper<ITextButton, IButtonHandler>()
{
+ [nameof(IText.Text)] = MapText,
[nameof(ITextStyle.CharacterSpacing)] = MapCharacterSpacing,
[nameof(ITextStyle.Font)] = MapFont,
[nameof(ITextStyle.TextColor)] = MapTextColor,
- [nameof(IText.Text)] = MapText
};
public static IPropertyMapper<IButton, IButtonHandler> Mapper = new PropertyMapper<IButton, IButtonHandler>(TextButtonMapper, ImageButtonMapper, ViewHandler.ViewMapper)
diff --git a/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs b/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs
index 678e953c4a..d5cdfc83d3 100644
--- a/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs
+++ b/src/Core/src/Handlers/Button/ButtonHandler.iOS.cs
@@ -103,8 +103,11 @@ namespace Microsoft.Maui.Handlers
{
handler.PlatformView?.UpdateText(button);
- // Any text update requires that we update any attributed string formatting
- MapFormatting(handler, button);
+ if (!handler.IsConnectingHandler())
+ {
+ // Any text update requires that we update any attributed string formatting
+ MapFormatting(handler, button);
+ }
}
public static void MapTextColor(IButtonHandler handler, ITextStyle button)
diff --git a/src/Core/src/Handlers/Entry/EntryHandler.cs b/src/Core/src/Handlers/Entry/EntryHandler.cs
index aa9347240b..7ba2417fa0 100644
--- a/src/Core/src/Handlers/Entry/EntryHandler.cs
+++ b/src/Core/src/Handlers/Entry/EntryHandler.cs
@@ -22,24 +22,34 @@ namespace Microsoft.Maui.Handlers
[nameof(IEntry.MaxLength)] = MapMaxLength,
};
- public static IPropertyMapper<IEntry, IEntryHandler> Mapper = new PropertyMapper<IEntry, IEntryHandler>(ViewHandler.ViewMapper, EntryPriorityMapper)
+ private static readonly IPropertyMapper<IEntry, IEntryHandler> TextMapper = new PropertyMapper<IEntry, IEntryHandler>
{
- [nameof(IEntry.Background)] = MapBackground,
- [nameof(IEntry.CharacterSpacing)] = MapCharacterSpacing,
[nameof(IEntry.ClearButtonVisibility)] = MapClearButtonVisibility,
+ // Ensure Text is mapped before MaxLength/CharacterSpacing/TextColor/Font
+ // due to them being applied to the native attributed string (i.e. AttributedText on iOS) created by mapping Text
+ [nameof(IEntry.Text)] = MapText,
+ [nameof(IEntry.MaxLength)] = MapMaxLength,
[nameof(IEntry.Font)] = MapFont,
+ [nameof(IEntry.CharacterSpacing)] = MapCharacterSpacing,
+ [nameof(IEntry.TextColor)] = MapTextColor
+ };
+
+ public static IPropertyMapper<IEntry, IEntryHandler> Mapper = new PropertyMapper<IEntry, IEntryHandler>(TextMapper, ViewHandler.ViewMapper)
+ {
+ [nameof(IEntry.Background)] = MapBackground,
[nameof(IEntry.IsPassword)] = MapIsPassword,
- [nameof(IEntry.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
- [nameof(IEntry.VerticalTextAlignment)] = MapVerticalTextAlignment,
[nameof(IEntry.IsReadOnly)] = MapIsReadOnly,
[nameof(IEntry.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled,
[nameof(IEntry.IsSpellCheckEnabled)] = MapIsSpellCheckEnabled,
[nameof(IEntry.Keyboard)] = MapKeyboard,
+ // HorizontalTextAlignment and VerticalTextAlignment must be mapped AFTER all InputType-setting
+ // operations (IsPassword, IsReadOnly, Keyboard, etc.) because on Android, setInputType() can
+ // reset the EditText gravity that UpdateVerticalAlignment/UpdateHorizontalAlignment sets.
+ [nameof(IEntry.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
+ [nameof(IEntry.VerticalTextAlignment)] = MapVerticalTextAlignment,
[nameof(IEntry.Placeholder)] = MapPlaceholder,
[nameof(IEntry.PlaceholderColor)] = MapPlaceholderColor,
[nameof(IEntry.ReturnType)] = MapReturnType,
- [nameof(IEntry.Text)] = MapText,
- [nameof(IEntry.TextColor)] = MapTextColor,
[nameof(IEntry.CursorPosition)] = MapCursorPosition,
[nameof(IEntry.SelectionLength)] = MapSelectionLength
};
@@ -69,4 +79,4 @@ namespace Microsoft.Maui.Handlers
PlatformView IEntryHandler.PlatformView => PlatformView;
}
-}
+}
\ No newline at end of file

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
The iOS Label performance was improved in PR Improve iOS Label performance #30864. In that PR, the Label and Entry Feature Matrix test sample and script were modified, which caused discrepancies in the expected images due to changes which is due to test sample's default property values. In this PR, I updated the test sample and re-saved the images accordingly.
Windows - The Entry is now unfocused, so I re-saved the latest image.
Android - I modified the test sample by altering the default values and re-saved two images.
Additionally, while working on the test sample changes, I identified and fixed issues in FormattedStringExtensions.
These updates improve formatted text rendering on iOS by correctly propagating span properties (font, character spacing, and line-break settings) from the label to each span. The layout logic is also more robust, falling back to MAUI’s calculated size when iOS has not yet provided a valid label size, preventing incorrect text positioning and rendering issues.
Issues Fixed
Contributing to #30864