From 8e07f9f85633ca6aa12541b83482e6618df9ef8a Mon Sep 17 00:00:00 2001 From: DmitryZhelnin Date: Sat, 23 Sep 2023 08:28:41 +0300 Subject: [PATCH] Fix TabItem memory leak (#12418) * TabItem: dispose _ownerSubscriptions when item is detached from visual tree * TabItem: update of TabStripPlacement moved to TabControl * TabControl: iterate over ItemsPresenterPart's children instead of calling ContainerFromIndex * TabItem: SubscribeToOwnerProperties marked as obsolete --------- Co-authored-by: Dmitry Zhelnin --- src/Avalonia.Controls/TabControl.cs | 22 +++++++++++++++++++++- src/Avalonia.Controls/TabItem.cs | 20 ++------------------ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Controls/TabControl.cs b/src/Avalonia.Controls/TabControl.cs index 2b3a00cf3e..95bf1a3cda 100644 --- a/src/Avalonia.Controls/TabControl.cs +++ b/src/Avalonia.Controls/TabControl.cs @@ -73,6 +73,7 @@ namespace Avalonia.Controls { SelectionModeProperty.OverrideDefaultValue(SelectionMode.AlwaysSelected); ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); + TabStripPlacementProperty.Changed.AddClassHandler((x, e) => x.UpdateTabStripPlacement()); AffectsMeasure(TabStripPlacementProperty); SelectedItemProperty.Changed.AddClassHandler((x, e) => x.UpdateSelectedContent()); AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue(AutomationControlType.Tab); @@ -153,7 +154,7 @@ namespace Avalonia.Controls protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey) { - return new TabItem(); + return new TabItem { TabStripPlacement = TabStripPlacement }; } protected internal override bool NeedsContainerOverride(object? item, int index, out object? recycleKey) @@ -229,6 +230,8 @@ namespace Avalonia.Controls ItemsPresenterPart = e.NameScope.Find("PART_ItemsPresenter"); ItemsPresenterPart?.ApplyTemplate(); + UpdateTabStripPlacement(); + // Set TabNavigation to Once on the panel if not already set and // forward the TabOnceActiveElement to the panel. if (ItemsPresenterPart?.Panel is { } panel) @@ -306,5 +309,22 @@ namespace Avalonia.Controls change.GetNewValue()); } } + + private void UpdateTabStripPlacement() + { + var controls = ItemsPresenterPart?.Panel?.Children; + if (controls is null) + { + return; + } + + foreach (var control in controls) + { + if (control is TabItem tabItem) + { + tabItem.TabStripPlacement = TabStripPlacement; + } + } + } } } diff --git a/src/Avalonia.Controls/TabItem.cs b/src/Avalonia.Controls/TabItem.cs index 7a3465b1a4..cdc28082f7 100644 --- a/src/Avalonia.Controls/TabItem.cs +++ b/src/Avalonia.Controls/TabItem.cs @@ -4,8 +4,6 @@ using Avalonia.Automation.Peers; using Avalonia.Controls.Metadata; using Avalonia.Controls.Mixins; using Avalonia.Controls.Primitives; -using Avalonia.Reactive; -using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -16,7 +14,6 @@ namespace Avalonia.Controls public class TabItem : HeaderedContentControl, ISelectable { private Dock? _tabStripPlacement; - private IDisposable? _ownerSubscriptions; /// /// Defines the property. @@ -48,7 +45,7 @@ namespace Avalonia.Controls public Dock? TabStripPlacement { get => _tabStripPlacement; - private set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value); + internal set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value); } /// @@ -62,22 +59,9 @@ namespace Avalonia.Controls protected override AutomationPeer OnCreateAutomationPeer() => new ListItemAutomationPeer(this); - protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) - { - base.OnAttachedToVisualTree(e); - - _ownerSubscriptions?.Dispose(); - _ownerSubscriptions = null; - - if (this.FindAncestorOfType() is { } owner && owner.IndexFromContainer(this) != -1) - { - SubscribeToOwnerProperties(owner); - } - } - + [Obsolete("Owner manages its children properties by itself")] protected void SubscribeToOwnerProperties(AvaloniaObject owner) { - _ownerSubscriptions = owner.GetObservable(TabControl.TabStripPlacementProperty).Subscribe(v => TabStripPlacement = v); } private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj)