|
@@ -10,6 +10,7 @@ using Avalonia.LogicalTree;
|
|
using Avalonia.VisualTree;
|
|
using Avalonia.VisualTree;
|
|
using Avalonia.Automation;
|
|
using Avalonia.Automation;
|
|
using Avalonia.Controls.Metadata;
|
|
using Avalonia.Controls.Metadata;
|
|
|
|
+using Avalonia.Reactive;
|
|
|
|
|
|
namespace Avalonia.Controls
|
|
namespace Avalonia.Controls
|
|
{
|
|
{
|
|
@@ -19,6 +20,10 @@ namespace Avalonia.Controls
|
|
[TemplatePart("PART_ItemsPresenter", typeof(ItemsPresenter))]
|
|
[TemplatePart("PART_ItemsPresenter", typeof(ItemsPresenter))]
|
|
public class TabControl : SelectingItemsControl, IContentPresenterHost
|
|
public class TabControl : SelectingItemsControl, IContentPresenterHost
|
|
{
|
|
{
|
|
|
|
+ private object? _selectedContent;
|
|
|
|
+ private IDataTemplate? _selectedContentTemplate;
|
|
|
|
+ private CompositeDisposable? _selectedItemSubscriptions;
|
|
|
|
+
|
|
/// <summary>
|
|
/// <summary>
|
|
/// Defines the <see cref="TabStripPlacement"/> property.
|
|
/// Defines the <see cref="TabStripPlacement"/> property.
|
|
/// </summary>
|
|
/// </summary>
|
|
@@ -46,14 +51,14 @@ namespace Avalonia.Controls
|
|
/// <summary>
|
|
/// <summary>
|
|
/// The selected content property
|
|
/// The selected content property
|
|
/// </summary>
|
|
/// </summary>
|
|
- public static readonly StyledProperty<object?> SelectedContentProperty =
|
|
|
|
- AvaloniaProperty.Register<TabControl, object?>(nameof(SelectedContent));
|
|
|
|
|
|
+ public static readonly DirectProperty<TabControl, object?> SelectedContentProperty =
|
|
|
|
+ AvaloniaProperty.RegisterDirect<TabControl, object?>(nameof(SelectedContent), o => o.SelectedContent);
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// The selected content template property
|
|
/// The selected content template property
|
|
/// </summary>
|
|
/// </summary>
|
|
- public static readonly StyledProperty<IDataTemplate?> SelectedContentTemplateProperty =
|
|
|
|
- AvaloniaProperty.Register<TabControl, IDataTemplate?>(nameof(SelectedContentTemplate));
|
|
|
|
|
|
+ public static readonly DirectProperty<TabControl, IDataTemplate?> SelectedContentTemplateProperty =
|
|
|
|
+ AvaloniaProperty.RegisterDirect<TabControl, IDataTemplate?>(nameof(SelectedContentTemplate), o => o.SelectedContentTemplate);
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
|
|
/// The default value for the <see cref="ItemsControl.ItemsPanel"/> property.
|
|
@@ -115,11 +120,10 @@ namespace Avalonia.Controls
|
|
/// <value>
|
|
/// <value>
|
|
/// The content of the selected tab.
|
|
/// The content of the selected tab.
|
|
/// </value>
|
|
/// </value>
|
|
- [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
|
|
|
|
public object? SelectedContent
|
|
public object? SelectedContent
|
|
{
|
|
{
|
|
- get { return GetValue(SelectedContentProperty); }
|
|
|
|
- internal set { SetValue(SelectedContentProperty, value); }
|
|
|
|
|
|
+ get => _selectedContent;
|
|
|
|
+ internal set => SetAndRaise(SelectedContentProperty, ref _selectedContent, value);
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
@@ -128,11 +132,10 @@ namespace Avalonia.Controls
|
|
/// <value>
|
|
/// <value>
|
|
/// The content template of the selected tab.
|
|
/// The content template of the selected tab.
|
|
/// </value>
|
|
/// </value>
|
|
- [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1032", Justification = "This property is supposed to be a styled readonly property.")]
|
|
|
|
public IDataTemplate? SelectedContentTemplate
|
|
public IDataTemplate? SelectedContentTemplate
|
|
{
|
|
{
|
|
- get { return GetValue(SelectedContentTemplateProperty); }
|
|
|
|
- internal set { SetValue(SelectedContentTemplateProperty, value); }
|
|
|
|
|
|
+ get => _selectedContentTemplate;
|
|
|
|
+ internal set => SetAndRaise(SelectedContentTemplateProperty, ref _selectedContentTemplate, value);
|
|
}
|
|
}
|
|
|
|
|
|
internal ItemsPresenter? ItemsPresenterPart { get; private set; }
|
|
internal ItemsPresenter? ItemsPresenterPart { get; private set; }
|
|
@@ -161,18 +164,10 @@ namespace Avalonia.Controls
|
|
protected internal override void PrepareContainerForItemOverride(Control element, object? item, int index)
|
|
protected internal override void PrepareContainerForItemOverride(Control element, object? item, int index)
|
|
{
|
|
{
|
|
base.PrepareContainerForItemOverride(element, item, index);
|
|
base.PrepareContainerForItemOverride(element, item, index);
|
|
-
|
|
|
|
- if (element is TabItem tabItem)
|
|
|
|
- {
|
|
|
|
- if (ContentTemplate is { } ct)
|
|
|
|
- tabItem.ContentTemplate = ct;
|
|
|
|
- tabItem.SetValue(TabStripPlacementProperty, TabStripPlacement);
|
|
|
|
- }
|
|
|
|
|
|
|
|
- if (index == SelectedIndex && element is ContentControl container)
|
|
|
|
|
|
+ if (index == SelectedIndex)
|
|
{
|
|
{
|
|
- SelectedContentTemplate = container.ContentTemplate;
|
|
|
|
- SelectedContent = container.Content;
|
|
|
|
|
|
+ UpdateSelectedContent(element);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -192,18 +187,25 @@ namespace Avalonia.Controls
|
|
UpdateSelectedContent();
|
|
UpdateSelectedContent();
|
|
}
|
|
}
|
|
|
|
|
|
- private void UpdateSelectedContent()
|
|
|
|
|
|
+ private void UpdateSelectedContent(Control? container = null)
|
|
{
|
|
{
|
|
|
|
+ _selectedItemSubscriptions?.Dispose();
|
|
|
|
+ _selectedItemSubscriptions = null;
|
|
|
|
+
|
|
if (SelectedIndex == -1)
|
|
if (SelectedIndex == -1)
|
|
{
|
|
{
|
|
SelectedContent = SelectedContentTemplate = null;
|
|
SelectedContent = SelectedContentTemplate = null;
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
- var container = SelectedItem as IContentControl ??
|
|
|
|
- ContainerFromIndex(SelectedIndex) as IContentControl;
|
|
|
|
- SelectedContentTemplate = container?.ContentTemplate;
|
|
|
|
- SelectedContent = container?.Content;
|
|
|
|
|
|
+ container ??= ContainerFromIndex(SelectedIndex);
|
|
|
|
+ if (container != null)
|
|
|
|
+ {
|
|
|
|
+ _selectedItemSubscriptions = new CompositeDisposable(
|
|
|
|
+ container.GetObservable(ContentControl.ContentProperty).Subscribe(v => SelectedContent = v),
|
|
|
|
+ // Note how we fall back to our own ContentTemplate if the container doesn't specify one
|
|
|
|
+ container.GetObservable(ContentControl.ContentTemplateProperty).Subscribe(v => SelectedContentTemplate = v ?? ContentTemplate));
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -224,7 +226,7 @@ namespace Avalonia.Controls
|
|
|
|
|
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
|
{
|
|
{
|
|
- ItemsPresenterPart = e.NameScope.Get<ItemsPresenter>("PART_ItemsPresenter");
|
|
|
|
|
|
+ ItemsPresenterPart = e.NameScope.Find<ItemsPresenter>("PART_ItemsPresenter");
|
|
ItemsPresenterPart?.ApplyTemplate();
|
|
ItemsPresenterPart?.ApplyTemplate();
|
|
|
|
|
|
// Set TabNavigation to Once on the panel if not already set and
|
|
// Set TabNavigation to Once on the panel if not already set and
|
|
@@ -285,6 +287,16 @@ namespace Avalonia.Controls
|
|
{
|
|
{
|
|
RefreshContainers();
|
|
RefreshContainers();
|
|
}
|
|
}
|
|
|
|
+ else if (change.Property == ContentTemplateProperty)
|
|
|
|
+ {
|
|
|
|
+ var newTemplate = change.GetNewValue<IDataTemplate?>();
|
|
|
|
+ if (SelectedContentTemplate != newTemplate &&
|
|
|
|
+ ContainerFromIndex(SelectedIndex) is { } container &&
|
|
|
|
+ container.GetValue(ContentControl.ContentTemplateProperty) == null)
|
|
|
|
+ {
|
|
|
|
+ SelectedContentTemplate = newTemplate; // See also UpdateSelectedContent
|
|
|
|
+ }
|
|
|
|
+ }
|
|
else if (change.Property == KeyboardNavigation.TabOnceActiveElementProperty &&
|
|
else if (change.Property == KeyboardNavigation.TabOnceActiveElementProperty &&
|
|
ItemsPresenterPart?.Panel is { } panel)
|
|
ItemsPresenterPart?.Panel is { } panel)
|
|
{
|
|
{
|