From 0b5126ea9cda4fc758c62c6e1c4ffcab01bfb664 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Fri, 21 May 2021 16:58:55 -0600 Subject: [PATCH 1/3] Remove views from native layout when disconnecting the handler --- .../Core/src/Android/Cells/ViewCellRenderer.cs | 12 +++++++++++- .../Handlers/Layout/LayoutHandler.Android.cs | 11 ++++++++--- .../Handlers/Layout/LayoutHandlerTests.cs | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Compatibility/Core/src/Android/Cells/ViewCellRenderer.cs b/src/Compatibility/Core/src/Android/Cells/ViewCellRenderer.cs index 08ab7ee206ac..25de14e2e26c 100644 --- a/src/Compatibility/Core/src/Android/Cells/ViewCellRenderer.cs +++ b/src/Compatibility/Core/src/Android/Cells/ViewCellRenderer.cs @@ -217,7 +217,17 @@ public void Update(ViewCell cell) RemoveView(_view.View); AppCompat.Platform.SetRenderer(_viewCell.View, null); _viewCell.View.IsPlatformEnabled = false; - _view.View.Dispose(); + + // Adding a special case for HandlerToRendererShim so that DisconnectHandler gets called; + // Pending https://github.com/xamarin/Xamarin.Forms/pull/14288 being merged, we won't need the special case + if (_view is HandlerToRendererShim htrs) + { + htrs.Dispose(); + } + else + { + _view.View.Dispose(); + } _viewCell = cell; _view = AppCompat.Platform.CreateRenderer(_viewCell.View, Context); diff --git a/src/Core/src/Handlers/Layout/LayoutHandler.Android.cs b/src/Core/src/Handlers/Layout/LayoutHandler.Android.cs index 8c009dd2ef44..d215c7047c3a 100644 --- a/src/Core/src/Handlers/Layout/LayoutHandler.Android.cs +++ b/src/Core/src/Handlers/Layout/LayoutHandler.Android.cs @@ -32,11 +32,9 @@ public override void SetVirtualView(IView view) NativeView.CrossPlatformMeasure = VirtualView.Measure; NativeView.CrossPlatformArrange = VirtualView.Arrange; - this.NativeView.RemoveAllViews(); + NativeView.RemoveAllViews(); foreach (var child in VirtualView.Children) { - //var wrap = ViewGroup.LayoutParams.WrapContent; - //NativeView.AddView(child.ToNative(MauiContext), new ViewGroup.LayoutParams(wrap, wrap)); NativeView.AddView(child.ToNative(MauiContext)); } } @@ -60,5 +58,12 @@ public void Remove(IView child) NativeView.RemoveView(view); } } + + protected override void DisconnectHandler(LayoutViewGroup nativeView) + { + // If we're being disconnected from the xplat element, then we should no longer be managing its chidren + NativeView?.RemoveAllViews(); + base.DisconnectHandler(nativeView); + } } } diff --git a/src/Core/tests/DeviceTests/Handlers/Layout/LayoutHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Layout/LayoutHandlerTests.cs index 5ef3894003a9..511c3e8ef4a6 100644 --- a/src/Core/tests/DeviceTests/Handlers/Layout/LayoutHandlerTests.cs +++ b/src/Core/tests/DeviceTests/Handlers/Layout/LayoutHandlerTests.cs @@ -44,5 +44,23 @@ public async Task HandlerRemovesChildFromNativeLayout() Assert.Equal(0, count); } + + [Fact(DisplayName = "DisconnectHandler removes child from native layout")] + public async Task DisconnectHandlerRemovesChildFromNativeLayout() + { + var layout = new LayoutStub(); + var slider = new SliderStub(); + layout.Add(slider); + + var handler = await CreateHandlerAsync(layout); + + var count = await InvokeOnMainThreadAsync(() => + { + layout.Handler.DisconnectHandler(); + return GetNativeChildCount(handler); + }); + + Assert.Equal(0, count); + } } } \ No newline at end of file From 496025c12a8d66eefa867d538014ebe3c2abb118 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Fri, 21 May 2021 17:02:06 -0600 Subject: [PATCH 2/3] iOS implementation --- src/Core/src/Handlers/Layout/LayoutHandler.iOS.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Core/src/Handlers/Layout/LayoutHandler.iOS.cs b/src/Core/src/Handlers/Layout/LayoutHandler.iOS.cs index 882b7f1d8acf..cc1f90a6e085 100644 --- a/src/Core/src/Handlers/Layout/LayoutHandler.iOS.cs +++ b/src/Core/src/Handlers/Layout/LayoutHandler.iOS.cs @@ -65,5 +65,16 @@ public void Remove(IView child) NativeView.SetNeedsLayout(); } } + + protected override void DisconnectHandler(LayoutView nativeView) + { + base.DisconnectHandler(nativeView); + var subViews = nativeView.Subviews; + + foreach (var subView in subViews) + { + subView.RemoveFromSuperview(); + } + } } } From 0d72bc2a5895523b3896700e6305fc1d1d45da53 Mon Sep 17 00:00:00 2001 From: "E.Z. Hart" Date: Fri, 21 May 2021 17:13:46 -0600 Subject: [PATCH 3/3] Windows implementation --- src/Core/src/Handlers/Layout/LayoutHandler.Windows.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Core/src/Handlers/Layout/LayoutHandler.Windows.cs b/src/Core/src/Handlers/Layout/LayoutHandler.Windows.cs index bccfde4616b4..873d38fbda51 100644 --- a/src/Core/src/Handlers/Layout/LayoutHandler.Windows.cs +++ b/src/Core/src/Handlers/Layout/LayoutHandler.Windows.cs @@ -58,5 +58,12 @@ protected override LayoutPanel CreateNativeView() return view; } + + protected override void DisconnectHandler(LayoutPanel nativeView) + { + // If we're being disconnected from the xplat element, then we should no longer be managing its chidren + NativeView?.Children.Clear(); + base.DisconnectHandler(nativeView); + } } }