Browse Source

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 <d.zhelnin@of-group.ru>
pull/12979/head
DmitryZhelnin 3 years ago
committed by GitHub
parent
commit
8e07f9f856
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      src/Avalonia.Controls/TabControl.cs
  2. 20
      src/Avalonia.Controls/TabItem.cs

22
src/Avalonia.Controls/TabControl.cs

@ -73,6 +73,7 @@ namespace Avalonia.Controls
{
SelectionModeProperty.OverrideDefaultValue<TabControl>(SelectionMode.AlwaysSelected);
ItemsPanelProperty.OverrideDefaultValue<TabControl>(DefaultPanel);
TabStripPlacementProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateTabStripPlacement());
AffectsMeasure<TabControl>(TabStripPlacementProperty);
SelectedItemProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateSelectedContent());
AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabControl>(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<ItemsPresenter>("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<IInputElement?>());
}
}
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;
}
}
}
}
}

20
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;
/// <summary>
/// Defines the <see cref="TabStripPlacement"/> 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);
}
/// <summary>
@ -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<TabControl>() 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)

Loading…
Cancel
Save