From 5f812be7dc25d72cdb39329fc0ba5b7cf1568517 Mon Sep 17 00:00:00 2001 From: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com> Date: Wed, 6 May 2026 11:21:16 +0530 Subject: [PATCH 1/2] Fixed-35277 : COMException when clone a page's content to a object and set it back later in mainthread on Windows --- .../TestCases.HostApp/Issues/Issue35277.cs | 56 +++++++++++++++++++ .../Tests/Issues/Issue35277.cs | 22 ++++++++ .../ScrollView/ScrollViewHandler.Windows.cs | 17 +++--- 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue35277.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35277.cs diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue35277.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue35277.cs new file mode 100644 index 000000000000..f29b81fc69df --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue35277.cs @@ -0,0 +1,56 @@ +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 35277, "COMException when restoring a page content after swapping it out", PlatformAffected.UWP)] +public class Issue35277 : ContentPage +{ + ScrollView _originalScrollView; + + public Issue35277() + { + var swapButton = new Button + { + Text = "Swap and Restore", + HorizontalOptions = LayoutOptions.Center, + AutomationId = "SwapAndRestoreButton" + }; + swapButton.Clicked += OnSwapAndRestoreClicked; + + _originalScrollView = new ScrollView + { + Content = new VerticalStackLayout + { + Spacing = 20, + VerticalOptions = LayoutOptions.Center, + Children = + { + swapButton, + new Label + { + Text = "Original Content", + HorizontalOptions = LayoutOptions.Center, + AutomationId = "OriginalLabel" + } + } + } + }; + + Content = _originalScrollView; + } + + void OnSwapAndRestoreClicked(object sender, EventArgs e) + { + var savedScrollView = _originalScrollView; + Content = new Label + { + Text = "Temporary Content", + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.Center, + AutomationId = "TemporaryLabel" + }; + + Microsoft.Maui.ApplicationModel.MainThread.BeginInvokeOnMainThread(() => + { + Content = savedScrollView; + }); + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35277.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35277.cs new file mode 100644 index 000000000000..4b9ab81c4b30 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue35277.cs @@ -0,0 +1,22 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue35277 : _IssuesUITest +{ + public Issue35277(TestDevice device) : base(device) { } + + public override string Issue => "COMException when restoring a page content after swapping it out"; + + [Test] + [Category(UITestCategories.ScrollView)] + public void ScrollViewContentShouldRestoreWithoutCOMException() + { + App.WaitForElement("SwapAndRestoreButton"); + App.Tap("SwapAndRestoreButton"); + // If no COMException is thrown, the original ScrollView content (with the button) restores successfully + App.WaitForElement("SwapAndRestoreButton"); + } +} diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs index 61d173bf8e9e..42df4232f18f 100644 --- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs @@ -149,20 +149,23 @@ static void UpdateContentPanel(IScrollView scrollView, IScrollViewHandler handle { currentPaddingLayer.CachedChildren.Clear(); } - + return; } + if (currentPaddingLayer is not null) + { + currentPaddingLayer.CachedChildren.Clear(); + } + + // Detach the old handler if it exists (prevents WinUI COM exception on reuse) + scrollView.PresentedContent.Handler?.DisconnectHandler(); + var nativeContent = scrollView.PresentedContent.ToPlatform(handler.MauiContext); if (currentPaddingLayer is not null) { - // Only update if content has changed or is missing - if (currentPaddingLayer.CachedChildren.Count == 0 || currentPaddingLayer.CachedChildren[0] != nativeContent) - { - currentPaddingLayer.CachedChildren.Clear(); - currentPaddingLayer.CachedChildren.Add(nativeContent); - } + currentPaddingLayer.CachedChildren.Add(nativeContent); } else { From 042150cd572bf7dc27759da5fd6ef4cf9f9b1184 Mon Sep 17 00:00:00 2001 From: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com> Date: Thu, 14 May 2026 16:25:31 +0530 Subject: [PATCH 2/2] Resolved deice test failures. --- .../src/Handlers/ScrollView/ScrollViewHandler.Windows.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs index 42df4232f18f..40cb099774c4 100644 --- a/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs +++ b/src/Core/src/Handlers/ScrollView/ScrollViewHandler.Windows.cs @@ -38,6 +38,12 @@ protected override void ConnectHandler(ScrollViewer platformView) protected override void DisconnectHandler(ScrollViewer platformView) { + // Cascade disconnect to the PresentedContent's handler. Without this, when the + // ScrollView is restored as ContentPage.Content after being swapped out, the + // PresentedContent's native view still has a stale parent (the old ContentPanel), + // causing a WinUI COM exception "Element already has a parent" (Issue #35277). + // Cascading here ensures ToPlatform() creates a fresh native view with no parent. + VirtualView?.PresentedContent?.Handler?.DisconnectHandler(); base.DisconnectHandler(platformView); platformView.ViewChanged -= ViewChanged; } @@ -158,9 +164,6 @@ static void UpdateContentPanel(IScrollView scrollView, IScrollViewHandler handle currentPaddingLayer.CachedChildren.Clear(); } - // Detach the old handler if it exists (prevents WinUI COM exception on reuse) - scrollView.PresentedContent.Handler?.DisconnectHandler(); - var nativeContent = scrollView.PresentedContent.ToPlatform(handler.MauiContext); if (currentPaddingLayer is not null)