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); SelectionModeProperty.OverrideDefaultValue<TabControl>(SelectionMode.AlwaysSelected);
ItemsPanelProperty.OverrideDefaultValue<TabControl>(DefaultPanel); ItemsPanelProperty.OverrideDefaultValue<TabControl>(DefaultPanel);
TabStripPlacementProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateTabStripPlacement());
AffectsMeasure<TabControl>(TabStripPlacementProperty); AffectsMeasure<TabControl>(TabStripPlacementProperty);
SelectedItemProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateSelectedContent()); SelectedItemProperty.Changed.AddClassHandler<TabControl>((x, e) => x.UpdateSelectedContent());
AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabControl>(AutomationControlType.Tab); AutomationProperties.ControlTypeOverrideProperty.OverrideDefaultValue<TabControl>(AutomationControlType.Tab);
@ -153,7 +154,7 @@ namespace Avalonia.Controls
protected internal override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey) 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) 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 = e.NameScope.Find<ItemsPresenter>("PART_ItemsPresenter");
ItemsPresenterPart?.ApplyTemplate(); ItemsPresenterPart?.ApplyTemplate();
UpdateTabStripPlacement();
// Set TabNavigation to Once on the panel if not already set and // Set TabNavigation to Once on the panel if not already set and
// forward the TabOnceActiveElement to the panel. // forward the TabOnceActiveElement to the panel.
if (ItemsPresenterPart?.Panel is { } panel) if (ItemsPresenterPart?.Panel is { } panel)
@ -306,5 +309,22 @@ namespace Avalonia.Controls
change.GetNewValue<IInputElement?>()); 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.Metadata;
using Avalonia.Controls.Mixins; using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Reactive;
using Avalonia.VisualTree;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
@ -16,7 +14,6 @@ namespace Avalonia.Controls
public class TabItem : HeaderedContentControl, ISelectable public class TabItem : HeaderedContentControl, ISelectable
{ {
private Dock? _tabStripPlacement; private Dock? _tabStripPlacement;
private IDisposable? _ownerSubscriptions;
/// <summary> /// <summary>
/// Defines the <see cref="TabStripPlacement"/> property. /// Defines the <see cref="TabStripPlacement"/> property.
@ -48,7 +45,7 @@ namespace Avalonia.Controls
public Dock? TabStripPlacement public Dock? TabStripPlacement
{ {
get => _tabStripPlacement; get => _tabStripPlacement;
private set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value); internal set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value);
} }
/// <summary> /// <summary>
@ -62,22 +59,9 @@ namespace Avalonia.Controls
protected override AutomationPeer OnCreateAutomationPeer() => new ListItemAutomationPeer(this); protected override AutomationPeer OnCreateAutomationPeer() => new ListItemAutomationPeer(this);
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) [Obsolete("Owner manages its children properties by itself")]
{
base.OnAttachedToVisualTree(e);
_ownerSubscriptions?.Dispose();
_ownerSubscriptions = null;
if (this.FindAncestorOfType<TabControl>() is { } owner && owner.IndexFromContainer(this) != -1)
{
SubscribeToOwnerProperties(owner);
}
}
protected void SubscribeToOwnerProperties(AvaloniaObject owner) protected void SubscribeToOwnerProperties(AvaloniaObject owner)
{ {
_ownerSubscriptions = owner.GetObservable(TabControl.TabStripPlacementProperty).Subscribe(v => TabStripPlacement = v);
} }
private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj) private void UpdateHeader(AvaloniaPropertyChangedEventArgs obj)

Loading…
Cancel
Save