Skip to content

Fix Shell flyout item template does not update selected visuals after DynamicResource changes - #35155

Merged
kubaflo merged 16 commits into
dotnet:inflight/currentfrom
devanathan-vaithiyanathan:fix-34931
Jun 29, 2026
Merged

kubaflo merged 16 commits into
dotnet:inflight/currentfrom
devanathan-vaithiyanathan:fix-34931

Conversation

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor

Note

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

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.

  • Android
  • Windows
  • iOS
  • Mac
Before After
Android
Before-fix.mov
Android
After-fix.mov

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Apr 27, 2026
@sheiksyedm sheiksyedm added area-controls-shell Shell Navigation, Routes, Tabs, Flyout community ✨ Community Contribution labels Apr 28, 2026
@sheiksyedm
sheiksyedm marked this pull request as ready for review April 28, 2026 13:29
Copilot AI review requested due to automatic review settings April 28, 2026 13:29
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 target is duplicated. Since target is already checked for null a few lines above, the second if (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 ResourcesChanged listener on View (via _viewResource). If BindingContext resources are also intended to trigger a visual-state refresh, consider adding/removing a listener when BindingContext changes (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 MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 7 findings

See inline comments for details.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Apr 28, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from MauiBot Apr 29, 2026
@dotnet dotnet deleted a comment from github-actions Bot Apr 29, 2026
@dotnet dotnet deleted a comment from azure-pipelines Bot Apr 29, 2026
@dotnet dotnet deleted a comment from Copilot AI Apr 29, 2026
@dotnet dotnet deleted a comment from Copilot AI Apr 29, 2026

@kubaflo kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The build is failing - could you please check?

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

The build is failing - could you please check?

@kubaflo , Based on AI suggestion, I have modified the test sample

@kubaflo

This comment has been minimized.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Gate Passed Confidence Low Platform Android


🗂️ 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.cs
  • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerCell.cs
  • src/Controls/src/Core/Handlers/Shell/Windows/ShellFlyoutItemView.cs
  • src/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 DynamicResource updates 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 Shell ItemTemplate using 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 gh is 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:1 compiles for MacCatalyst but no Mac snapshot baseline is included.
  • ⚠️ src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.cs:27 toggles 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 --required failed due unauthenticated gh (gh auth login). Public GitHub REST check-runs for head SHA showed maui-pr still 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.dll failed from a Windows file-lock IOException in sahara-itinerary-1.txt, outside this PR’s touched files.
  • Action taken: Invoked azdo-build-investigator context; 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 GoToState returns 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 UpdateVisualState guards _content.
  • Multiple subscriptions: Android removes from old Element before 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 kubaflo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you please resolve conflicts?

@devanathan-vaithiyanathan

Copy link
Copy Markdown
Contributor Author

Could you please resolve conflicts?

@kubaflo , I have resolved the conflicts

@kubaflo

This comment has been minimized.

KarthikRajaKalaimani and others added 16 commits June 29, 2026 13:09
…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"> |
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-shell Shell Navigation, Routes, Tabs, Flyout community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flyout Item issue when using DynamicResources

7 participants