Fix Shell flyout item template does not update selected visuals after DynamicResource changes - #35155
Conversation
|
/azp run maui-pr-uitests , maui-pr-devicetests |
There was a problem hiding this comment.
Pull request overview
Fixes a Shell flyout ItemTemplate issue where selected-item visuals (e.g., background color driven by DynamicResource) can become stale after resource updates, especially with recycled cells.
Changes:
- Core: add an internal
VisualStateManager.GoToState(..., force: true)path to allow reapplying the current visual state. - Platform flyout rendering: listen for resource changes and reapply the current visual state (Android/iOS/Windows) while cleaning up listeners during recycling/context changes.
- Tests: add HostApp repro page (Issue34931) + Appium UITest with screenshot verification.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/Controls/src/Core/VisualStateManager.cs |
Adds force support to reapply a visual state even when the requested state name matches the current state. |
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cs |
Updates recycled flyout view holders to manage resource listeners and force visual-state reapply on resource changes. |
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerCell.cs |
Adds resource-change listening for the flyout cell’s view and forces visual-state reapply when resources change. |
src/Controls/src/Core/Handlers/Shell/Windows/ShellFlyoutItemView.cs |
Adds resource-change listening and forces visual-state reapply on Windows flyout item views during context changes. |
src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml |
Adds a Shell ItemTemplate using DynamicResource + visual states to reproduce the stale-selected-visual issue. |
src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs |
Initializes dynamic resource values and provides pages used by the repro Shell. |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs |
Adds an Appium UITest that exercises navigation/resource changes and verifies selected visuals via screenshot. |
Comments suppressed due to low confidence (2)
src/Controls/src/Core/VisualStateManager.cs:120
- The null-check for
targetis duplicated. Sincetargetis already checked for null a few lines above, the secondif (target == null)block is unreachable and should be removed to avoid dead code.
if (target == null)
{
continue;
}
src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerCell.cs:124
- The PR description says iOS listens for resource changes from both the cell view and its BindingContext, but this code only registers a
ResourcesChangedlistener onView(via_viewResource). If BindingContext resources are also intended to trigger a visual-state refresh, consider adding/removing a listener whenBindingContextchanges (or update the PR description if only the view listener is required).
if (_bindingContext != null && _bindingContext is BaseShellItem baseShell)
baseShell.PropertyChanged -= OnElementPropertyChanged;
_bindingContext = value;
View.BindingContext = value;
if (_bindingContext != null && _bindingContext is BaseShellItem baseShell2)
{
baseShell2.PropertyChanged += OnElementPropertyChanged;
UpdateVisualState();
}
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 7 findings
See inline comments for details.
kubaflo
left a comment
There was a problem hiding this comment.
The build is failing - could you please check?
@kubaflo , Based on AI suggestion, I have modified the test sample |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@devanathan-vaithiyanathan — new AI review results are available based on this last commit:
699cb78. To request a fresh review after new comments or commits, comment/review rerun.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ✅ PASSED
Platform: ANDROID · Base: main · Merge base: a6d9e30a
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue34931 Issue34931 |
✅ FAIL — 2262s | ✅ PASS — 566s |
🔴 Without fix — 🖥️ Issue34931: FAIL ✅ · 2262s
(truncated to last 15,000 chars)
set by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/0dbd1639-4c0e-4a36-b8fc-11f3bd58088e/nuget/v3/flat2/system.reflection.metadata/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/516521bf-6417-457e-9a9c-0a4bdfde03e7/nuget/v3/flat2/system.reflection.metadata/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Failed to download package 'Magick.NET.Core.14.13.1' from 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/45bacae2-5efb-47c8-91e5-8ec20c22b4f8/nuget/v3/flat2/magick.net.core/14.13.1/magick.net.core.14.13.1.nupkg'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/0dbd1639-4c0e-4a36-b8fc-11f3bd58088e/nuget/v3/flat2/system.reflection.metadata/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/45bacae2-5efb-47c8-91e5-8ec20c22b4f8/nuget/v3/flat2/system.reflection.metadata/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 9.95 sec).
Failed to download package 'NUnit.3.13.3' from 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/45bacae2-5efb-47c8-91e5-8ec20c22b4f8/nuget/v3/flat2/nunit/3.13.3/nunit.3.13.3.nupkg'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 12.68 sec).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 1 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 2 ms).
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/45bacae2-5efb-47c8-91e5-8ec20c22b4f8/nuget/v3/flat2/microsoft.codeanalysis.csharp.workspaces/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/516521bf-6417-457e-9a9c-0a4bdfde03e7/nuget/v3/flat2/microsoft.codeanalysis.csharp.workspaces/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/f1040238-e8b2-49c9-84cf-abd79c186da2/nuget/v3/flat2/microsoft.codeanalysis.csharp.workspaces/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 2.51 sec).
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/f1040238-e8b2-49c9-84cf-abd79c186da2/nuget/v3/flat2/humanizer.core/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/516521bf-6417-457e-9a9c-0a4bdfde03e7/nuget/v3/flat2/humanizer.core/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/f1040238-e8b2-49c9-84cf-abd79c186da2/nuget/v3/flat2/system.collections.immutable/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/45bacae2-5efb-47c8-91e5-8ec20c22b4f8/nuget/v3/flat2/system.collections.immutable/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/0dbd1639-4c0e-4a36-b8fc-11f3bd58088e/nuget/v3/flat2/system.collections.immutable/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/516521bf-6417-457e-9a9c-0a4bdfde03e7/nuget/v3/flat2/system.collections.immutable/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/4828dfac-e9f8-49bc-acb6-319be99331fc/nuget/v3/flat2/system.collections.immutable/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/f1040238-e8b2-49c9-84cf-abd79c186da2/nuget/v3/flat2/system.threading.channels/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/516521bf-6417-457e-9a9c-0a4bdfde03e7/nuget/v3/flat2/system.threading.channels/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/0dbd1639-4c0e-4a36-b8fc-11f3bd58088e/nuget/v3/flat2/system.composition/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/4828dfac-e9f8-49bc-acb6-319be99331fc/nuget/v3/flat2/system.io.pipelines/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/d1622942-d16f-48e5-bc83-96f4539e7601/nuget/v3/flat2/system.io.pipelines/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/4828dfac-e9f8-49bc-acb6-319be99331fc/nuget/v3/flat2/system.composition.runtime/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/d1622942-d16f-48e5-bc83-96f4539e7601/nuget/v3/flat2/system.composition.convention/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/1a5f89f6-d8da-4080-b15f-242650c914a8/nuget/v3/flat2/system.composition.hosting/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Retrying 'FindPackagesByIdAsync' for source 'https://pkgs.dev.azure.com/dnceng/9ee6d478-d288-47f7-aacc-f6e6d082ae6d/_packaging/d1622942-d16f-48e5-bc83-96f4539e7601/nuget/v3/flat2/system.composition.hosting/index.json'.
An error occurred while sending the request.
Unable to read data from the transport connection: Connection reset by peer.
Connection reset by peer
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 5.8 sec).
5 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.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 /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 06/23/2026 15:26:53 FixtureSetup for Issue34931(Android)
>>>>> 06/23/2026 15:26:54 FlyoutSelectedStateReflectsUpdatedDynamicResource Start
>>>>> 06/23/2026 15:27:14 FlyoutSelectedStateReflectsUpdatedDynamicResource Stop
>>>>> 06/23/2026 15:27:14 Log types: logcat, bugreport, server
Failed FlyoutSelectedStateReflectsUpdatedDynamicResource [20 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: FlyoutSelectedStateReflectsUpdatedDynamicResource.png (10.90% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
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 VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
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 309
at Microsoft.Maui.TestCases.Tests.Issues.Issue34931.FlyoutSelectedStateReflectsUpdatedDynamicResource() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs:line 30
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
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.13] Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.53] Discovered: Controls.TestCases.Android.Tests
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue34931.trx
Test Run Failed.
Total tests: 1
Failed: 1
Total time: 41.6331 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue34931.trx
🟢 With fix — 🖥️ Issue34931: PASS ✅ · 566s
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:07:11.19
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Determining projects to restore...
All projects are up-to-date for restore.
Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14459929
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.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 /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 06/23/2026 15:46:04 FixtureSetup for Issue34931(Android)
>>>>> 06/23/2026 15:46:05 FlyoutSelectedStateReflectsUpdatedDynamicResource Start
>>>>> 06/23/2026 15:46:21 FlyoutSelectedStateReflectsUpdatedDynamicResource Stop
Passed FlyoutSelectedStateReflectsUpdatedDynamicResource [15 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.Android.Tests
[xUnit.net 00:00:00.56] Discovered: Controls.TestCases.Android.Tests
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue34931.trx
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 28.9514 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue34931.trx
📁 Fix files reverted (4 files)
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cssrc/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerCell.cssrc/Controls/src/Core/Handlers/Shell/Windows/ShellFlyoutItemView.cssrc/Controls/src/Core/VisualStateManager.cs
📋 Pre-Flight — Context & Validation
Issue: #34931 - Flyout Item issue when using DynamicResources
PR: #35155 - Fix Shell flyout item template does not update selected visuals after DynamicResource changes
Platforms Affected: Android, iOS, MacCatalyst, Windows (testing platform requested: android)
Files Changed: 4 implementation, 6 test
Key Findings
- The PR fixes stale selected Shell flyout visuals after runtime
DynamicResourceupdates by adding resource-change listeners in platform Shell flyout item code and a forced internal VSM reapply path. - Linked issue reports stale flyout item selected visuals when changing
Application.Current.Resources["Primary"]at runtime with a custom ShellItemTemplateusing VisualState setters. - Prior review comments flagged test determinism and screenshot baseline coverage; the duplicate
Assign<T>compile issue appears fixed in current code. - Required CI status could not be fully collected because
ghis unauthenticated; public metadata/comments were used and CI coverage remains undetermined.
Code Review Summary
Verdict: NEEDS_CHANGES
Confidence: low
Errors: 1 | Warnings: 1 | Suggestions: 0
Key code review findings:
- ❌
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs:1compiles for MacCatalyst but no Mac snapshot baseline is included. ⚠️ src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs:27toggles the resource back to the initial color before screenshot verification, weakening the regression signal.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35155 | Add platform resource listeners and an internal forced VisualStateManager.GoToState path to reapply selected/normal Shell flyout item visual states after resource changes. |
✅ PASSED (Gate) | ShellFlyoutRecyclerAdapter.cs, UIContainerCell.cs, ShellFlyoutItemView.cs, VisualStateManager.cs, UI test/snapshots |
Original PR; gate result supplied by caller. |
🔬 Code Review — Deep Analysis
Code Review — PR #35155
Independent Assessment
What this changes: Reapplies Shell flyout item visual states when resource values change by adding an internal forced VisualStateManager.GoToState(..., force: true) path and platform resource-change listeners for Android, iOS, and Windows flyout item templates. Adds an Appium screenshot regression test for issue 34931.
Inferred motivation: Shell flyout item templates using DynamicResource could keep stale selected-state colors after runtime resource changes and item reuse.
Reconciliation with PR Narrative
Author claims: Fixes stale Shell flyout selected visuals with DynamicResource changes across Android/iOS/Windows/Mac and adds regression coverage.
Agreement/disagreement: The implementation generally matches the claim. However, the test is compiled for MacCatalyst but this PR does not add the required Mac snapshot baseline, so the claimed platform coverage is incomplete.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
Duplicate Assign<T> helper caused CS0121 build failures |
MauiBot reviews/gate | ✅ Fixed | Current Issue34931.xaml.cs no longer defines or calls Assign<T>. |
| Screenshot baselines missing for compiled platforms | MauiBot inline / expert reviewer | ❌ Unresolved | Android/iOS baselines were added, but MacCatalyst still compiles the test and no TestCases.Mac.Tests/snapshots/mac/FlyoutSelectedStateReflectsUpdatedDynamicResource.png exists. |
| Test toggles resource back to initial color before screenshot | MauiBot inline | ❌ Unresolved | Issue34931.cs:27 still taps ChangeColorButton a second time before VerifyScreenshot(). |
Blast Radius Assessment
- Runs for all instances: No; forced VSM path is internal and used by Shell flyout item code, but listeners run for every custom Shell flyout item on affected platforms.
- Startup impact: No; applies during Shell flyout item creation/resource changes.
- Static/shared state: No new static/shared state.
CI Status
- Required-check result:
gh pr checks --requiredfailed due unauthenticatedgh(gh auth login). Public GitHub REST check-runs for head SHA showedmaui-prstill queued/in-progress, one failed Helix unit-test check, and many pending checks. - Classification: CI overall undetermined/pending. The observed failure is likely unrelated:
Microsoft.Maui.Essentials.AI.UnitTests.dllfailed from a Windows file-lock IOException insahara-itinerary-1.txt, outside this PR’s touched files. - Action taken: Invoked
azdo-build-investigatorcontext; inspected public GitHub/Azure/Helix logs; confidence capped low and no LGTM.
Findings
❌ Error — MacCatalyst screenshot baseline is missing
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs:1 compiles this test whenever TEST_FAILS_ON_WINDOWS is defined. The Mac UI test project defines that constant, and VerifyScreenshot() at line 30 resolves Mac baselines under TestCases.Mac.Tests/snapshots/mac/. This PR adds Android and iOS baselines only, so the MacCatalyst UI test will fail on missing baseline if run.
⚠️ Warning — Test screenshots after toggling back to the initial resource
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs:27 taps ChangeColorButton a second time before opening the flyout and screenshotting. That validates a second resource update back to the initial color, not the updated #FF6347 state asserted earlier. This weakens the regression signal for the exact user scenario.
Failure-Mode Probing
- Resource changes on items without custom VSM: listener may fire, but
GoToStatereturns false if no non-default VSM groups exist. - Handler/cell reuse: Android and iOS remove listeners on element/cell disconnect; Windows removes on DataContext replacement.
- Null/disconnected callbacks: Android/iOS callbacks are detached before nulling views; Windows
UpdateVisualStateguards_content. - Multiple subscriptions: Android removes from old
Elementbefore adding to new one; iOS stores one view listener per cell.
Verdict: NEEDS_CHANGES
Confidence: low
Summary: The implementation approach is plausible, but the added test is currently incomplete for MacCatalyst and CI is not green/complete. The missing Mac snapshot is a concrete blocking issue because the test is compiled for Mac but has no baseline.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Android-local state-toggle reapplication: on resource change, transition through the opposite VSM state and back to the desired selected/normal state, avoiding a new forced VSM API. | ✅ PASS (Android FullyQualifiedName~Issue34931) |
1 file | Passed Android regression test; self-review clean. Different from PR because it leaves VisualStateManager.cs unchanged. |
| PR | PR #35155 | Add platform resource listeners plus internal VisualStateManager.GoToState(..., force: true) so current selected/normal states can be reapplied after DynamicResource changes. |
✅ PASSED (Gate) | 4 implementation files + tests/snapshots | Original PR; broader cross-platform implementation, but code review found test issues outside Android. |
Candidate 1 Details
Approach: State-Toggle Reapplication (No VSM API Change)
Instead of adding force: true to VSM's GoToState, this approach toggles through the opposite visual state and back when resources change (GoToState("Normal") -> GoToState("Selected"), or vice versa). This forces VSM to naturally unapply current setters and reapply them with fresh DynamicResource values using existing API semantics.
Diff: See CustomAgentLogsTmp/PRState/35155/PRAgent/try-fix-1/content.md and CustomAgentLogsTmp/PRState/35155/PRAgent/try-fix/attempt-1/fix.diff.
Test Results: ✅ PASS on Android emulator for:
pwsh .github/scripts/BuildAndRunHostApp.ps1 -Platform android -TestFilter "FullyQualifiedName~Issue34931"Failure Analysis: Not applicable; attempt passed. Minor limitation: this candidate is Android-only. Adopting this strategy instead of the PR's current fix would require analogous localized implementations for iOS and Windows/MacCatalyst.
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | Yes | State-toggle reapplication produced a passing Android candidate. |
Exhausted: No — stopped because Candidate #1 passed the requested Android regression criteria and is meaningfully different from the PR's current fix.
Selected Fix: Candidate #1 for Android-only alternative evaluation — it is narrower and avoids modifying VisualStateManager.cs. PR #35155 remains broader for cross-platform coverage unless analogous localized platform implementations are added.
🧭 Next Steps — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
@kubaflo , I have resolved the conflicts |
This comment has been minimized.
This comment has been minimized.
…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"> |
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment whether this change resolves your issue. Thank you!
Issue Details
Flyout items were keeping the old selected background color after selection changed, especially with DynamicResource colors and recycled cells.
Description of Change
Core: Added a forced visual-state reapply path in VisualStateManager so controls can re-enter the current state when resource values change, instead of no-oping when the requested state name matches the active state.
iOS: Updated UIContainerCell.cs to listen for resource changes from both the cell view and its binding context, and to force the selected visual state to refresh when those resources change.
Android: Updated ShellFlyoutRecyclerAdapter.cs so recycled flyout view holders detach listeners from the previous item, attach to the new item, and reapply the current visual state when DynamicResource values change.
Windows: Updated ShellFlyoutItemView.cs to correctly manage resource listeners during context changes and to force the visual state to reapply after relevant resource updates.
Issues Fixed
Fixes #34931
Tested the behavior in the following platforms.
Before-fix.mov
After-fix.mov