From c2b226d22149ab1b7fc1001a81361c53bb51405e Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:48:45 +0530 Subject: [PATCH 1/9] fix added --- .../ContentView/ContentViewHandler.Windows.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index e62ecf46af3a..dc1ff9845889 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -21,13 +21,19 @@ static void UpdateContent(IContentViewHandler handler) _ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class."); _ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); - handler.PlatformView.CachedChildren.Clear(); - if (handler.VirtualView.PresentedContent is IView view) { - // Detach the old handler if it exists (prevents WinUI COM exception on reuse) - view.Handler?.DisconnectHandler(); - handler.PlatformView.CachedChildren.Add(view.ToPlatform(handler.MauiContext)); + var platformView = view.ToPlatform(handler.MauiContext); + + // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview + if (platformView is FrameworkElement fwElement && + fwElement.Parent is MauiPanel existingParent) + { + existingParent.CachedChildren.Remove(fwElement); + } + + handler.PlatformView.CachedChildren.Clear(); + handler.PlatformView.CachedChildren.Add(platformView); } } From 832f997f18d57032fce8acddbb872db0f2701154 Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Fri, 3 Jul 2026 18:58:01 +0530 Subject: [PATCH 2/9] test added --- .../TestCases.HostApp/Issues/Issue36298.cs | 69 +++++++++++++++++++ .../Tests/Issues/Issue36298.cs | 33 +++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue36298.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue36298.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue36298.cs new file mode 100644 index 000000000000..136970189669 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue36298.cs @@ -0,0 +1,69 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 36298, "[Windows] ContentPresenter throws ArgumentException when dynamically switching RefreshView or ScrollView content.", PlatformAffected.UWP)] +public class Issue36298 : ContentPage +{ + ContentView _contentHolder; + View _view1; + View _view2; + + public Issue36298() + { + _view1 = new ContentView + { + Content = new RefreshView + { + Content = new Label { Text = "View 1 - RefreshView" } + } + }; + + _view2 = new ContentView + { + Content = new ScrollView + { + Content = new Label { Text = "View 2 - ScrollView" } + } + }; + + _contentHolder = new ContentView + { + Content = _view1 + }; + + var switchToView2Button = new Button + { + Text = "Switch to View 2", + AutomationId = "SwitchToView2" + }; + switchToView2Button.Clicked += (_, _) => _contentHolder.Content = _view2; + + var switchToView1Button = new Button + { + Text = "Switch to View 1", + AutomationId = "SwitchToView1" + }; + switchToView1Button.Clicked += (_, _) => _contentHolder.Content = _view1; + + var successLabel = new Label + { + Text = "Waiting", + AutomationId = "SuccessLabel" + }; + + switchToView2Button.Clicked += (_, _) => successLabel.Text = "View2"; + switchToView1Button.Clicked += (_, _) => successLabel.Text = "Success"; + + Content = new VerticalStackLayout + { + Spacing = 10, + Padding = new Thickness(20), + Children = + { + switchToView2Button, + switchToView1Button, + _contentHolder, + successLabel + } + }; + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs new file mode 100644 index 000000000000..f62db7171e55 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue36298 : _IssuesUITest +{ + public Issue36298(TestDevice device) : base(device) { } + + public override string Issue => "[Windows] ContentPresenter throws ArgumentException when dynamically switching RefreshView or ScrollView content."; + + [Test] + [Category(UITestCategories.Layout)] + public void SwitchingContentPresenterContentShouldNotCrash() + { + // Wait for the page to load showing View 1 + App.WaitForElement("SwitchToView2"); + + // Switch to View 2 + App.Tap("SwitchToView2"); + App.WaitForElement("SwitchToView1"); + + // Switch back to View 1 — this triggered the ArgumentException before the fix + App.Tap("SwitchToView1"); + + // Label is updated to "Success" only if the switch completed without crashing. + // WaitForElement("SuccessLabel") alone is insufficient because the label + // is present from page load; we must verify the text was actually updated. + App.WaitForElement("SuccessLabel"); + Assert.That(App.FindElement("SuccessLabel").GetText(), Is.EqualTo("Success")); + } +} From 49d188cc1c95ffc3e1f12ce70878b061fe48f5c7 Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Fri, 3 Jul 2026 19:14:55 +0530 Subject: [PATCH 3/9] Update ContentViewHandler.Windows.cs --- src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index dc1ff9845889..554526d69746 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Maui.Graphics; +using Microsoft.UI.Xaml; namespace Microsoft.Maui.Handlers { From fad4715a4fb95790454c5b59037476d667f84e8a Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:07:35 +0530 Subject: [PATCH 4/9] Fix added for BorderHandler --- .../src/Handlers/Border/BorderHandler.Windows.cs | 14 +++++++++++--- .../ContentView/ContentViewHandler.Windows.cs | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs index d9a32ce0caa0..49a160bd0aee 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.UI.Xaml; namespace Microsoft.Maui.Handlers { @@ -25,9 +26,16 @@ static partial void UpdateContent(IBorderHandler handler) if (handler.VirtualView.PresentedContent is IView view) { - // Detach the old handler if it exists (prevents WinUI COM exception on reuse) - view.Handler?.DisconnectHandler(); - handler.PlatformView.Content = view.ToPlatform(handler.MauiContext); + var platformView = view.ToPlatform(handler.MauiContext); + + // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview + if (platformView is FrameworkElement fwElement && + fwElement.Parent is MauiPanel existingParent) + { + existingParent.CachedChildren.Remove(fwElement); + } + + handler.PlatformView.Content = platformView; } } diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index 554526d69746..9521873d986f 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -22,6 +22,8 @@ static void UpdateContent(IContentViewHandler handler) _ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class."); _ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); + handler.PlatformView.CachedChildren.Clear(); + if (handler.VirtualView.PresentedContent is IView view) { var platformView = view.ToPlatform(handler.MauiContext); @@ -33,7 +35,6 @@ static void UpdateContent(IContentViewHandler handler) existingParent.CachedChildren.Remove(fwElement); } - handler.PlatformView.CachedChildren.Clear(); handler.PlatformView.CachedChildren.Add(platformView); } } From 0d426871cb7f14071a30af4d5c42c8462ac3a5af Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:08:55 +0530 Subject: [PATCH 5/9] Update ContentViewHandler.Windows.cs --- src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index 9521873d986f..051f25ee4b26 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -23,7 +23,7 @@ static void UpdateContent(IContentViewHandler handler) _ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); handler.PlatformView.CachedChildren.Clear(); - + if (handler.VirtualView.PresentedContent is IView view) { var platformView = view.ToPlatform(handler.MauiContext); From 97a2789e5ddb89b627c903a5e0ca10addbe40c86 Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Mon, 6 Jul 2026 17:26:14 +0530 Subject: [PATCH 6/9] review concerns addressed --- src/Core/src/Handlers/Border/BorderHandler.Windows.cs | 5 ++++- .../Handlers/ContentView/ContentViewHandler.Windows.cs | 8 +++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs index 49a160bd0aee..c94bb828692b 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs @@ -21,7 +21,6 @@ static partial void UpdateContent(IBorderHandler handler) _ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class."); _ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); - handler.PlatformView.CachedChildren.Clear(); handler.PlatformView.EnsureBorderPath(); if (handler.VirtualView.PresentedContent is IView view) @@ -37,6 +36,10 @@ static partial void UpdateContent(IBorderHandler handler) handler.PlatformView.Content = platformView; } + else + { + handler.PlatformView.CachedChildren.Clear(); + } } diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index 051f25ee4b26..5abd44646dee 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -22,8 +22,6 @@ static void UpdateContent(IContentViewHandler handler) _ = handler.VirtualView ?? throw new InvalidOperationException($"{nameof(VirtualView)} should have been set by base class."); _ = handler.MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set by base class."); - handler.PlatformView.CachedChildren.Clear(); - if (handler.VirtualView.PresentedContent is IView view) { var platformView = view.ToPlatform(handler.MauiContext); @@ -35,7 +33,11 @@ static void UpdateContent(IContentViewHandler handler) existingParent.CachedChildren.Remove(fwElement); } - handler.PlatformView.CachedChildren.Add(platformView); + handler.PlatformView.Content = platformView; + } + else + { + handler.PlatformView.CachedChildren.Clear(); } } From d31044350b0670fe8fead42e8ef5a989e7322fb3 Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Mon, 6 Jul 2026 18:52:00 +0530 Subject: [PATCH 7/9] Changed clear to null logic --- src/Core/src/Handlers/Border/BorderHandler.Windows.cs | 2 +- src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs index c94bb828692b..c7074991d81c 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs @@ -38,7 +38,7 @@ static partial void UpdateContent(IBorderHandler handler) } else { - handler.PlatformView.CachedChildren.Clear(); + handler.PlatformView.Content = null; } } diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index 5abd44646dee..63702743a185 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -37,7 +37,7 @@ static void UpdateContent(IContentViewHandler handler) } else { - handler.PlatformView.CachedChildren.Clear(); + handler.PlatformView.Content = null; } } From 7d8dc701d8eb1bf8352ca312f98eb2e5cdbf49e1 Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:10:16 +0530 Subject: [PATCH 8/9] AI summary addressed --- .../Tests/Issues/Issue36298.cs | 3 +-- .../src/Handlers/Border/BorderHandler.Windows.cs | 16 ++++++++++++---- .../ContentView/ContentViewHandler.Windows.cs | 16 ++++++++++++---- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs index f62db7171e55..8c836ece04d0 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs @@ -27,7 +27,6 @@ public void SwitchingContentPresenterContentShouldNotCrash() // Label is updated to "Success" only if the switch completed without crashing. // WaitForElement("SuccessLabel") alone is insufficient because the label // is present from page load; we must verify the text was actually updated. - App.WaitForElement("SuccessLabel"); - Assert.That(App.FindElement("SuccessLabel").GetText(), Is.EqualTo("Success")); + Assert.That(App.WaitForTextToBePresentInElement("SuccessLabel", "Success"), Is.True); } } diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs index c7074991d81c..c3c3c3c5067b 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs @@ -27,11 +27,19 @@ static partial void UpdateContent(IBorderHandler handler) { var platformView = view.ToPlatform(handler.MauiContext); - // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview - if (platformView is FrameworkElement fwElement && - fwElement.Parent is MauiPanel existingParent) + // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview. + // When the parent is a ContentPanel, use its Content setter so the internal _content + // field is cleared consistently (clip/border logic relies on ContentPanel._content). + if (platformView is FrameworkElement fwElement && fwElement.Parent is not null) { - existingParent.CachedChildren.Remove(fwElement); + if (fwElement.Parent is ContentPanel existingContentPanel) + { + existingContentPanel.Content = null; + } + else if (fwElement.Parent is MauiPanel existingPanel) + { + existingPanel.CachedChildren.Remove(fwElement); + } } handler.PlatformView.Content = platformView; diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index 63702743a185..25075d945f12 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -26,11 +26,19 @@ static void UpdateContent(IContentViewHandler handler) { var platformView = view.ToPlatform(handler.MauiContext); - // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview - if (platformView is FrameworkElement fwElement && - fwElement.Parent is MauiPanel existingParent) + // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview. + // When the parent is a ContentPanel, use its Content setter so the internal _content + // field is cleared consistently (clip/border logic relies on ContentPanel._content). + if (platformView is FrameworkElement fwElement && fwElement.Parent is not null) { - existingParent.CachedChildren.Remove(fwElement); + if (fwElement.Parent is ContentPanel existingContentPanel) + { + existingContentPanel.Content = null; + } + else if (fwElement.Parent is MauiPanel existingPanel) + { + existingPanel.CachedChildren.Remove(fwElement); + } } handler.PlatformView.Content = platformView; From 6aa094eec8163fa3c1bcbf92041366da275b643e Mon Sep 17 00:00:00 2001 From: devanathan-vaithiyanathan <114395405+devanathan-vaithiyanathan@users.noreply.github.com> Date: Tue, 14 Jul 2026 18:44:30 +0530 Subject: [PATCH 9/9] AI summary addressed --- .../Tests/Issues/Issue36298.cs | 2 ++ .../src/Handlers/Border/BorderHandler.Windows.cs | 12 +++++++++--- .../ContentView/ContentViewHandler.Windows.cs | 12 +++++++++--- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs index 8c836ece04d0..c19e9a2a7bca 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36298.cs @@ -23,6 +23,8 @@ public void SwitchingContentPresenterContentShouldNotCrash() // Switch back to View 1 — this triggered the ArgumentException before the fix App.Tap("SwitchToView1"); + App.Tap("SwitchToView2"); + App.Tap("SwitchToView1"); // Label is updated to "Success" only if the switch completed without crashing. // WaitForElement("SuccessLabel") alone is insufficient because the label diff --git a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs index c3c3c3c5067b..0feb80de3e8f 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Windows.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Windows.cs @@ -28,13 +28,19 @@ static partial void UpdateContent(IBorderHandler handler) var platformView = view.ToPlatform(handler.MauiContext); // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview. - // When the parent is a ContentPanel, use its Content setter so the internal _content - // field is cleared consistently (clip/border logic relies on ContentPanel._content). + // Always remove via CachedChildren directly: Content = null is a no-op when _content + // is null (e.g. ScrollViewHandler adds via paddingShim.CachedChildren.Add, not the + // Content setter), leaving the element with a live parent and causing a COM exception + // when we try to reparent it. Only clear _content when it actually tracks fwElement. if (platformView is FrameworkElement fwElement && fwElement.Parent is not null) { if (fwElement.Parent is ContentPanel existingContentPanel) { - existingContentPanel.Content = null; + existingContentPanel.CachedChildren.Remove(fwElement); + if (existingContentPanel.Content == fwElement) + { + existingContentPanel.Content = null; + } } else if (fwElement.Parent is MauiPanel existingPanel) { diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs index 25075d945f12..73e05ae50823 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Windows.cs @@ -27,13 +27,19 @@ static void UpdateContent(IContentViewHandler handler) var platformView = view.ToPlatform(handler.MauiContext); // Detach from existing parent — mirrors Android RemoveFromParent / iOS RemoveFromSuperview. - // When the parent is a ContentPanel, use its Content setter so the internal _content - // field is cleared consistently (clip/border logic relies on ContentPanel._content). + // Always remove via CachedChildren directly: Content = null is a no-op when _content + // is null (e.g. ScrollViewHandler adds via paddingShim.CachedChildren.Add, not the + // Content setter), leaving the element with a live parent and causing a COM exception + // when we try to reparent it. Only clear _content when it actually tracks fwElement. if (platformView is FrameworkElement fwElement && fwElement.Parent is not null) { if (fwElement.Parent is ContentPanel existingContentPanel) { - existingContentPanel.Content = null; + existingContentPanel.CachedChildren.Remove(fwElement); + if (existingContentPanel.Content == fwElement) + { + existingContentPanel.Content = null; + } } else if (fwElement.Parent is MauiPanel existingPanel) {