diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue11812.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue11812.cs new file mode 100644 index 000000000000..81a34923d5e4 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue11812.cs @@ -0,0 +1,65 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 11812, "Setting Content of ContentView through style would crash on parent change", PlatformAffected.Android)] +public class Issue11812 : ContentPage +{ + ContentView _mainContentView; + public Issue11812() + { + var button = new Button + { + Text = "Add ContentView", + HorizontalOptions = LayoutOptions.Center, + AutomationId = "ChangeInnerContent" + }; + button.Clicked += OnButtonClicked; + + _mainContentView = new ContentView(); + _mainContentView.Content = new Issue11812InnerContentView(); + + var layout = new VerticalStackLayout + { + Spacing = 25, + Padding = new Thickness(30, 0), + VerticalOptions = LayoutOptions.Center, + Children = + { + button, + _mainContentView + } + }; + + Content = layout; + } + private void OnButtonClicked(object sender, EventArgs e) + { + _mainContentView.Content = new Issue11812InnerContentView() { BackgroundColor = Colors.Red }; + } +} + +public class Issue11812InnerContentView : ContentView +{ + public Issue11812InnerContentView() + { + Application.Current.Resources ??= new ResourceDictionary(); + + if (!Application.Current.Resources.ContainsKey("SubContentStyle")) + { + Application.Current.Resources["SubContentStyle"] = new Style(typeof(ContentView)) + { + Setters = + { + new Setter + { + Property = ContentView.ContentProperty, + Value = new Label { Text = "SubContent" } + } + } + }; + } + Style = (Style)Application.Current.Resources["SubContentStyle"]; + } +} \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11812.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11812.cs new file mode 100644 index 000000000000..94d49e188280 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue11812.cs @@ -0,0 +1,23 @@ +#if TEST_FAILS_ON_WINDOWS // test fails on windows , see https://github.com/dotnet/maui/issues/29930 +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue11812 : _IssuesUITest +{ + public Issue11812(TestDevice device) : base(device) { } + + public override string Issue => "Setting Content of ContentView through style would crash on parent change"; + + [Test] + [Category(UITestCategories.Border)] + public void InnerContentViewShouldNotCrashWhenDynamicallyChange() + { + App.WaitForElement("ChangeInnerContent"); + App.Tap("ChangeInnerContent"); + App.WaitForElement("ChangeInnerContent"); + } +} +#endif \ No newline at end of file diff --git a/src/Core/src/Handlers/Border/BorderHandler.Android.cs b/src/Core/src/Handlers/Border/BorderHandler.Android.cs index f46cae72dff7..a770576fceff 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.Android.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.Android.cs @@ -42,7 +42,12 @@ static partial void UpdateContent(IBorderHandler handler) handler.PlatformView.RemoveAllViews(); if (handler.VirtualView.PresentedContent is IView view) - handler.PlatformView.AddView(view.ToPlatform(handler.MauiContext)); + { + var platformView = view.ToPlatform(handler.MauiContext); + // Ensure the view is detached from any existing parent before adding it + platformView.RemoveFromParent(); + handler.PlatformView.AddView(platformView); + } } public static partial void MapHeight(IBorderHandler handler, IBorderView border) diff --git a/src/Core/src/Handlers/Border/BorderHandler.iOS.cs b/src/Core/src/Handlers/Border/BorderHandler.iOS.cs index 2d86123db589..8324f96f6de5 100644 --- a/src/Core/src/Handlers/Border/BorderHandler.iOS.cs +++ b/src/Core/src/Handlers/Border/BorderHandler.iOS.cs @@ -48,6 +48,7 @@ static partial void UpdateContent(IBorderHandler handler) if (handler.VirtualView.PresentedContent is IView content) { var platformContent = content.ToPlatform(handler.MauiContext); + platformContent.RemoveFromSuperview(); // If the content is a UIScrollView, we need a container to handle masks and clip shapes effectively. if (platformContent is UIScrollView) diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs index 88cacde6a1b1..a02de4ca3287 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.Android.cs @@ -39,7 +39,12 @@ static void UpdateContent(IContentViewHandler handler) handler.PlatformView.RemoveAllViews(); if (handler.VirtualView.PresentedContent is IView view) - handler.PlatformView.AddView(view.ToPlatform(handler.MauiContext)); + { + var platformView = view.ToPlatform(handler.MauiContext); + // Ensure the view is detached from any existing parent before adding it + platformView.RemoveFromParent(); + handler.PlatformView.AddView(platformView); + } } public static partial void MapContent(IContentViewHandler handler, IContentView page) diff --git a/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs b/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs index 1f2ea84c600f..5b91c4e52cb6 100644 --- a/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs +++ b/src/Core/src/Handlers/ContentView/ContentViewHandler.iOS.cs @@ -38,6 +38,7 @@ static void UpdateContent(IContentViewHandler handler) if (handler.VirtualView.PresentedContent is IView view) { var platformView = view.ToPlatform(handler.MauiContext); + platformView.RemoveFromSuperview(); handler.PlatformView.AddSubview(platformView); if (view.FlowDirection == FlowDirection.MatchParent)