diff --git a/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml b/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml
index 0fc880d..3236865 100644
--- a/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml
+++ b/samples/AtomUI.Demo.Desktop/ShowCase/TabControlShowCase.axaml
@@ -103,6 +103,13 @@
Tab 2
Tab 3
+
+
+ Tab 1
+ Tab 2
+ Tab 3
+
+
diff --git a/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs b/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs
index e80e976..872976e 100644
--- a/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs
+++ b/src/AtomUI.Controls/TabControl/CardTabControlTheme.cs
@@ -27,14 +27,12 @@ internal class CardTabControlTheme : BaseTabControlTheme
CreateTemplateParentBinding(alignWrapper, DockPanel.DockProperty, BaseTabControl.TabStripPlacementProperty);
CreateTemplateParentBinding(alignWrapper, Panel.MarginProperty,TabControl.TabStripMarginProperty);
- var cardTabStripContainer = new DockPanel()
+ var cardTabControlContainer = new TabsContainerPanel()
{
Name = TabsContainerPart,
};
- cardTabStripContainer.RegisterInNameScope(scope);
-
- TokenResourceBinder.CreateTokenBinding(cardTabStripContainer, StackPanel.SpacingProperty,
- TabControlResourceKey.CardGutter);
+ cardTabControlContainer.RegisterInNameScope(scope);
+ CreateTemplateParentBinding(cardTabControlContainer, TabsContainerPanel.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
var tabScrollViewer = new TabControlScrollViewer()
{
@@ -64,6 +62,7 @@ internal class CardTabControlTheme : BaseTabControlTheme
BorderThickness = new Thickness(1),
Icon = addTabIcon
};
+ DockPanel.SetDock(addTabButton, Dock.Right);
CreateTemplateParentBinding(addTabButton, IconButton.BorderThicknessProperty, CardTabControl.CardBorderThicknessProperty);
CreateTemplateParentBinding(addTabButton, IconButton.CornerRadiusProperty, CardTabControl.CardBorderRadiusProperty);
@@ -73,10 +72,10 @@ internal class CardTabControlTheme : BaseTabControlTheme
addTabButton.RegisterInNameScope(scope);
- cardTabStripContainer.Children.Add(addTabButton);
- cardTabStripContainer.Children.Add(tabScrollViewer);
+ cardTabControlContainer.TabScrollViewer = tabScrollViewer;
+ cardTabControlContainer.AddTabButton = addTabButton;
- alignWrapper.Children.Add(cardTabStripContainer);
+ alignWrapper.Children.Add(cardTabControlContainer);
container.Children.Add(alignWrapper);
}
diff --git a/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs b/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs
index ad4b8ff..ac930f3 100644
--- a/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs
+++ b/src/AtomUI.Controls/TabControl/TabScrollContentPresenter.cs
@@ -29,7 +29,6 @@ internal class TabScrollContentPresenter : ScrollContentPresenter, ICustomHitTes
var x = Offset.X;
var y = Offset.Y;
var delta = e.Delta;
-
if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
delta = new Vector(delta.Y, delta.X);
}
diff --git a/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs b/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs
index 0e54da5..2a851c4 100644
--- a/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs
+++ b/src/AtomUI.Controls/TabControl/TabStrip/BaseTabStripTheme.cs
@@ -12,6 +12,8 @@ internal class BaseTabStripTheme : BaseControlTheme
{
public const string FrameDecoratorPart = "PART_FrameDecorator";
public const string ItemsPresenterPart = "PART_ItemsPresenter";
+ public const string TabsContainerPart = "PART_TabsContainer";
+ public const string AlignWrapperPart = "PART_AlignWrapper";
public BaseTabStripTheme(Type targetType) : base(targetType) { }
@@ -50,9 +52,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
- var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
- itemsPresenterStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
- tabAlignCenterStyle.Add(itemsPresenterStyle);
+ var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
+ tabsContainerStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
+ tabAlignCenterStyle.Add(tabsContainerStyle);
}
topStyle.Add(tabAlignCenterStyle);
@@ -69,9 +71,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
- var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
- itemsPresenterStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
- tabAlignCenterStyle.Add(itemsPresenterStyle);
+ var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
+ tabsContainerStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
+ tabAlignCenterStyle.Add(tabsContainerStyle);
}
rightStyle.Add(tabAlignCenterStyle);
@@ -86,9 +88,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
- var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
- itemsPresenterStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
- tabAlignCenterStyle.Add(itemsPresenterStyle);
+ var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
+ tabsContainerStyle.Add(ItemsPresenter.HorizontalAlignmentProperty, HorizontalAlignment.Center);
+ tabAlignCenterStyle.Add(tabsContainerStyle);
}
bottomStyle.Add(tabAlignCenterStyle);
@@ -105,9 +107,9 @@ internal class BaseTabStripTheme : BaseControlTheme
// tabs 是否居中
var tabAlignCenterStyle = new Style(selector => selector.Nesting().PropertyEquals(TabStrip.TabAlignmentCenterProperty, true));
{
- var itemsPresenterStyle = new Style(selector => selector.Nesting().Template().Name(ItemsPresenterPart));
- itemsPresenterStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
- tabAlignCenterStyle.Add(itemsPresenterStyle);
+ var tabsContainerStyle = new Style(selector => selector.Nesting().Template().Name(TabsContainerPart));
+ tabsContainerStyle.Add(ItemsPresenter.VerticalAlignmentProperty, VerticalAlignment.Center);
+ tabAlignCenterStyle.Add(tabsContainerStyle);
}
leftStyle.Add(tabAlignCenterStyle);
diff --git a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs
index 0bf0431..afc6ea9 100644
--- a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs
+++ b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStrip.cs
@@ -127,37 +127,10 @@ public class CardTabStrip : BaseTabStrip, IControlCustomStyle
if (change.Property == SizeTypeProperty) {
HandleSizeTypeChanged();
} else if (change.Property == TabStripPlacementProperty) {
- SetupCardTabStripContainer();
HandleTabStripPlacementChanged();
}
}
- private void SetupCardTabStripContainer(Size finalSize)
- {
- if (_cardTabStripContainer is not null) {
- double addButtonOffset = 0;
- double markOffset = 0;
- if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
- addButtonOffset = _addTabButton?.Bounds.Right ?? 0;
- markOffset = finalSize.Width;
- if (addButtonOffset > markOffset) {
- _cardTabStripContainer.ColumnDefinitions[0].Width = GridLength.Star;
- } else {
- _cardTabStripContainer.ColumnDefinitions[0].Width = GridLength.Auto;
- }
- } else {
- addButtonOffset = _addTabButton?.Bounds.Bottom ?? 0;
- markOffset = finalSize.Height;
- if (addButtonOffset > markOffset) {
- _cardTabStripContainer.RowDefinitions[0].Height = GridLength.Star;
- } else {
- _cardTabStripContainer.RowDefinitions[0].Height = GridLength.Auto;
- }
- }
-
- }
- }
-
#region IControlCustomStyle 实现
void IControlCustomStyle.HandleTemplateApplied(INameScope scope)
@@ -170,7 +143,6 @@ public class CardTabStrip : BaseTabStrip, IControlCustomStyle
_addTabButton.Click += HandleAddButtonClicked;
}
HandleSizeTypeChanged();
- SetupCardTabStripContainer();
}
#endregion
@@ -193,56 +165,9 @@ public class CardTabStrip : BaseTabStrip, IControlCustomStyle
protected override Size ArrangeOverride(Size finalSize)
{
var size = base.ArrangeOverride(finalSize);
- SetupCardTabStripContainer(finalSize);
HandleTabStripPlacementChanged();
return size;
}
-
- private void SetupCardTabStripContainer()
- {
- if (TabStripPlacement == Dock.Top ||
- TabStripPlacement == Dock.Bottom) {
- if (_cardTabStripContainer is not null) {
- _cardTabStripContainer.Children.Clear();
- _cardTabStripContainer.RowDefinitions.Clear();
- _cardTabStripContainer.ColumnDefinitions = new ColumnDefinitions()
- {
- new ColumnDefinition(GridLength.Auto),
- new ColumnDefinition(GridLength.Auto),
- };
- }
-
- if (_tabScrollViewer is not null) {
- Grid.SetColumn(_tabScrollViewer, 0);
- }
-
- if (_addTabButton is not null) {
- Grid.SetColumn(_addTabButton, 1);
- }
-
- _cardTabStripContainer!.Children.Add(_tabScrollViewer!);
- _cardTabStripContainer.Children.Add(_addTabButton!);
- } else {
- if (_cardTabStripContainer is not null) {
- _cardTabStripContainer.Children.Clear();
- _cardTabStripContainer.ColumnDefinitions.Clear();
- _cardTabStripContainer.RowDefinitions = new RowDefinitions()
- {
- new RowDefinition(GridLength.Auto),
- new RowDefinition(GridLength.Auto),
- };
- }
- if (_tabScrollViewer is not null) {
- Grid.SetRow(_tabScrollViewer, 0);
- }
-
- if (_addTabButton is not null) {
- Grid.SetRow(_addTabButton, 1);
- }
- _cardTabStripContainer!.Children.Add(_tabScrollViewer!);
- _cardTabStripContainer.Children.Add(_addTabButton!);
- }
- }
private void HandleTabStripPlacementChanged()
{
diff --git a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs
index 73dd0ba..f3da7b1 100644
--- a/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs
+++ b/src/AtomUI.Controls/TabControl/TabStrip/CardTabStripTheme.cs
@@ -12,22 +12,24 @@ namespace AtomUI.Controls;
[ControlThemeProvider]
internal class CardTabStripTheme : BaseTabStripTheme
{
- public const string AddTabButtonPart = "PART_AddTabButton";
- public const string CardTabStripContainerPart = "PART_CardTabStripContainer";
+ public const string AddTabButtonPart = "PART_AddTabButton";
+ public const string CardTabStripContainerPart = "PART_CardTabStripContainer";
public const string CardTabStripScrollViewerPart = "PART_CardTabStripScrollViewer";
public CardTabStripTheme() : base(typeof(CardTabStrip)) { }
protected override void NotifyBuildControlTemplate(BaseTabStrip baseTabStrip, INameScope scope, Border container)
{
- var cardTabStripContainer = new Grid()
+ var alignWrapper = new Panel()
{
- Name = CardTabStripContainerPart,
+ Name = AlignWrapperPart
+ };
+ var cardTabStripContainer = new TabsContainerPanel()
+ {
+ Name = TabsContainerPart,
};
cardTabStripContainer.RegisterInNameScope(scope);
-
- TokenResourceBinder.CreateTokenBinding(cardTabStripContainer, StackPanel.SpacingProperty,
- TabControlResourceKey.CardGutter);
+ CreateTemplateParentBinding(cardTabStripContainer, TabsContainerPanel.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
var tabScrollViewer = new TabStripScrollViewer()
{
@@ -65,10 +67,13 @@ internal class CardTabStripTheme : BaseTabStripTheme
TokenResourceBinder.CreateGlobalResourceBinding(addTabButton, IconButton.BorderBrushProperty, GlobalResourceKey.ColorBorderSecondary);
addTabButton.RegisterInNameScope(scope);
+
+ cardTabStripContainer.TabScrollViewer = tabScrollViewer;
+ cardTabStripContainer.AddTabButton = addTabButton;
- cardTabStripContainer.Children.Add(tabScrollViewer);
- cardTabStripContainer.Children.Add(addTabButton);
- container.Child = cardTabStripContainer;
+ alignWrapper.Children.Add(cardTabStripContainer);
+
+ container.Child = alignWrapper;
}
private ItemsPresenter CreateTabStripContentPanel(INameScope scope)
diff --git a/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs b/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs
index 35cdf06..9696f77 100644
--- a/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs
+++ b/src/AtomUI.Controls/TabControl/TabStrip/TabStripTheme.cs
@@ -17,12 +17,23 @@ internal class TabStripTheme : BaseTabStripTheme
protected override void NotifyBuildControlTemplate(BaseTabStrip baseTabStrip, INameScope scope, Border container)
{
- var tabScrollViewer = new TabStripScrollViewer();
+ var alignWrapper = new Panel()
+ {
+ Name = AlignWrapperPart
+ };
+ alignWrapper.RegisterInNameScope(scope);
+
+ var tabScrollViewer = new TabStripScrollViewer()
+ {
+ Name = TabsContainerPart
+ };
CreateTemplateParentBinding(tabScrollViewer, BaseTabScrollViewer.TabStripPlacementProperty, TabStrip.TabStripPlacementProperty);
var contentPanel = CreateTabStripContentPanel(scope);
tabScrollViewer.Content = contentPanel;
tabScrollViewer.TabStrip = baseTabStrip;
- container.Child = tabScrollViewer;
+
+ alignWrapper.Children.Add(tabScrollViewer);
+ container.Child = alignWrapper;
}
private Panel CreateTabStripContentPanel(INameScope scope)
diff --git a/src/AtomUI.Controls/TabControl/TabsContainerPanel.cs b/src/AtomUI.Controls/TabControl/TabsContainerPanel.cs
new file mode 100644
index 0000000..1a36201
--- /dev/null
+++ b/src/AtomUI.Controls/TabControl/TabsContainerPanel.cs
@@ -0,0 +1,115 @@
+using Avalonia;
+using Avalonia.Controls;
+
+namespace AtomUI.Controls;
+
+internal class TabsContainerPanel : Panel
+{
+ #region 公共属性定义
+
+ public static readonly DirectProperty TabScrollViewerProperty =
+ AvaloniaProperty.RegisterDirect(nameof(TabScrollViewer),
+ o => o.TabScrollViewer,
+ (o, v) => o.TabScrollViewer = v);
+
+ private BaseTabScrollViewer? _tabScrollViewer;
+ public BaseTabScrollViewer? TabScrollViewer
+ {
+ get => _tabScrollViewer;
+ set => SetAndRaise(TabScrollViewerProperty, ref _tabScrollViewer, value);
+ }
+
+ public static readonly DirectProperty AddTabButtonProperty =
+ AvaloniaProperty.RegisterDirect(nameof(AddTabButton),
+ o => o.AddTabButton,
+ (o, v) => o.AddTabButton = v);
+
+ private IconButton? _addTabButton;
+ public IconButton? AddTabButton
+ {
+ get => _addTabButton;
+ set => SetAndRaise(AddTabButtonProperty, ref _addTabButton, value);
+ }
+
+ #endregion
+
+ #region 内部属性定义
+
+ internal static readonly DirectProperty TabStripPlacementProperty =
+ AvaloniaProperty.RegisterDirect(nameof(TabStripPlacement),
+ o => o.TabStripPlacement,
+ (o, v) => o.TabStripPlacement = v);
+
+ private Dock _tabStripPlacement;
+
+ internal Dock TabStripPlacement
+ {
+ get => _tabStripPlacement;
+ set => SetAndRaise(TabStripPlacementProperty, ref _tabStripPlacement, value);
+ }
+
+ #endregion
+
+ static TabsContainerPanel()
+ {
+ AffectsMeasure(TabScrollViewerProperty, AddTabButtonProperty);
+ AffectsArrange(TabStripPlacementProperty);
+ }
+
+ protected override Size ArrangeOverride(Size arrangeSize)
+ {
+ // TODO 暂时不做验证,默认认为两个元素都存在
+ // 理论上这里要报错,但是我们是内部使用
+ if (_tabScrollViewer is not null && _addTabButton is not null) {
+ if (TabStripPlacement == Dock.Top || TabStripPlacement == Dock.Bottom) {
+ var scrollViewerDesiredWidth = _tabScrollViewer.DesiredSize.Width;
+ var addTabButtonDesiredWidth = _addTabButton.DesiredSize.Width;
+ var totalDesiredWidth = scrollViewerDesiredWidth + addTabButtonDesiredWidth;
+ if (totalDesiredWidth > arrangeSize.Width) {
+ _tabScrollViewer.Arrange(new Rect(new Point(0, 0), new Size(arrangeSize.Width - addTabButtonDesiredWidth, arrangeSize.Height)));
+ _addTabButton.Arrange(new Rect(new Point(arrangeSize.Width - addTabButtonDesiredWidth, 0), _addTabButton.DesiredSize));
+ } else {
+ _tabScrollViewer.Arrange(new Rect(new Point(0, 0), _tabScrollViewer.DesiredSize));
+ _addTabButton.Arrange(new Rect(new Point(scrollViewerDesiredWidth, 0), _addTabButton.DesiredSize));
+ }
+ } else {
+ var scrollViewerDesiredHeight = _tabScrollViewer.DesiredSize.Height;
+ var addTabButtonDesiredHeight = _addTabButton.DesiredSize.Height;
+ var totalDesiredHeight = scrollViewerDesiredHeight + addTabButtonDesiredHeight;
+ if (totalDesiredHeight > arrangeSize.Height) {
+ _tabScrollViewer.Arrange(new Rect(new Point(0, 0), new Size(arrangeSize.Width, arrangeSize.Height - addTabButtonDesiredHeight)));
+ _addTabButton.Arrange(new Rect(new Point(0, arrangeSize.Width - addTabButtonDesiredHeight), _addTabButton.DesiredSize));
+ } else {
+ _tabScrollViewer.Arrange(new Rect(new Point(0, 0), _tabScrollViewer.DesiredSize));
+ _addTabButton.Arrange(new Rect(new Point(scrollViewerDesiredHeight, 0), _addTabButton.DesiredSize));
+ }
+ }
+ }
+
+ return (arrangeSize);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ if (change.Property == TabScrollViewerProperty) {
+ var oldScrollViewer = change.GetOldValue();
+ if (oldScrollViewer is not null) {
+ Children.Remove(oldScrollViewer);
+ }
+
+ if (TabScrollViewer is not null) {
+ Children.Add(TabScrollViewer);
+ }
+ } else if (change.Property == AddTabButtonProperty) {
+ var oldAddTabButton = change.GetOldValue();
+ if (oldAddTabButton is not null) {
+ Children.Remove(oldAddTabButton);
+ }
+
+ if (AddTabButton is not null) {
+ Children.Add(AddTabButton);
+ }
+ }
+ }
+}
\ No newline at end of file