From f349108d25cf5d5e2633170419a3e80ff3c1f359 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 25 Apr 2024 17:00:54 -0500 Subject: [PATCH 1/8] [ios/catalyst] fix memory leaks in `*Cell` Context: https://github.com/AdamEssenmacher/MemoryToolkit.Maui/tree/main/samples Retesting this sample with the `ListView` leak fixed, I noticed the sample reports 0 leaks! Unfortunately, it still displayed memory growing over time... Taking a `.gcdump` snapshot, I noticed a cycle: * `ViewCellRenderer.ViewTableCell` -> `ViewCell _viewCell` -> `ViewCellRenderer` -> `ViewTableCell` I was able to write a test that reproduces the leak, and I extended it for every type of `*Cell` like `TextCell`, `ImageCell`, `SwitchCell`, etc. The fixes are: * `ViewTableCell` now uses a `WeakReference _viewCell` * `CellTableViewCell` now uses a `WeakReference _cell` * `CellTableViewCell` now uses a `WeakEventManager` for `PropertyChanged`, as the `*Renderer` subscribes to this event. Note that I changed `PropertyChanged` to an event, which is an API change. (I can revisit this if needed) --- .../ListView/iOS/CellTableViewCell.cs | 48 ++++++++++------- .../Handlers/ListView/iOS/TextCellRenderer.cs | 2 +- .../Handlers/ListView/iOS/ViewCellRenderer.cs | 53 ++++++++++--------- .../PublicAPI/net-ios/PublicAPI.Shipped.txt | 2 +- .../net-maccatalyst/PublicAPI.Shipped.txt | 2 +- .../tests/DeviceTests/Memory/MemoryTests.cs | 47 ++++++++++++++++ 6 files changed, 108 insertions(+), 46 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs index 3ec0d531bc93..5e8d8877d916 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs @@ -9,9 +9,14 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility { public class CellTableViewCell : UITableViewCell, INativeElementView { - Cell _cell; + WeakReference _cell; + readonly WeakEventManager _weakEventManager = new(); - public Action PropertyChanged; + public event Action PropertyChanged + { + add => _weakEventManager.AddEventHandler(value); + remove => _weakEventManager.RemoveEventHandler(value); + } bool _disposed; @@ -21,30 +26,37 @@ public CellTableViewCell(UITableViewCellStyle style, string key) : base(style, k public Cell Cell { - get { return _cell; } + get => _cell?.GetTargetOrDefault(); set { - if (_cell == value) - return; - - if (_cell != null) + if (_cell is null) { - _cell.PropertyChanged -= HandlePropertyChanged; - BeginInvokeOnMainThread(_cell.SendDisappearing); + _cell = new(value); + } + else + { + if (_cell.TryGetTarget(out var cell) && cell == value) + return; + + if (cell != null) + { + cell.PropertyChanged -= HandlePropertyChanged; + BeginInvokeOnMainThread(cell.SendDisappearing); + } + _cell = new(value); } - _cell = value; - if (_cell != null) + if (value != null) { - _cell.PropertyChanged += HandlePropertyChanged; - BeginInvokeOnMainThread(_cell.SendAppearing); + value.PropertyChanged += HandlePropertyChanged; + BeginInvokeOnMainThread(value.SendAppearing); } } } public Element Element => Cell; - public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) => PropertyChanged?.Invoke(sender, e); + public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) => _weakEventManager.HandleEvent(sender, e, nameof(PropertyChanged)); internal static UITableViewCell GetPlatformCell(UITableView tableView, Cell cell, bool recycleCells = false, string templateId = "") { @@ -109,12 +121,10 @@ protected override void Dispose(bool disposing) if (disposing) { - PropertyChanged = null; - - if (_cell != null) + if (Cell is Cell cell) { - _cell.PropertyChanged -= HandlePropertyChanged; - CellRenderer.SetRealCell(_cell, null); + cell.PropertyChanged -= HandlePropertyChanged; + CellRenderer.SetRealCell(cell, null); } _cell = null; } diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs index adc91a73b87e..2917e2959367 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs @@ -29,7 +29,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, SetRealCell(item, tvc); tvc.Cell = textCell; - tvc.PropertyChanged = HandleCellPropertyChanged; + tvc.PropertyChanged += HandleCellPropertyChanged; #pragma warning disable CA1416, CA1422 // TODO: 'UITableViewCell.TextLabel', DetailTextLabel is unsupported on: 'ios' 14.0 and later tvc.TextLabel.Text = textCell.Text; diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs index 3ffa18919f74..de75058e41ef 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs @@ -43,9 +43,9 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, internal sealed class ViewTableCell : UITableViewCell, INativeElementView { - IMauiContext MauiContext => _viewCell.FindMauiContext(); + IMauiContext MauiContext => ViewCell?.FindMauiContext(); WeakReference _rendererRef; - ViewCell _viewCell; + WeakReference _viewCell; Element INativeElementView.Element => ViewCell; internal bool SupressSeparator { get; set; } @@ -57,13 +57,19 @@ public ViewTableCell(string key) : base(UITableViewCellStyle.Default, key) public ViewCell ViewCell { - get { return _viewCell; } + get => _viewCell?.GetTargetOrDefault(); set { - if (_viewCell == value) - return; - - _viewCell?.Handler?.DisconnectHandler(); + if (_viewCell is null) + { + _viewCell = new(value); + } + else + { + if (_viewCell.TryGetTarget(out var viewCell) && viewCell == value) + return; + viewCell?.Handler?.DisconnectHandler(); + } UpdateCell(value); } } @@ -82,7 +88,7 @@ void ViewCellPropertyChanged(object sender, PropertyChangedEventArgs e) var realCell = (ViewTableCell)GetRealCell(viewCell); if (e.PropertyName == Cell.IsEnabledProperty.PropertyName) - UpdateIsEnabled(_viewCell.IsEnabled); + UpdateIsEnabled(viewCell.IsEnabled); } public override void LayoutSubviews() @@ -149,11 +155,11 @@ protected override void Dispose(bool disposing) _rendererRef = null; } - if (_viewCell != null) + if (ViewCell is ViewCell viewCell) { - _viewCell.PropertyChanged -= ViewCellPropertyChanged; - _viewCell.View.MeasureInvalidated -= OnMeasureInvalidated; - SetRealCell(_viewCell, null); + viewCell.PropertyChanged -= ViewCellPropertyChanged; + viewCell.View.MeasureInvalidated -= OnMeasureInvalidated; + SetRealCell(viewCell, null); } _viewCell = null; } @@ -165,10 +171,10 @@ protected override void Dispose(bool disposing) IPlatformViewHandler GetNewRenderer() { - if (_viewCell.View == null) - throw new InvalidOperationException($"ViewCell must have a {nameof(_viewCell.View)}"); + if (ViewCell is not ViewCell viewCell || viewCell.View == null) + throw new InvalidOperationException($"ViewCell must have a {nameof(viewCell.View)}"); - var newRenderer = _viewCell.View.ToHandler(_viewCell.View.FindMauiContext()); + var newRenderer = viewCell.View.ToHandler(viewCell.View.FindMauiContext()); _rendererRef = new WeakReference(newRenderer); ContentView.ClearSubviews(); ContentView.AddSubview(newRenderer.VirtualView.ToPlatform()); @@ -179,15 +185,14 @@ void UpdateCell(ViewCell cell) { Performance.Start(out string reference); - var oldCell = _viewCell; - if (oldCell != null) + if (ViewCell is ViewCell oldCell) { BeginInvokeOnMainThread(oldCell.SendDisappearing); oldCell.PropertyChanged -= ViewCellPropertyChanged; oldCell.View.MeasureInvalidated -= OnMeasureInvalidated; } - _viewCell = cell; + _viewCell = new(cell); if (cell is null) { @@ -196,20 +201,20 @@ void UpdateCell(ViewCell cell) return; } - _viewCell.PropertyChanged += ViewCellPropertyChanged; - BeginInvokeOnMainThread(_viewCell.SendAppearing); + cell.PropertyChanged += ViewCellPropertyChanged; + BeginInvokeOnMainThread(cell.SendAppearing); IPlatformViewHandler renderer; if (_rendererRef == null || !_rendererRef.TryGetTarget(out renderer)) renderer = GetNewRenderer(); else { - var viewHandlerType = MauiContext.Handlers.GetHandlerType(_viewCell.View.GetType()); + var viewHandlerType = MauiContext.Handlers.GetHandlerType(cell.View.GetType()); var reflectableType = renderer as System.Reflection.IReflectableType; var rendererType = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : (renderer != null ? renderer.GetType() : typeof(System.Object)); if (rendererType == viewHandlerType/* || (renderer is Platform.DefaultRenderer && type == null)*/) - renderer.SetVirtualView(this._viewCell.View); + renderer.SetVirtualView(cell.View); else { //when cells are getting reused the element could be already set to another cell @@ -219,8 +224,8 @@ void UpdateCell(ViewCell cell) } } - UpdateIsEnabled(_viewCell.IsEnabled); - _viewCell.View.MeasureInvalidated += OnMeasureInvalidated; + UpdateIsEnabled(cell.IsEnabled); + cell.View.MeasureInvalidated += OnMeasureInvalidated; Performance.Stop(reference); } diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt index 82744bf66c6b..04146b2eb6f1 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt @@ -487,7 +487,7 @@ ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.CellTableViewCell(UIKit.UITableViewCellStyle style, string key) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.Element.get -> Microsoft.Maui.Controls.Element ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) -> void -~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action +Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.EntryCellTableViewCell(string cellName) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.TextField.get -> UIKit.UITextField ~Microsoft.Maui.Controls.Handlers.Compatibility.NavigationRenderer.Element.get -> Microsoft.Maui.Controls.VisualElement diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt index e25e1460bf99..7c99931a2960 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt @@ -487,7 +487,7 @@ ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.CellTableViewCell(UIKit.UITableViewCellStyle style, string key) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.Element.get -> Microsoft.Maui.Controls.Element ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) -> void -~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action +Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.EntryCellTableViewCell(string cellName) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.TextField.get -> UIKit.UITextField ~Microsoft.Maui.Controls.Handlers.Compatibility.NavigationRenderer.Element.get -> Microsoft.Maui.Controls.VisualElement diff --git a/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs b/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs index 31ad522ff871..ae8a6211b8fc 100644 --- a/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs +++ b/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs @@ -30,6 +30,7 @@ void SetupBuilder() handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); + handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); @@ -41,6 +42,7 @@ void SetupBuilder() handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); + handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); @@ -49,11 +51,13 @@ void SetupBuilder() handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); + handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); handlers.AddHandler(); + handlers.AddHandler(); #if IOS || MACCATALYST handlers.AddHandler(); #else @@ -242,6 +246,49 @@ await navPage.Navigation.PushAsync(new ContentPage await AssertionExtensions.WaitForGC(viewReference, handlerReference); } + [Theory("Cells Do Not Leak")] + [InlineData(typeof(TextCell))] + [InlineData(typeof(EntryCell))] + [InlineData(typeof(ImageCell))] + [InlineData(typeof(SwitchCell))] + [InlineData(typeof(ViewCell))] + public async Task CellsDoNotLeak(Type type) + { + SetupBuilder(); + + WeakReference viewReference = null; + WeakReference handlerReference = null; + + var observable = new ObservableCollection { 1 }; + var navPage = new NavigationPage(new ContentPage { Title = "Page 1" }); + + await CreateHandlerAndAddToWindow(new Window(navPage), async () => + { + await navPage.Navigation.PushAsync(new ContentPage + { + Content = new ListView + { + ItemTemplate = new DataTemplate(() => + { + var cell = (Cell)Activator.CreateInstance(type); + if (cell is ViewCell viewCell) + { + viewCell.View = new Label(); + } + viewReference = new WeakReference(cell); + handlerReference = new WeakReference(cell.Handler); + return cell; + }), + ItemsSource = observable + } + }); + + await navPage.Navigation.PopAsync(); + }); + + await AssertionExtensions.WaitForGC(viewReference, handlerReference); + } + #if IOS [Fact] public async Task ResignFirstResponderTouchGestureRecognizer() From 6c1cb88140b36d24293e2a0f63f21cb22bdfd7d2 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Fri, 26 Apr 2024 09:39:46 -0500 Subject: [PATCH 2/8] Fixed all public API changes --- .../Handlers/ListView/iOS/CellTableViewCell.cs | 8 ++++++-- .../Handlers/ListView/iOS/EntryCellRenderer.cs | 4 ++-- .../Handlers/ListView/iOS/SwitchCellRenderer.cs | 4 ++-- .../Handlers/ListView/iOS/TextCellRenderer.cs | 4 ++-- .../src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt | 2 +- .../Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs index 5e8d8877d916..d34b3e5c3914 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs @@ -12,7 +12,11 @@ public class CellTableViewCell : UITableViewCell, INativeElementView WeakReference _cell; readonly WeakEventManager _weakEventManager = new(); - public event Action PropertyChanged + // NOTE: internal callers can use InternalPropertyChanged + [Obsolete("To be removed in a future release.")] + public Action PropertyChanged; + + internal event Action InternalPropertyChanged { add => _weakEventManager.AddEventHandler(value); remove => _weakEventManager.RemoveEventHandler(value); @@ -56,7 +60,7 @@ public Cell Cell public Element Element => Cell; - public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) => _weakEventManager.HandleEvent(sender, e, nameof(PropertyChanged)); + public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) => _weakEventManager.HandleEvent(sender, e, nameof(InternalPropertyChanged)); internal static UITableViewCell GetPlatformCell(UITableView tableView, Cell cell, bool recycleCells = false, string templateId = "") { diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs index 0a6ff189c91d..ac6f5525e859 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs @@ -31,7 +31,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, tvc = new EntryCellTableViewCell(item.GetType().FullName); else { - tvc.PropertyChanged -= HandlePropertyChanged; + tvc.InternalPropertyChanged -= HandlePropertyChanged; tvc.TextFieldTextChanged -= OnTextFieldTextChanged; tvc.KeyboardDoneButtonPressed -= OnKeyBoardDoneButtonPressed; } @@ -39,7 +39,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, SetRealCell(item, tvc); tvc.Cell = item; - tvc.PropertyChanged += HandlePropertyChanged; + tvc.InternalPropertyChanged += HandlePropertyChanged; tvc.TextFieldTextChanged += OnTextFieldTextChanged; tvc.KeyboardDoneButtonPressed += OnKeyBoardDoneButtonPressed; diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs index fb07f837d2fe..645f82cb4c06 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs @@ -28,7 +28,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, else { uiSwitch = tvc.AccessoryView as UISwitch; - tvc.PropertyChanged -= HandlePropertyChanged; + tvc.InternalPropertyChanged -= HandlePropertyChanged; } SetRealCell(item, tvc); @@ -45,7 +45,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, _defaultOnColor = UISwitch.Appearance.OnTintColor; tvc.Cell = item; - tvc.PropertyChanged += HandlePropertyChanged; + tvc.InternalPropertyChanged += HandlePropertyChanged; tvc.AccessoryView = uiSwitch; #pragma warning disable CA1416, CA1422 // TODO: 'UITableViewCell.TextLabel' is unsupported on: 'ios' 14.0 and later tvc.TextLabel.Text = boolCell.Text; diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs index 2917e2959367..ac641da7fc73 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs @@ -24,12 +24,12 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, if (!(reusableCell is CellTableViewCell tvc)) tvc = new CellTableViewCell(UITableViewCellStyle.Subtitle, item.GetType().FullName); else - tvc.PropertyChanged -= HandleCellPropertyChanged; + tvc.InternalPropertyChanged -= HandleCellPropertyChanged; SetRealCell(item, tvc); tvc.Cell = textCell; - tvc.PropertyChanged += HandleCellPropertyChanged; + tvc.InternalPropertyChanged += HandleCellPropertyChanged; #pragma warning disable CA1416, CA1422 // TODO: 'UITableViewCell.TextLabel', DetailTextLabel is unsupported on: 'ios' 14.0 and later tvc.TextLabel.Text = textCell.Text; diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt index 04146b2eb6f1..82744bf66c6b 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Shipped.txt @@ -487,7 +487,7 @@ ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.CellTableViewCell(UIKit.UITableViewCellStyle style, string key) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.Element.get -> Microsoft.Maui.Controls.Element ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) -> void -Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action +~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.EntryCellTableViewCell(string cellName) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.TextField.get -> UIKit.UITextField ~Microsoft.Maui.Controls.Handlers.Compatibility.NavigationRenderer.Element.get -> Microsoft.Maui.Controls.VisualElement diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt index 7c99931a2960..e25e1460bf99 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Shipped.txt @@ -487,7 +487,7 @@ ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.CellTableViewCell(UIKit.UITableViewCellStyle style, string key) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.Element.get -> Microsoft.Maui.Controls.Element ~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) -> void -Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action +~Microsoft.Maui.Controls.Handlers.Compatibility.CellTableViewCell.PropertyChanged -> System.Action ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.EntryCellTableViewCell(string cellName) -> void ~Microsoft.Maui.Controls.Handlers.Compatibility.EntryCellRenderer.EntryCellTableViewCell.TextField.get -> UIKit.UITextField ~Microsoft.Maui.Controls.Handlers.Compatibility.NavigationRenderer.Element.get -> Microsoft.Maui.Controls.VisualElement From bff18485962d76eb7d0bb89cbfad9bfe191ddb07 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 8 May 2024 09:15:42 -0500 Subject: [PATCH 3/8] Better null checking One test was crashing (even on rerun), so maybe this will fix --- .../Handlers/ListView/iOS/CellTableViewCell.cs | 16 ++++++++-------- .../Handlers/ListView/iOS/ViewCellRenderer.cs | 11 ++++------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs index d34b3e5c3914..9119dc5db602 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs @@ -33,28 +33,28 @@ public Cell Cell get => _cell?.GetTargetOrDefault(); set { - if (_cell is null) - { - _cell = new(value); - } - else + if (_cell is not null) { if (_cell.TryGetTarget(out var cell) && cell == value) return; - if (cell != null) + if (cell is not null) { cell.PropertyChanged -= HandlePropertyChanged; BeginInvokeOnMainThread(cell.SendDisappearing); } - _cell = new(value); } - if (value != null) + if (value is not null) { + _cell = new(value); value.PropertyChanged += HandlePropertyChanged; BeginInvokeOnMainThread(value.SendAppearing); } + else + { + _cell = null; + } } } diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs index de75058e41ef..9031ae1e0de9 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs @@ -60,11 +60,7 @@ public ViewCell ViewCell get => _viewCell?.GetTargetOrDefault(); set { - if (_viewCell is null) - { - _viewCell = new(value); - } - else + if (_viewCell is not null) { if (_viewCell.TryGetTarget(out var viewCell) && viewCell == value) return; @@ -192,15 +188,16 @@ void UpdateCell(ViewCell cell) oldCell.View.MeasureInvalidated -= OnMeasureInvalidated; } - _viewCell = new(cell); - if (cell is null) { + _viewCell = null; _rendererRef = null; ContentView.ClearSubviews(); return; } + _viewCell = new(cell); + cell.PropertyChanged += ViewCellPropertyChanged; BeginInvokeOnMainThread(cell.SendAppearing); From ee996a61ca4f4b4a920267d5b226906ab7e3fce5 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 16 May 2024 11:05:56 -0500 Subject: [PATCH 4/8] - add null check for ImageCellRenderer --- .../Handlers/ListView/iOS/ImageCellRenderer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs index 50214195fa46..822f29b0e1a2 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs @@ -47,12 +47,12 @@ void SetImage(ImageCell cell, CellTableViewCell target) source.LoadImage(cell.FindMauiContext(), (result) => { - var uiimage = result.Value; - if (uiimage != null) + var uiimage = result?.Value; + if (uiimage is not null) { NSRunLoop.Main.BeginInvokeOnMainThread(() => { - if (target.Cell != null) + if (target.Cell is not null) { target.ImageView.Image = uiimage; target.SetNeedsLayout(); From 6afe10b568bbe40cfaeaa023614f57d48fe7aac1 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 16 May 2024 14:23:05 -0500 Subject: [PATCH 5/8] - wire everything up through handler paths --- src/Controls/src/Core/Cells/Cell.cs | 1 + .../Handlers/ListView/iOS/CellRenderer.cs | 79 +++++++++---------- .../ListView/iOS/CellTableViewCell.cs | 13 +-- .../ListView/iOS/EntryCellRenderer.cs | 8 +- .../ListView/iOS/ImageCellRenderer.cs | 2 - .../ListView/iOS/SwitchCellRenderer.cs | 6 +- .../Handlers/ListView/iOS/TextCellRenderer.cs | 6 +- .../Handlers/ListView/iOS/ViewCellRenderer.cs | 2 - src/Controls/src/Core/Element/Element.cs | 8 +- .../PublicAPI/net-ios/PublicAPI.Unshipped.txt | 2 + 10 files changed, 56 insertions(+), 71 deletions(-) diff --git a/src/Controls/src/Core/Cells/Cell.cs b/src/Controls/src/Core/Cells/Cell.cs index c63c4a94d386..3dcc71edef02 100644 --- a/src/Controls/src/Core/Cells/Cell.cs +++ b/src/Controls/src/Core/Cells/Cell.cs @@ -273,6 +273,7 @@ async void OnForceUpdateSizeRequested() // don't run more than once per 16 milliseconds await Task.Delay(TimeSpan.FromMilliseconds(16)); ForceUpdateSizeRequested?.Invoke(this, null); + Handler.Invoke("ForceUpdateSizeRequested", null); _nextCallToForceUpdateSizeQueued = false; } diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs index a0e390947e65..119b6f4effe2 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs @@ -11,8 +11,6 @@ public class CellRenderer : ElementHandler, IRegisterable { static readonly BindableProperty RealCellProperty = BindableProperty.CreateAttached("RealCell", typeof(UITableViewCell), typeof(Cell), null); - EventHandler? _onForceUpdateSizeRequested; - PropertyChangedEventHandler? _onPropertyChangedEventHandler; readonly UIColor _defaultCellBgColor = (OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13)) ? UIColor.Clear : UIColor.White; public static PropertyMapper Mapper = @@ -22,6 +20,8 @@ public class CellRenderer : ElementHandler, IRegisterable new CommandMapper(ElementHandler.ElementCommandMapper); UITableView? _tableView; + private protected event PropertyChangedEventHandler? CellPropertyChanged; + public CellRenderer() : base(Mapper, CommandMapper) { } @@ -44,8 +44,6 @@ public virtual UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, tvc.Cell = item; - WireUpForceUpdateSizeRequested(item, tvc, tv); - if (OperatingSystem.IsIOSVersionAtLeast(14)) { var content = tvc.DefaultContentConfiguration; @@ -135,44 +133,9 @@ protected void UpdateBackground(UITableViewCell tableViewCell, Cell cell) SetBackgroundColor(tableViewCell, cell, uiBgColor); } + [Obsolete("The ForceUpdateSizeRequested event is now managed by the command mapper, so it's not necessary to invoke this event manually.")] protected void WireUpForceUpdateSizeRequested(ICellController cell, UITableViewCell platformCell, UITableView tableView) { - var inpc = cell as INotifyPropertyChanged; - cell.ForceUpdateSizeRequested -= _onForceUpdateSizeRequested; - - if (inpc != null) - inpc.PropertyChanged -= _onPropertyChangedEventHandler; - - _onForceUpdateSizeRequested = (sender, e) => - { - var index = tableView?.IndexPathForCell(platformCell); - if (index == null && sender is Cell c) - { - index = Controls.Compatibility.Platform.iOS.CellExtensions.GetIndexPath(c); - } - - if (index != null) - tableView?.ReloadRows(new[] { index }, UITableViewRowAnimation.None); - }; - - _onPropertyChangedEventHandler = (sender, e) => - { - if (e.PropertyName == "RealCell" && sender is BindableObject bo && GetRealCell(bo) == null) - { - if (sender is ICellController icc) - icc.ForceUpdateSizeRequested -= _onForceUpdateSizeRequested; - - if (sender is INotifyPropertyChanged notifyPropertyChanged) - notifyPropertyChanged.PropertyChanged -= _onPropertyChangedEventHandler; - - _onForceUpdateSizeRequested = null; - _onPropertyChangedEventHandler = null; - } - }; - - cell.ForceUpdateSizeRequested += _onForceUpdateSizeRequested; - if (inpc != null) - inpc.PropertyChanged += _onPropertyChangedEventHandler; } @@ -190,5 +153,39 @@ internal static void SetRealCell(BindableObject cell, UITableViewCell renderer) { cell.SetValue(RealCellProperty, renderer); } + + public override void UpdateValue(string property) + { + base.UpdateValue(property); + var args = new PropertyChangedEventArgs(property); + if (VirtualView is BindableObject bindableObject) + { + var realCell = GetRealCell(bindableObject); + if(realCell is CellTableViewCell ctv) + { + ctv.HandlePropertyChanged(bindableObject, args); + } + } + + CellPropertyChanged?.Invoke(VirtualView, args); + } + + public override void Invoke(string command, object? args) + { + base.Invoke(command, args); + + if (command == "ForceUpdateSizeRequested"&& + VirtualView is BindableObject bindableObject && + GetRealCell(bindableObject) is UITableViewCell ctv) + { + var index = _tableView?.IndexPathForCell(ctv); + if (index == null && VirtualView is Cell c) + { + index = Controls.Compatibility.Platform.iOS.CellExtensions.GetIndexPath(c); + } + if (index != null) + _tableView?.ReloadRows(new[] { index }, UITableViewRowAnimation.None); + } + } } -} +} \ No newline at end of file diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs index 9119dc5db602..a21d4362c25e 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs @@ -10,18 +10,9 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility public class CellTableViewCell : UITableViewCell, INativeElementView { WeakReference _cell; - readonly WeakEventManager _weakEventManager = new(); - // NOTE: internal callers can use InternalPropertyChanged - [Obsolete("To be removed in a future release.")] public Action PropertyChanged; - internal event Action InternalPropertyChanged - { - add => _weakEventManager.AddEventHandler(value); - remove => _weakEventManager.RemoveEventHandler(value); - } - bool _disposed; public CellTableViewCell(UITableViewCellStyle style, string key) : base(style, key) @@ -40,7 +31,6 @@ public Cell Cell if (cell is not null) { - cell.PropertyChanged -= HandlePropertyChanged; BeginInvokeOnMainThread(cell.SendDisappearing); } } @@ -48,7 +38,6 @@ public Cell Cell if (value is not null) { _cell = new(value); - value.PropertyChanged += HandlePropertyChanged; BeginInvokeOnMainThread(value.SendAppearing); } else @@ -60,7 +49,7 @@ public Cell Cell public Element Element => Cell; - public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) => _weakEventManager.HandleEvent(sender, e, nameof(InternalPropertyChanged)); + public void HandlePropertyChanged(object sender, PropertyChangedEventArgs e) => PropertyChanged?.Invoke(sender, e); internal static UITableViewCell GetPlatformCell(UITableView tableView, Cell cell, bool recycleCells = false, string templateId = "") { diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs index ac6f5525e859..3b506c726b52 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/EntryCellRenderer.cs @@ -28,10 +28,12 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, var tvc = reusableCell as EntryCellTableViewCell; if (tvc == null) + { tvc = new EntryCellTableViewCell(item.GetType().FullName); + } else { - tvc.InternalPropertyChanged -= HandlePropertyChanged; + CellPropertyChanged -= HandlePropertyChanged; tvc.TextFieldTextChanged -= OnTextFieldTextChanged; tvc.KeyboardDoneButtonPressed -= OnKeyBoardDoneButtonPressed; } @@ -39,12 +41,10 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, SetRealCell(item, tvc); tvc.Cell = item; - tvc.InternalPropertyChanged += HandlePropertyChanged; + CellPropertyChanged += HandlePropertyChanged; tvc.TextFieldTextChanged += OnTextFieldTextChanged; tvc.KeyboardDoneButtonPressed += OnKeyBoardDoneButtonPressed; - WireUpForceUpdateSizeRequested(item, tvc, tv); - UpdateBackground(tvc, entryCell); UpdateLabel(tvc, entryCell); UpdateText(tvc, entryCell); diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs index 822f29b0e1a2..428711634557 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ImageCellRenderer.cs @@ -20,8 +20,6 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, var imageCell = (ImageCell)item; - WireUpForceUpdateSizeRequested(item, result, tv); - SetImage(imageCell, result); return result; diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs index 645f82cb4c06..85a8239d8869 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/SwitchCellRenderer.cs @@ -28,7 +28,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, else { uiSwitch = tvc.AccessoryView as UISwitch; - tvc.InternalPropertyChanged -= HandlePropertyChanged; + CellPropertyChanged -= HandlePropertyChanged; } SetRealCell(item, tvc); @@ -45,7 +45,7 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, _defaultOnColor = UISwitch.Appearance.OnTintColor; tvc.Cell = item; - tvc.InternalPropertyChanged += HandlePropertyChanged; + CellPropertyChanged += HandlePropertyChanged; tvc.AccessoryView = uiSwitch; #pragma warning disable CA1416, CA1422 // TODO: 'UITableViewCell.TextLabel' is unsupported on: 'ios' 14.0 and later tvc.TextLabel.Text = boolCell.Text; @@ -53,8 +53,6 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, uiSwitch.On = boolCell.On; - WireUpForceUpdateSizeRequested(item, tvc, tv); - UpdateBackground(tvc, item); UpdateIsEnabled(tvc, boolCell); UpdateFlowDirection(tvc, boolCell); diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs index ac641da7fc73..82e83a4c695c 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/TextCellRenderer.cs @@ -24,12 +24,12 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, if (!(reusableCell is CellTableViewCell tvc)) tvc = new CellTableViewCell(UITableViewCellStyle.Subtitle, item.GetType().FullName); else - tvc.InternalPropertyChanged -= HandleCellPropertyChanged; + CellPropertyChanged -= HandleCellPropertyChanged; SetRealCell(item, tvc); tvc.Cell = textCell; - tvc.InternalPropertyChanged += HandleCellPropertyChanged; + CellPropertyChanged += HandleCellPropertyChanged; #pragma warning disable CA1416, CA1422 // TODO: 'UITableViewCell.TextLabel', DetailTextLabel is unsupported on: 'ios' 14.0 and later tvc.TextLabel.Text = textCell.Text; @@ -37,8 +37,6 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, tvc.TextLabel.TextColor = (textCell.TextColor ?? DefaultTextColor).ToPlatform(); tvc.DetailTextLabel.TextColor = (textCell.DetailColor ?? DefaultDetailColor).ToPlatform(); - WireUpForceUpdateSizeRequested(item, tvc, tv); - UpdateIsEnabled(tvc, textCell); #pragma warning restore CA1416, CA1422 diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs index 9031ae1e0de9..b4e43ce53683 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/ViewCellRenderer.cs @@ -31,8 +31,6 @@ public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, SetRealCell(item, cell); - WireUpForceUpdateSizeRequested(item, cell, tv); - UpdateBackground(cell, item); SetAccessibility(cell, item); diff --git a/src/Controls/src/Core/Element/Element.cs b/src/Controls/src/Core/Element/Element.cs index bf3dada20406..e23be851a4a1 100644 --- a/src/Controls/src/Core/Element/Element.cs +++ b/src/Controls/src/Core/Element/Element.cs @@ -567,11 +567,15 @@ protected virtual void OnParentSet() /// Method that is called when a bound property is changed. /// The name of the bound property that changed. - protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) + protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) => + OnPropertyChanged(propertyName, true); + + internal void OnPropertyChanged([CallerMemberName] string propertyName = null, bool propagateToHandler = true) { base.OnPropertyChanged(propertyName); - UpdateHandlerValue(propertyName); + if (propagateToHandler) + UpdateHandlerValue(propertyName); if (_effects?.Count > 0) { diff --git a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt index 73b19b6a1f68..ec30a83003c7 100644 --- a/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ #nullable enable +override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.Invoke(string! command, object? args) -> void +override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.UpdateValue(string! property) -> void override Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.PreferredStatusBarUpdateAnimation.get -> UIKit.UIStatusBarAnimation override Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.PrefersStatusBarHidden() -> bool override Microsoft.Maui.Controls.GradientBrush.IsEmpty.get -> bool From d9e0f64e65fbfbb2f190d5f28ec3c94b36ef5306 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 16 May 2024 14:27:27 -0500 Subject: [PATCH 6/8] - cleanup --- .../Handlers/ListView/iOS/CellRenderer.cs | 11 ++++------- src/Controls/src/Core/Element/Element.cs | 8 ++------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs index 119b6f4effe2..eab9e8276267 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellRenderer.cs @@ -158,13 +158,10 @@ public override void UpdateValue(string property) { base.UpdateValue(property); var args = new PropertyChangedEventArgs(property); - if (VirtualView is BindableObject bindableObject) + if (VirtualView is BindableObject bindableObject && + GetRealCell(bindableObject) is CellTableViewCell ctv ) { - var realCell = GetRealCell(bindableObject); - if(realCell is CellTableViewCell ctv) - { - ctv.HandlePropertyChanged(bindableObject, args); - } + ctv.HandlePropertyChanged(bindableObject, args); } CellPropertyChanged?.Invoke(VirtualView, args); @@ -174,7 +171,7 @@ public override void Invoke(string command, object? args) { base.Invoke(command, args); - if (command == "ForceUpdateSizeRequested"&& + if (command == "ForceUpdateSizeRequested" && VirtualView is BindableObject bindableObject && GetRealCell(bindableObject) is UITableViewCell ctv) { diff --git a/src/Controls/src/Core/Element/Element.cs b/src/Controls/src/Core/Element/Element.cs index e23be851a4a1..bf3dada20406 100644 --- a/src/Controls/src/Core/Element/Element.cs +++ b/src/Controls/src/Core/Element/Element.cs @@ -567,15 +567,11 @@ protected virtual void OnParentSet() /// Method that is called when a bound property is changed. /// The name of the bound property that changed. - protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) => - OnPropertyChanged(propertyName, true); - - internal void OnPropertyChanged([CallerMemberName] string propertyName = null, bool propagateToHandler = true) + protected override void OnPropertyChanged([CallerMemberName] string propertyName = null) { base.OnPropertyChanged(propertyName); - if (propagateToHandler) - UpdateHandlerValue(propertyName); + UpdateHandlerValue(propertyName); if (_effects?.Count > 0) { From 12c1161de3a160621c18468d9087e1f7169d765a Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 16 May 2024 14:29:37 -0500 Subject: [PATCH 7/8] Update CellTableViewCell.cs --- .../Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs index a21d4362c25e..84ad28b6f1dd 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/ListView/iOS/CellTableViewCell.cs @@ -116,7 +116,6 @@ protected override void Dispose(bool disposing) { if (Cell is Cell cell) { - cell.PropertyChanged -= HandlePropertyChanged; CellRenderer.SetRealCell(cell, null); } _cell = null; @@ -127,4 +126,4 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } } -} \ No newline at end of file +} From d518c87559d72e31719d97f07bb68314b234cea3 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 16 May 2024 15:27:37 -0500 Subject: [PATCH 8/8] - add additional unshipped --- .../src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt index caa61b6d473d..c77202070fd5 100644 --- a/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt +++ b/src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt @@ -1,4 +1,6 @@ #nullable enable +override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.Invoke(string! command, object? args) -> void +override Microsoft.Maui.Controls.Handlers.Compatibility.CellRenderer.UpdateValue(string! property) -> void override Microsoft.Maui.Controls.GradientBrush.IsEmpty.get -> bool override Microsoft.Maui.Controls.Label.ArrangeOverride(Microsoft.Maui.Graphics.Rect bounds) -> Microsoft.Maui.Graphics.Size override Microsoft.Maui.Controls.Handlers.Compatibility.ShellRenderer.PreferredStatusBarUpdateAnimation.get -> UIKit.UIStatusBarAnimation