实现 BaseTabControl

完成基础的 BaseTabControl 类,支持两个风格
This commit is contained in:
polarboy
2024-08-03 13:17:07 +08:00
parent d1784e6e9e
commit fe7ea95236
2 changed files with 265 additions and 2 deletions
@@ -1,6 +1,126 @@
namespace AtomUI.Controls;
using AtomUI.Data;
using AtomUI.Theme.Data;
using AtomUI.Theme.Styling;
using AtomUI.Utils;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.LogicalTree;
using Avalonia.Media;
public class BaseTabControl
namespace AtomUI.Controls;
using AvaloniaTabControl = Avalonia.Controls.TabControl;
public class BaseTabControl : AvaloniaTabControl
{
public const string TopPC = ":top";
public const string RightPC = ":right";
public const string BottomPC = ":bottom";
public const string LeftPC = ":left";
private static readonly FuncTemplate<Panel?> DefaultPanel =
new(() => new StackPanel());
#region 公共属性定义
public static readonly StyledProperty<SizeType> SizeTypeProperty =
AvaloniaProperty.Register<BaseTabControl, SizeType>(nameof(SizeType), SizeType.Middle);
public static readonly StyledProperty<bool> TabAlignmentCenterProperty =
AvaloniaProperty.Register<BaseTabControl, bool>(nameof(TabAlignmentCenter), false);
public SizeType SizeType
{
get => GetValue(SizeTypeProperty);
set => SetValue(SizeTypeProperty, value);
}
public bool TabAlignmentCenter
{
get => GetValue(TabAlignmentCenterProperty);
set => SetValue(TabAlignmentCenterProperty, value);
}
#endregion
private Border? _frameDecorator;
static BaseTabControl()
{
ItemsPanelProperty.OverrideDefaultValue<BaseTabControl>(DefaultPanel);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
_frameDecorator = e.NameScope.Find<Border>(BaseTabControlTheme.FrameDecoratorPart);
SetupBorderBinding();
}
private void SetupBorderBinding()
{
if (_frameDecorator is not null) {
TokenResourceBinder.CreateTokenBinding(this, BorderThicknessProperty, GlobalResourceKey.BorderThickness, BindingPriority.Template,
new RenderScaleAwareThicknessConfigure(this));
}
}
protected override void PrepareContainerForItemOverride(Control container, object? item, int index)
{
base.PrepareContainerForItemOverride(container, item, index);
if (container is TabItem tabItem) {
BindUtils.RelayBind(this, SizeTypeProperty, tabItem, TabItem.SizeTypeProperty);
}
}
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
UpdatePseudoClasses();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == TabStripPlacementProperty) {
UpdatePseudoClasses();
}
}
private void UpdatePseudoClasses()
{
PseudoClasses.Set(TopPC, TabStripPlacement == Dock.Top);
PseudoClasses.Set(RightPC, TabStripPlacement == Dock.Right);
PseudoClasses.Set(BottomPC, TabStripPlacement == Dock.Bottom);
PseudoClasses.Set(LeftPC, TabStripPlacement == Dock.Left);
}
public override void Render(DrawingContext context)
{
Point startPoint = default;
Point endPoint = default;
var borderThickness = BorderThickness.Left;
var offsetDelta = borderThickness / 2;
if (TabStripPlacement == Dock.Top) {
startPoint = new Point(0, Bounds.Height - offsetDelta);
endPoint = new Point(Bounds.Width, Bounds.Height - offsetDelta);
} else if (TabStripPlacement == Dock.Right) {
startPoint = new Point(offsetDelta, 0);
endPoint = new Point(offsetDelta, Bounds.Height);
} else if (TabStripPlacement == Dock.Bottom) {
startPoint = new Point(0, offsetDelta);
endPoint = new Point(Bounds.Width, offsetDelta);
} else {
startPoint = new Point(Bounds.Width - offsetDelta, 0);
endPoint = new Point(Bounds.Width - offsetDelta, Bounds.Height);
}
using var optionState = context.PushRenderOptions(new RenderOptions()
{
EdgeMode = EdgeMode.Aliased
});
context.DrawLine(new Pen(BorderBrush, borderThickness), startPoint, endPoint);
}
}
@@ -0,0 +1,143 @@
using AtomUI.Theme;
using AtomUI.Theme.Styling;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Layout;
using Avalonia.Styling;
namespace AtomUI.Controls;
internal class BaseTabControlTheme : BaseControlTheme
{
public const string FrameDecoratorPart = "PART_FrameDecorator";
public const string ItemsPresenterPart = "PART_ItemsPresenter";
public const string MainLayoutContainerPart = "PART_MainLayoutContainer";
public const string SelectedContentHostPart = "PART_SelectedContentHost";
public BaseTabControlTheme(Type targetType) : base(targetType) { }
protected override IControlTemplate BuildControlTemplate()
{
return new FuncControlTemplate<BaseTabControl>((baseTabControl, scope) =>
{
var frameDecorator = new Border()
{
Name = FrameDecoratorPart
};
frameDecorator.RegisterInNameScope(scope);
var layoutContainer = new DockPanel()
{
Name = MainLayoutContainerPart
};
NotifyBuildTabStripTemplate(baseTabControl, scope, layoutContainer);
NotifyBuildContentPresenter(baseTabControl, scope, layoutContainer);
return frameDecorator;
});
}
protected virtual void NotifyBuildTabStripTemplate(BaseTabControl baseTabControl, INameScope scope, DockPanel container)
{
}
protected virtual void NotifyBuildContentPresenter(BaseTabControl baseTabControl, INameScope scope, DockPanel container)
{
var contentPresenter = new ContentPresenter
{
Name = SelectedContentHostPart
};
contentPresenter.RegisterInNameScope(scope);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.MarginProperty, BaseTabControl.PaddingProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentProperty, BaseTabControl.SelectedContentProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.ContentTemplateProperty, BaseTabControl.SelectedContentTemplateProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.HorizontalContentAlignmentProperty, BaseTabControl.HorizontalContentAlignmentProperty);
CreateTemplateParentBinding(contentPresenter, ContentPresenter.VerticalContentAlignmentProperty, BaseTabControl.VerticalContentAlignmentProperty);
container.Children.Add(contentPresenter);
}
protected override void BuildStyles()
{
base.BuildStyles();
var commonStyle = new Style(selector => selector.Nesting());
commonStyle.Add(BaseTabControl.BorderBrushProperty, GlobalResourceKey.ColorBorderSecondary);
// 设置 items presenter 是否居中
// 分为上、右、下、左
{
// 上
var topStyle = new Style(selector => selector.Nesting().Class(BaseTabControl.TopPC));
topStyle.Add(BaseTabControl.HorizontalAlignmentProperty, HorizontalAlignment.Stretch);
topStyle.Add(BaseTabControl.VerticalAlignmentProperty, VerticalAlignment.Top);
// 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);
}
topStyle.Add(tabAlignCenterStyle);
commonStyle.Add(topStyle);
}
{
// 右
var rightStyle = new Style(selector => selector.Nesting().Class(BaseTabControl.RightPC));
rightStyle.Add(BaseTabControl.HorizontalAlignmentProperty, HorizontalAlignment.Left);
rightStyle.Add(BaseTabControl.VerticalAlignmentProperty, VerticalAlignment.Stretch);
// 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);
}
rightStyle.Add(tabAlignCenterStyle);
commonStyle.Add(rightStyle);
}
{
// 下
var bottomStyle = new Style(selector => selector.Nesting().Class(BaseTabControl.BottomPC));
bottomStyle.Add(BaseTabControl.HorizontalAlignmentProperty, HorizontalAlignment.Stretch);
bottomStyle.Add(BaseTabControl.VerticalAlignmentProperty, VerticalAlignment.Top);
// 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);
}
bottomStyle.Add(tabAlignCenterStyle);
commonStyle.Add(bottomStyle);
}
{
// 左
var leftStyle = new Style(selector => selector.Nesting().Class(BaseTabControl.LeftPC));
leftStyle.Add(BaseTabControl.HorizontalAlignmentProperty, HorizontalAlignment.Left);
leftStyle.Add(BaseTabControl.VerticalAlignmentProperty, VerticalAlignment.Stretch);
// 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);
}
leftStyle.Add(tabAlignCenterStyle);
commonStyle.Add(leftStyle);
}
Add(commonStyle);
}
}