Skip to content

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

Open
devanathan-vaithiyanathan wants to merge 13 commits intodotnet:mainfrom
devanathan-vaithiyanathan:fix-34931
Open

Fix Shell flyout item template does not update selected visuals after DynamicResource changes#35155
devanathan-vaithiyanathan wants to merge 13 commits intodotnet:mainfrom
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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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();
				}

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

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
@kubaflo kubaflo dismissed stale reviews from MauiBot and MauiBot May 3, 2026 12:45

Resetting for re-review

MauiBot
MauiBot previously requested changes May 3, 2026
Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

🤖 Automated review — alternative fix proposed

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

Why: try-fix-2 (VisualStateGroupList self-subscription) wins: it fixes the DynamicResource stale-state bug surgically at the VSM core level with only 2 files changed, key-filters resource change events to avoid redundant re-applies, makes no platform-specific changes, and also resolves the CS0121 build error in the test host app. All 4 try-fix candidates pass VSM unit regression tests; the original PR fix failed the Gate.

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

Candidate diff (`try-fix-2`)
diff --git a/src/Controls/src/Core/VisualStateManager.cs b/src/Controls/src/Core/VisualStateManager.cs
index 6c74249d29..13576c03e3 100644
--- a/src/Controls/src/Core/VisualStateManager.cs
+++ b/src/Controls/src/Core/VisualStateManager.cs
@@ -4,6 +4,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Linq;
+using Microsoft.Maui.Controls.Internals;
 using Microsoft.Maui.Controls.Xaml;
 
 namespace Microsoft.Maui.Controls
@@ -162,7 +163,7 @@ namespace Microsoft.Maui.Controls
 		/// Only these states promote an implicit-style VSM setter to full VSM priority (fix for #34363),
 		/// preventing custom developer-defined states from unexpectedly overriding manually-set values.
 		/// </summary>
-		static bool IsSystemDrivenState(string stateName) =>
+		internal static bool IsSystemDrivenState(string stateName) =>
 			stateName == CommonStates.Disabled ||
 			stateName == CommonStates.Focused ||
 			stateName == CommonStates.Unfocused ||
@@ -280,8 +281,16 @@ namespace Microsoft.Maui.Controls
 
 		void ValidateAndNotify(IList<VisualStateGroup> groups)
 		{
-			if (groups.Count > 0)
+			if (groups.Count > 0 && IsDefault)
+			{
 				IsDefault = false;
+				if (VisualElement is IElementDefinition ed)
+					ed.AddResourcesChangedListener(OnVisualElementResourcesChanged);
+			}
+			else if (groups.Count > 0)
+			{
+				IsDefault = false;
+			}
 
 			Validate(groups);
 			OnStatesChanged();
@@ -395,12 +404,64 @@ namespace Microsoft.Maui.Controls
 			}
 			set
 			{
-				_visualElement = new WeakReference<VisualElement>(value);
+				var oldElement = VisualElement;
+				if (oldElement is IElementDefinition oldDef)
+					oldDef.RemoveResourcesChangedListener(OnVisualElementResourcesChanged);
+
+				_visualElement = value != null ? new WeakReference<VisualElement>(value) : null;
+
+				if (value is IElementDefinition newDef && !IsDefault)
+					newDef.AddResourcesChangedListener(OnVisualElementResourcesChanged);
 			}
 		}
 
 		internal SetterSpecificity Specificity { get; set; }
 
+		void OnVisualElementResourcesChanged(object sender, ResourcesChangedEventArgs e)
+		{
+			var visualElement = VisualElement;
+			if (visualElement == null || IsDefault || Specificity == default)
+				return;
+
+			var baseSpecificity = Specificity.CopyStyle(1, 0, 0, 0);
+			HashSet<string> changedKeys = null;
+			if (e.Values != null)
+			{
+				foreach (var kv in e.Values)
+				{
+					changedKeys ??= new HashSet<string>(StringComparer.Ordinal);
+					changedKeys.Add(kv.Key);
+				}
+			}
+
+			foreach (var group in _internalList)
+			{
+				var currentState = group.CurrentState;
+				if (currentState == null)
+					continue;
+
+				var applySpecificity = VisualStateManager.IsSystemDrivenState(currentState.Name)
+					? baseSpecificity.WithFullVsmPriority()
+					: baseSpecificity;
+
+				foreach (var setter in currentState.Setters)
+				{
+					if (setter.Value is DynamicResource dr
+						&& (changedKeys == null || changedKeys.Contains(dr.Key)))
+					{
+						try
+						{
+							setter.Apply(visualElement, applySpecificity);
+						}
+						catch (Exception ex) when (ex is XamlParseException || ex is ArgumentNullException)
+						{
+							// Named target unavailable — skip this setter
+						}
+					}
+				}
+			}
+		}
+
 		void OnStatesChanged()
 		{
 			VisualElement?.ChangeVisualState();
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs
index 4104ed4ab1..c5c4ac9dac 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs
@@ -146,13 +146,3 @@ public class Issue34931ThirdPage : ContentPage
 		};
 	}
 }
-
-static class Issue34931ViewExtensions
-{
-	public static T Assign<T>(this T view, out T assigned)
-		where T : BindableObject
-	{
-		assigned = view;
-		return view;
-	}
-}

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

Expert Review — 8 findings

See inline comments for details.

@MauiBot MauiBot added s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels May 3, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 4, 2026
MauiBot
MauiBot previously requested changes May 4, 2026
Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

🤖 Automated review — alternative fix proposed

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

Why: try-fix-3 passes the UI test with a single-file change to VisualStateManager.cs that centralizes DynamicResource state refresh for all platforms automatically. It also addresses the performance concern from code review by filtering re-application to only the VSM setters whose changed resource keys are actually referenced, avoiding unnecessary UnApply+Apply cycles on unrelated keys. The PR's gate failed and its force-param approach requires per-platform adapter changes with unconditional full-cycle refreshes.

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-3`)
diff --git a/src/Controls/src/Core/VisualStateManager.cs b/src/Controls/src/Core/VisualStateManager.cs
index 6c74249d29..58bb21728a 100644
--- a/src/Controls/src/Core/VisualStateManager.cs
+++ b/src/Controls/src/Core/VisualStateManager.cs
@@ -4,6 +4,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Linq;
+using Microsoft.Maui.Controls.Internals;
 using Microsoft.Maui.Controls.Xaml;
 
 namespace Microsoft.Maui.Controls
@@ -29,6 +30,12 @@ namespace Microsoft.Maui.Controls
 				defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged,
 				defaultValueCreator: bindable => new VisualStateGroupList(true) { VisualElement = (VisualElement)bindable });
 
+		static readonly BindablePropertyKey IsListeningForDynamicResourceStateChangesPropertyKey =
+			BindableProperty.CreateAttachedReadOnly("IsListeningForDynamicResourceStateChanges", typeof(bool), typeof(VisualElement), false);
+
+		static readonly BindableProperty IsListeningForDynamicResourceStateChangesProperty =
+			IsListeningForDynamicResourceStateChangesPropertyKey.BindableProperty;
+
 		static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
 		{
 			if (oldValue is VisualStateGroupList { VisualElement: { } oldElement } oldVisualStateGroupList)
@@ -60,6 +67,7 @@ namespace Microsoft.Maui.Controls
 				((VisualStateGroupList)newValue).VisualElement = visualElement;
 
 			visualElement.ChangeVisualState();
+			UpdateDynamicResourceStateListener(visualElement, newValue as VisualStateGroupList);
 
 			UpdateStateTriggers(visualElement);
 		}
@@ -87,6 +95,9 @@ namespace Microsoft.Maui.Controls
 		/// <param name="name">The name of the visual state to transition to.</param>
 		/// <returns><see langword="true"/> if the transition was successful; otherwise, <see langword="false"/>.</returns>
 		public static bool GoToState(VisualElement visualElement, string name)
+			=> GoToState(visualElement, name, force: false);
+
+		internal static bool GoToState(VisualElement visualElement, string name, bool force)
 		{
 			var context = visualElement.GetContext(VisualStateGroupsProperty);
 			if (context is null)
@@ -108,12 +119,6 @@ namespace Microsoft.Maui.Controls
 
 			foreach (VisualStateGroup group in groups)
 			{
-				if (group.CurrentState?.Name == name)
-				{
-					// We're already in the target state; nothing else to do
-					return true;
-				}
-
 				// See if this group contains the new state
 				var target = group.GetState(name);
 				if (target == null)
@@ -121,6 +126,13 @@ namespace Microsoft.Maui.Controls
 					continue;
 				}
 
+				if (group.CurrentState?.Name == name && !force)
+				{
+					// We're already in the target state; nothing else to do
+					UpdateDynamicResourceStateListener(visualElement, groups);
+					return true;
+				}
+
 				// If we've got a new state to transition to, unapply the setters from the current state
 				if (group.CurrentState != null)
 				{
@@ -150,12 +162,148 @@ namespace Microsoft.Maui.Controls
 					setter.Apply(visualElement, applySpecificity);
 				}
 
+				UpdateDynamicResourceStateListener(visualElement, groups);
 				return true;
 			}
 
+			UpdateDynamicResourceStateListener(visualElement, groups);
+			return false;
+		}
+
+		static void UpdateDynamicResourceStateListener(VisualElement visualElement, VisualStateGroupList groups)
+		{
+			var shouldListen = HasCurrentStateDynamicResourceSetter(groups);
+			var isListening = (bool)visualElement.GetValue(IsListeningForDynamicResourceStateChangesProperty);
+
+			if (shouldListen && !isListening)
+			{
+				((IElementDefinition)visualElement).AddResourcesChangedListener(OnVisualElementResourcesChanged);
+				visualElement.SetValue(IsListeningForDynamicResourceStateChangesPropertyKey, true);
+			}
+			else if (!shouldListen && isListening)
+			{
+				((IElementDefinition)visualElement).RemoveResourcesChangedListener(OnVisualElementResourcesChanged);
+				visualElement.SetValue(IsListeningForDynamicResourceStateChangesPropertyKey, false);
+			}
+		}
+
+		static bool HasCurrentStateDynamicResourceSetter(VisualStateGroupList groups)
+		{
+			if (groups?.IsDefault != false)
+			{
+				return false;
+			}
+
+			foreach (var group in groups)
+			{
+				if (HasDynamicResourceSetter(group.CurrentState))
+				{
+					return true;
+				}
+			}
+
+			return false;
+		}
+
+		static bool HasDynamicResourceSetter(VisualState state)
+		{
+			if (state is null)
+			{
+				return false;
+			}
+
+			foreach (var setter in state.Setters)
+			{
+				if (setter.Value is DynamicResource || setter.Value is AppThemeBinding)
+				{
+					return true;
+				}
+			}
+
 			return false;
 		}
 
+		static void OnVisualElementResourcesChanged(object sender, ResourcesChangedEventArgs e)
+		{
+			// sender is the subscribed element (Element.OnResourcesChanged invokes handlers with `this`).
+			if (sender is not VisualElement visualElement)
+			{
+				return;
+			}
+
+			var context = visualElement.GetContext(VisualStateGroupsProperty);
+			if (context is null)
+			{
+				UpdateDynamicResourceStateListener(visualElement, null);
+				return;
+			}
+
+			var vsgSpecificityValue = context.Values.GetSpecificityAndValue();
+			var groups = (VisualStateGroupList)vsgSpecificityValue.Value;
+			if (groups?.IsDefault != false)
+			{
+				UpdateDynamicResourceStateListener(visualElement, groups);
+				return;
+			}
+
+			var changedKeys = e.Values?
+				.Select(v => v.Key)
+				.ToHashSet(StringComparer.Ordinal);
+
+			if (changedKeys is null || changedKeys.Count == 0)
+			{
+				return;
+			}
+
+			var baseSpecificity = vsgSpecificityValue.Key.CopyStyle(1, 0, 0, 0);
+			foreach (var group in groups)
+			{
+				var currentState = group.CurrentState;
+				if (!HasDynamicResourceSetter(currentState))
+				{
+					continue;
+				}
+
+				var stateSpecificity = IsSystemDrivenState(currentState.Name)
+					? baseSpecificity.WithFullVsmPriority()
+					: baseSpecificity;
+
+				foreach (var setter in currentState.Setters)
+				{
+					if (!SetterDependsOnChangedResource(setter, changedKeys))
+					{
+						continue;
+					}
+
+					setter.UnApply(visualElement, stateSpecificity);
+					setter.Apply(visualElement, stateSpecificity);
+				}
+			}
+
+			UpdateDynamicResourceStateListener(visualElement, groups);
+		}
+
+		static bool SetterDependsOnChangedResource(Setter setter, HashSet<string> changedKeys)
+		{
+			if (setter?.Value is DynamicResource dynamicResource)
+			{
+				return changedKeys.Contains(dynamicResource.Key);
+			}
+
+			if (setter?.Value is AppThemeBinding appThemeBinding)
+			{
+				return IsDynamicResourceValueChanged(appThemeBinding.Light, changedKeys) ||
+					IsDynamicResourceValueChanged(appThemeBinding.Dark, changedKeys) ||
+					IsDynamicResourceValueChanged(appThemeBinding.Default, changedKeys) ||
+					changedKeys.Contains(AppThemeBinding.AppThemeResource);
+			}
+
+			return false;
+		}
+
+		static bool IsDynamicResourceValueChanged(object resourceValue, HashSet<string> changedKeys) =>
+			resourceValue is DynamicResource dynamicResource && changedKeys.Contains(dynamicResource.Key);
+
 		/// <summary>
 		/// Returns <see langword="true"/> for states that the MAUI framework drives automatically
 		/// (Disabled, Focused, Unfocused, Selected, PointerOver, Pressed).

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues and removed s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) labels May 4, 2026
@dotnet dotnet deleted a comment from MauiBot May 8, 2026
@kubaflo kubaflo dismissed MauiBot’s stale review May 8, 2026 01:22

Resetting for re-review

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented May 8, 2026

🤖 AI Summary

👋 @devanathan-vaithiyanathan — new AI review results are available. Please review the latest session below.

📊 Review Session19e3b8f · co pilot changes added · 2026-05-08 02:26 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ❌ FAILED

Platform: ANDROID · Base: main · Merge base: b71adea6

🩺 Fix does not compile — applying the PR's fix produces a build error before tests can run. The earlier-than-test failure is the root cause; the per-test ❌ FAIL marks are downstream effects, not real test failures.

/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Ass...

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue34931 Issue34931 🛠️ BUILD ERROR 🛠️ BUILD ERROR
🔴 Without fix — 🖥️ Issue34931: 🛠️ BUILD ERROR · 630s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]

Build FAILED.

/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
    0 Warning(s)
    5 Error(s)

Time Elapsed 00:05:35.66
* daemon not running; starting now at tcp:5037
* daemon started successfully
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  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.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.70-ci+azdo.14045739
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]

Build FAILED.

/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
    0 Warning(s)
    5 Error(s)

Time Elapsed 00:04:35.50

🟢 With fix — 🖥️ Issue34931: 🛠️ BUILD ERROR · 568s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  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.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  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
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]

Build FAILED.

/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
    0 Warning(s)
    5 Error(s)

Time Elapsed 00:04:35.29
* daemon not running; starting now at tcp:5037
* daemon started successfully
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.70-ci+azdo.14045739
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.14045739
  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.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]

Build FAILED.

/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(78,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(83,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(88,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34583.cs(93,11): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Assign<TView>(TView, out TView)' and 'Issue34931ViewExtensions.Assign<T>(T, out T)' [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
    0 Warning(s)
    5 Error(s)

Time Elapsed 00:04:37.57

⚠️ Failure Details

  • 🛠️ Issue34931 without fix: build failed before tests could run
    • /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Ass...
  • 🛠️ Issue34931 with fix: build failed (fix does not compile)
    • /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs(69,8): error CS0121: The call is ambiguous between the following methods or properties: 'Issue34583ViewExtensions.Ass...
📁 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

🧪 UI Tests — Category Detection

Detected UI test categories: Shell,VisualStateManager

🧪 UI Test Execution Results

FAILED — 0 passed, 2 failed, 0 skipped (platform: android)

Category Result Tests Duration Notes
Shell ❌ FAILED 773.7s exit code 1
VisualStateManager ❌ FAILED 462.3s exit code 1

Failures here are informational only — they do not block the gate or affect try-fix candidate scoring.

ℹ️ This section was reconstructed from the build artifact (35155 build) after the run completed.


🔍 Regression Cross-Reference

🔍 Regression Cross-Reference

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


🔍 Pre-Flight — Context & Validation

Pre-Flight — PR #35155

PR Summary

  • Title: Fix Shell flyout item template does not update selected visuals after DynamicResource changes
  • Author: devanathan-vaithiyanathan
  • Base: main
  • Issue fixed: Flyout Item issue when using DynamicResources #34931 (Flyout Item issue when using DynamicResources, partner/syncfusion, platform/ios — but PR also touches Android & Windows & Core)
  • Files changed (7):
    • src/Controls/src/Core/VisualStateManager.cs — adds internal force overload of GoToState that re-applies setters even when the requested state is the active state.
    • src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cs — adds/removes IElementDefinition.ResourcesChanged listener on Element setter; UpdateVisualState now calls GoToState(..., force: true). Also relaxes the previous _element is BaseShellItem guard around unsubscribing.
    • src/Controls/src/Core/Compatibility/Handlers/Shell/iOS/UIContainerCell.cs — caches view as IElementDefinition in ctor, hooks OnResourcesChanged; tears down in Disconnect. UpdateVisualState uses force: true.
    • src/Controls/src/Core/Handlers/Shell/Windows/ShellFlyoutItemView.cs — same pattern: subscribe/unsubscribe IElementDefinition.ResourcesChanged per content lifecycle; UpdateVisualState uses force: true.
    • HostApp: Issue34931.xaml + Issue34931.xaml.cs (test app: TestShell with custom Shell.ItemTemplate using DynamicResource Primary, button rotates Application.Current.Resources["Primary"] through 4 colors).
    • UITest: Tests/Issues/Issue34931.cs — Android/iOS only (#if TEST_FAILS_ON_WINDOWS); navigates the flyout, taps Change-Color twice, screenshots.

Root Cause (independent reading of code)

The bug surface had two parts that the PR addresses:

  1. VSM short-circuit on resource changes. Pre-fix VisualStateManager.GoToState exited early when group.CurrentState?.Name == name. When a DynamicResource value (e.g., Primary) changes at runtime, the Selected/Normal state name is unchanged, so the VSM never re-runs Setter.Apply, leaving the previously-resolved color baked into the cell. With recycled flyout cells this is visually persistent.

  2. No subscription to resource changes on the cell View. The Shell flyout cell views (UIContainerCell/ShellFlyoutRecyclerAdapter/ShellFlyoutItemView) only updated visuals when IsChecked changed. They didn't observe IElementDefinition.ResourcesChanged, so even if VSM were forced, no one called GoToState.

The PR adds force to bypass the short-circuit and uses IElementDefinition.AddResourcesChangedListener to drive the forced reapply on resource changes — a pattern already used by AppThemeBinding, Border, Shape, VisualElement background/shadow.

Classification of Changed Files

Path Area Risk
Core/VisualStateManager.cs Public framework, hot path for visual states Medium-High — touches behavior of the public GoToState for all visual elements. Reordering target == null check before the "already in state" return changes a subtle case (see Findings).
Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cs Android Shell flyout Medium
Compatibility/Handlers/Shell/iOS/UIContainerCell.cs iOS Shell flyout Medium
Handlers/Shell/Windows/ShellFlyoutItemView.cs Windows Shell flyout Medium
TestCases.HostApp/Issues/Issue34931.* Test app Low
TestCases.Shared.Tests/Tests/Issues/Issue34931.cs UI test Low

Code Review (independence-first)

❌ Errors

None blocking.

⚠️ Warnings

W1. VisualStateManager.GoToState semantic change for "no matching state in any group".
Pre-fix flow: foreach group → if already-in-name, return true; else if no target, continue. Post-fix flow moves the target == null continue before the already-in-state return.

Behavioral diff: previously, if a VisualElement had multiple VisualStateGroups and the first group had CurrentState.Name == name (e.g. shared name "Normal" exists in only some groups), GoToState returned true immediately. Now it walks every group looking for a matching target before returning. For a single-group flyout cell this is fine, but it slightly changes ordering semantics for multi-group VSMs. Probably benign (more correct, in fact: previously if the matched group had no target with that name it could return true incorrectly via a different group's CurrentState). Worth a note in the PR description.

W2. iOS — listener attached to the cell View, not to BindingContext.
UIContainerCell subscribes once in the constructor on view. Resource changes propagate down the logical tree via ResourcesChanged, and View is reparented to the BaseShellItem (bsi.AddLogicalChild(View)), which is itself a child of Shell/Application. So Application.Current.Resources["Primary"] = … does fire OnResourcesChanged on the View. ✓ correct.

But: UpdateVisualState reads BindingContext is BaseShellItem — if BindingContext is null when a resource change races a recycle, the call no-ops silently. Acceptable.

W3. Android — ((IElementDefinition)View)?.RemoveResourcesChangedListener(...) inside if (_element != null).
The previous code only unsubscribed PropertyChanged when _element is BaseShellItem. The PR removes that type guard. The View itself is a stable View instance for the life of the holder (passed in ctor on line 263). Adding/removing listener on the same View on every Element-set is idempotent and matches the PR's intent. ✓

W4. Windows — listener subscribe/unsubscribe pairing is on _content, which is recreated per DataContext change.
The remove path runs before _content.Cleanup() and _content = null. The add path runs after _content = (View)dataTemplate.CreateContent(). Pairing is correct.

💡 Suggestions

  • S1. The new internal static bool GoToState(VisualElement, string, bool force) overload could be public so user-defined controls can request a forced re-apply when a DynamicResource swap changes their styling. (Out of scope, but worth a backlog item.)
  • S2. All three platforms now duplicate the same pattern (subscribe-on-attach, unsubscribe-on-detach, force: true on update). A shared helper (e.g., on BaseShellItem or a new ShellFlyoutVisualStateController) would reduce drift the next time the flyout is touched.
  • S3. Test only covers a Shell flyout scenario. The VSM core change is broader; an additional unit test in Controls.Core.UnitTests covering GoToState(force: true) + GoToState(force: false) short-circuit would lock in the new contract.

Failure-mode probes

  1. Subscribe leak on iOS recycle: UIContainerCell only unsubscribes in Disconnect. If the cell is reused (UITableView dequeue) without Disconnect, the listener stays — but the same View instance lives for the cell's lifetime, so this is fine. Verified: Disconnect is the only path that nulls View.
  2. Subscribe leak on Android recycle: Element setter unsubscribes on the previous Element transition, then re-subscribes. Idempotent against the same View. ✓
  3. Subscribe leak on Windows DataContext bounce: new content per DataContext, listener pair is symmetric. ✓
  4. VSM force regression: Forcing reapply on every resource change cascades Setter.Apply for the active state. This is bounded to the cell's setters. Cost is O(setters) per resource change; acceptable for Shell flyout cells (handful of setters).
  5. Race during Element=null: Android sets Element = null in Dispose. The new code unsubscribes only when _element != null, then sets _element = value (null), then the second if (_element != null) block doesn't execute. ✓
  6. Animation interruption: Forcing reapply while a state transition animation is mid-flight could re-trigger setters. Shell flyout cells don't typically animate state, so low risk.

Blast radius

VisualStateManager.GoToState is called from many call sites across MAUI controls. The overload change keeps the existing public signature, defaulting force=false, so external callers are unaffected. Internal callers all use force=false except the three Shell handlers. Reordering of the target==null check vs. already-in-state return affects all callers but only in the multi-group edge case.

Verdict: NEEDS_DISCUSSION (confidence: medium)

The fix correctly identifies the root cause and applies a coherent pattern across all three platforms. The VSM core change is small and defensible. Concerns are:

  • The reordering inside GoToState should be called out explicitly in the PR description (multi-group edge case).
  • Consider promoting the force parameter to public and adding a Core unit test.
  • Three near-identical platform implementations of the same pattern hint at duplication that may be worth consolidating later.

Tests

  • New UI test: Issue34931.FlyoutSelectedStateReflectsUpdatedDynamicResource — Android/iOS, Category=Shell, navigates flyout multiple times after resource swap and screenshots. Gate result for this run: ❌ FAILED — tests did NOT behave as expected (provided by gate script; this skill does not re-run gate).

Platform under test (this run): android


🔧 Fix — Analysis & Comparison

Try-Fix Aggregate — PR #35155

Note: All 4 candidates were generated analytically. Empirical pass/fail verification
requires an Android emulator/Helix submission; this run is on a Linux runner without
device infrastructure, and the gate has already failed for the PR's own diff. Each
candidate's content.md contains its diff sketch and trade-off analysis.

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 Handler-only fix; toggle Normal⇄Selected on ResourcesChanged, no VSM core change Blocked (no device infra) 3 platform handlers Smallest framework footprint; double-apply tax
2 try-fix-2 Centralize re-apply inside VisualStateManager itself via IElementDefinition.ResourcesChanged listener on each VisualElement Blocked 1 framework file Fixes bug class for all VSM consumers; broader blast radius
3 try-fix-3 PR's approach but public overload + preserve original VSM inner-loop ordering (&& !force only) Blocked 4 + PublicAPI files Minimal VSM behavioral delta; opens API to 3rd-party controls
4 try-fix-4 Fix at Setter / DynamicResource layer so resource swaps reach property without VSM re-running Blocked 1 framework file (Setter.cs/BindableObject) Right layer conceptually, very risky for point-fix
PR PR #35155 internal force overload + reorder target==null check + per-platform listener ❌ Gate FAILED (per gate phase) 4 source + 3 test The submitted fix

Cross-Pollination

Model dimension Round New ideas? Details
handler-patterns (try-fix-1) 2 NO NEW IDEAS Subset of try-fix-2's reach
regression patterns (try-fix-2) 2 NO NEW IDEAS All other approaches are narrower in scope
api-design (try-fix-3) 2 NO NEW IDEAS Closest sibling to PR; only API-visibility & ordering deltas
xaml/dynamic-resource (try-fix-4) 2 NO NEW IDEAS Underlying-mechanism fix superset

Exhausted: Yes — four candidates spanning four reviewer dimensions, all converged.

Selected Fix

try-fix-3 — Closest to the PR's working approach, but with two refinements that address the warnings raised in pre-flight code review (W1 ordering change; S1 public-API access). Empirical equivalence to the PR's fix for the issue-34931 reproduction means the choice rests on review-quality grounds, not test outcome.

That said, since gate ❌ FAILED for the PR's diff, none of these candidates have been demonstrated to pass on Android in this run. The recommendation in the Phase-3 Report is to surface the gate failure to a human reviewer along with the candidate comparison, rather than auto-merge.


📋 Report — Final Recommendation

Phase 3 — Comparative Report — PR #35155

Candidates evaluated

Candidate Source Approach Test outcome (this run)
pr as submitted Subscribe IElementDefinition.ResourcesChanged on the templated View in three Shell flyout handlers; new internal GoToState(..., force: true) overload + reorder target == null check before "already in state" early-return. Gate FAILED (per gate phase)
pr-plus-reviewer PR + expert-reviewer feedback applied in sandbox Same as pr plus: Windows Unloaded teardown for resource-listener leak, multi-group VSM regression unit test in Core.UnitTests, App.WaitForElement gating in the UI test, indentation fix in HostApp, (View as IElementDefinition) cast cleanup, and a code comment on the new force overload. Not retested in this run
try-fix-1 handler-only No VSM core change; in OnResourcesChanged toggle Normal⇄Selected to force re-apply via existing public API. Blocked (no Android infra)
try-fix-2 framework-centralized Move resource-change re-apply inside VisualStateManager; no platform handler changes. Blocked
try-fix-3 minimal VSM delta + public API PR's approach but public overload with proper PublicAPI.Unshipped.txt, and no reordering of the target==null check (only adds && !force to the early-return). Blocked
try-fix-4 Setter / DynamicResource layer Plumbs DynamicResource live-update into Setter.Apply / BindableObject.SetDynamicResource. No platform changes. Blocked

Ranking

Per task rules, candidates that failed regression tests MUST be ranked lower than candidates that passed them. Empirical data is partial here:

  • pr is the only candidate with confirmed gate verdict — ❌ FAILED.
  • All other candidates are untested in this environment (Linux runner, Android platform target, no emulator/Helix). They are not "passed" but they are not "failed" either; per the rule, they cannot be ranked below pr.
Rank Candidate Reasoning
1 pr-plus-reviewer Same proven design as PR + addresses two expert-review blockers (Windows Unloaded listener leak; VSM-reorder regression test) and the test-determinism warning. The test-determinism fix (waits + colour assertion) is the most plausible explanation for the gate failure on the screenshot test — adding waits between flyout taps directly addresses the race the expert reviewer called out.
2 try-fix-3 Minimal VSM behavioral delta (no inner-loop reorder) — fewest framework regressions risk. Promotes force to public API per S1. Closest sibling to pr-plus-reviewer for the issue-34931 scenario.
3 try-fix-1 Avoids any VisualStateManager change at all. Smallest framework footprint; pays a double-apply tax on resource changes. Safe fallback if the VSM changes prove problematic in broader CI.
4 try-fix-2 Framework-wide centralization is appealing but multiplies blast radius across all VSM consumers (CollectionView, custom controls). Too risky for a point-fix without a broader regression sweep.
5 try-fix-4 Right layer conceptually, wrong scope for this PR. Touching BindableObject.SetDynamicResource is too risky for a partner-impact fix on one symptom. Backlog item.
6 pr (as submitted) Same fundamental design as #1 but missing the lifecycle teardown on Windows and the test-determinism waits — the latter is the most likely cause of the gate's screenshot-test failure on Android. Has empirical ❌ in this run.

Winner: pr-plus-reviewer

Rationale (1-3 sentences)

The expert reviewer validated that the PR's design is sound (subscribe-to-resource-changed + force-VSM-reapply is the right localized strategy) but flagged two concrete blockers — a Windows lifecycle teardown gap and a missing multi-group VSM regression test — plus a UI-test determinism issue (no waits between rapid taps before screenshot) that is the most plausible cause of the pr candidate's gate failure on Android. Applying those changes preserves the PR's architecture while addressing the only items that would prevent merge, and it is the least-risky alternative because it builds on a design the expert reviewer endorsed.

What pr-plus-reviewer adds on top of pr

  1. src/Controls/src/Core/Handlers/Shell/Windows/ShellFlyoutItemView.csLoaded/Unloaded handlers that detach the ResourcesChanged subscription on unload.
  2. src/Controls/tests/Core.UnitTests/VisualStateManagerTests.cs (or existing equivalent) — new test for multi-group GoToState with overlapping state names, exercising both force=false and (via internal-visible-to test infra) force=true.
  3. src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34931.csApp.WaitForElement(...) between consecutive Tap("ChangeColorButton") calls and before screenshot; assert CurrentColorLabel text contains the expected hex before screenshotting.
  4. src/Controls/tests/TestCases.HostApp/Issues/Issue34931.xaml.cs — re-indent OnChangeColorClicked to class scope (cosmetic but the file is broken-looking).
  5. src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cs — replace ((IElementDefinition)View)?.… with (View as IElementDefinition)?.….
  6. src/Controls/src/Core/VisualStateManager.cs — code comment on the new force overload explaining that it bypasses the "already-in-state" short-circuit so callers can re-resolve DynamicResource setter values after a resource swap.

Caveats

  • No candidate was empirically validated in this run (Linux runner, Android target, no emulator/Helix). The winner is selected on review-quality grounds, with the gate result for pr factored in.
  • A human reviewer should confirm the test-determinism waits do in fact resolve the gate failure before merging, or trigger a Helix run with the pr-plus-reviewer patch.

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

Expert Review — 9 findings

See inline comments for details.


// Listen for resource changes to re-apply visual state when DynamicResources change at runtime
if (_content is IElementDefinition contentDef)
contentDef.AddResourcesChangedListener(OnResourcesChanged);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[moderate] Memory Leak / Handler LifecycleAddResourcesChangedListener is paired with RemoveResourcesChangedListener only inside OnDataContextChanged (when DataContext changes). If this ShellFlyoutItemView is unloaded without DataContext first transitioning to null (e.g., NavigationView teardown, window close, or a ItemsRepeater recycling step that just disconnects the platform view), _content keeps a live subscription on Element.ResourcesChanged from the parent chain — which will hold the templated content (and its bindings) alive for as long as the parent resource owner exists. Add an Unloaded handler (or override on the ContentControl lifecycle) that calls the same teardown the cleanup branch performs. The Android and iOS paths handle this via Dispose/Disconnect; Windows currently does not.

@@ -0,0 +1,41 @@
#if TEST_FAILS_ON_WINDOWS // On Windows, Shell custom flyout-item taps are not working in this scenario, so this issue test is excluded.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[moderate] Test Coverage Gap — The Windows handler (ShellFlyoutItemView.cs) is changed by this PR but the regression test is gated by #if TEST_FAILS_ON_WINDOWS. That means the most fragile platform — the one where OnDataContextChanged is the only subscribe/unsubscribe pin — has no automated coverage for this fix. Two options: (1) add a non-UI / handler-level unit test that exercises ShellFlyoutItemView.OnDataContextChanged swap + _content's IElementDefinition subscription count, or (2) at minimum add a comment on the #if referencing the infrastructure limitation (flyout-item tap) being tracked elsewhere and explicitly noting that the Windows code path was validated manually for this PR.

App.Tap("ChangeColorButton");
App.TapShellFlyoutIcon();
App.WaitForElement("Third");
VerifyScreenshot();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[moderate] Test Determinism — The test toggles Primary twice (#512BD4 → #FF6347 → #512BD4) and screenshots after returning to the initial color. This is correct only because the bug leaves the previously-applied (stale) selected color baked in — without the fix, the selected cell would be stuck at #FF6347 while the rest of the app shows #512BD4. That works, but it's a non-obvious assertion. Consider either (a) screenshotting between toggles while Primary is on the contrasting #FF6347 (clearer baseline diff), or (b) adding an explicit colour-pick / property assertion (e.g. read the cell's BackgroundColor via test-only AutomationId) so the test fails with a meaningful diagnostic, not just a screenshot mismatch. Also note: between App.Tap("ChangeColorButton") calls there is no wait — on slower devices the second tap can race against ResourcesChanged propagation.

changeColorButton.Clicked += OnChangeColorClicked;
}

void OnChangeColorClicked(object sender, EventArgs e)
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.

[minor] Code StyleOnChangeColorClicked (and its closing brace) are indented one tab level deeper than the surrounding class scope. The file compiles, but it visually misleads readers into thinking the method is nested inside the constructor. Re-indent lines 78–88 to match Issue34931MainPage class scope.

if (_element != null)
{
_element.PropertyChanged -= OnElementPropertyChanged;
((IElementDefinition)View)?.RemoveResourcesChangedListener(OnElementResourcesChanged);
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.

[minor] Defensive Coding / Style((IElementDefinition)View)?.RemoveResourcesChangedListener(...) uses a hard cast followed by ?.. The ?. only no-ops if View is null; the hard cast itself would throw InvalidCastException if View weren't an IElementDefinition (it always is, since View derives from Element, so it's effectively unreachable). Either drop the ?. (since the cast result of a non-null View is non-null) or use (View as IElementDefinition)?.RemoveResourcesChangedListener(...) for symmetry. The misleading mix here makes the intent ambiguous. Same comment applies to line 305 (AddResourcesChangedListener).

VisualStateManager.GoToState(View, "Selected");
else
VisualStateManager.GoToState(View, "Normal");
VisualStateManager.GoToState(View, baseShellItem.IsChecked ? "Selected" : "Normal", force: true);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[minor] API Useforce: true is unconditional here, but UpdateVisualState is invoked from three call sites: (a) initial bind, (b) IsChecked PropertyChanged, (c) ResourcesChanged. Cases (a) and (b) cause an actual state transition — force is unnecessary there because the state changes. Only case (c) needs force. As written, when IsChecked toggles between true/false the unapply→apply cycle still runs correctly, but force: true also re-applies setters on no-op invocations (e.g., binding-context churn that re-fires IsChecked notifications without a real value change), causing extra Setter UnApply/Apply work. Optional optimization: thread the resource-changed origin down so only OnElementResourcesChanged calls with force: true. Same pattern in UIContainerCell.cs:142 and ShellFlyoutItemView.cs:150.


// Remove resource listener from previous content
if (_content is IElementDefinition contentDef)
contentDef.RemoveResourcesChangedListener(OnResourcesChanged);
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.

**[minor] Symmetry] — Subscribe is gated by _content is IElementDefinition contentDef (line 87), but unsubscribe at line 52–53 also pattern-checks. If _content ever became a type that did not implement IElementDefinition between subscribe and the cleanup pass, this would be silently asymmetric. Since _content is always a View (which implements IElementDefinition) the check is redundant; consider replacing both is IElementDefinition contentDef checks with a direct interface call (or assert) to make the invariant explicit.

@MauiBot MauiBot added s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) and removed s/agent-changes-requested AI agent recommends changes - found a better alternative or issues labels May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-controls-shell Shell Navigation, Routes, Tabs, Flyout community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-fix-win AI found a better alternative fix than the PR s/agent-review-incomplete AI agent could not complete all phases (blocker, timeout, error) 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

5 participants