chore: add avalonia TabControlSliding demo.

This commit is contained in:
NaBian committed 2025-02-11 21:34:21 +08:00
1 parent 045ced1c73
commit 3e2869865d
6 files changed
+423 -12

No files matched your search

@@ -9,6 +9,93 @@
ItemHeight="200"
MaxWidth="832"
ChildWrapping="Wrap">
<TabControl SelectedIndex="3"
Theme="{StaticResource TabControlSliding}">
<TabItem Header="Title1">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="1" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title22">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="2" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title333">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="3" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title4444">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="4" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title55555">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="5" />
</TextBlock>
</Border>
</TabItem>
</TabControl>
<TabControl Theme="{StaticResource TabControlSliding}"
TabStripPlacement="Bottom">
<TabItem Header="Title1">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="1" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title22"
IsEnabled="False">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="2" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title333">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="3" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title4444">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="4" />
</TextBlock>
</Border>
</TabItem>
<TabItem Header="Title55555">
<Border>
<TextBlock Theme="{StaticResource TextBlockLargeBold}">
<Run Text="Text" />
<Run Text="5" />
</TextBlock>
</Border>
</TabItem>
</TabControl>
<TabControl TabStripPlacement="Right">
<TabItem Header="Title1">
<Border>
@@ -0,0 +1,113 @@
using System;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Animation.Easings;
using Avalonia.Controls;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Styling;
using HandyControl.Tools;
namespace HandyControl.Controls;
[TemplatePart(Name = ElementSliding, Type = typeof(Layoutable))]
public class SlidingTabContainer : ContentControl
{
private const string ElementSliding = "PART_Sliding";
private Control? _sliding;
private TabControl? _tabControl;
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
OnSelectionChanged(null, null!);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
if (_tabControl is not null)
{
_tabControl.SelectionChanged -= OnSelectionChanged;
}
base.OnApplyTemplate(e);
_tabControl = VisualHelper.GetParent<TabControl>(this);
if (_tabControl is null)
{
return;
}
_tabControl.SelectionChanged += OnSelectionChanged;
_sliding = e.NameScope.Find<Control>(ElementSliding);
if (_sliding is null)
{
return;
}
if (IsLoaded)
{
OnSelectionChanged(null, null!);
}
}
protected override void OnSizeChanged(SizeChangedEventArgs e)
{
base.OnSizeChanged(e);
if (_tabControl is null)
{
return;
}
OnSelectionChanged(null, null!);
}
private void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
{
const int animationSpeed = 200;
if (_sliding is null || _tabControl?.SelectedItem is null || !IsLoaded)
{
return;
}
if (_tabControl.ContainerFromItem(_tabControl.SelectedItem) is not TabItem tabItem)
{
return;
}
var padding = Padding;
Point? offset = tabItem.TranslatePoint(new Point(-padding.Left, -padding.Top), this);
if (offset is null)
{
return;
}
new Animation
{
FillMode = FillMode.Forward,
Duration = TimeSpan.FromMilliseconds(animationSpeed),
Easing = new QuadraticEaseOut(),
Children =
{
new KeyFrame
{
Setters =
{
new Setter(WidthProperty, tabItem.Bounds.Width),
new Setter(HeightProperty, tabItem.Bounds.Height),
new Setter(TranslateTransform.XProperty, offset.Value.X),
new Setter(TranslateTransform.YProperty, offset.Value.Y),
},
Cue = new Cue(1),
}
}
}.RunAsync(_sliding);
}
}
@@ -402,4 +402,196 @@
Value="{DynamicResource TextIconBrush}" />
</ControlTheme>
<ControlTheme x:Key="SlidingTabContainerBaseStyle"
TargetType="hc:SlidingTabContainer">
<Setter Property="Focusable"
Value="False" />
<Setter Property="VerticalAlignment"
Value="Center" />
<Setter Property="HorizontalAlignment"
Value="Center" />
<Setter Property="Padding"
Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}">
<Panel>
<Border x:Name="PART_Sliding"
CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}"
Width="0"
Height="0"
Effect="{StaticResource EffectShadow1}"
Background="{Binding Path=(hc:TitleElement.Background),RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
<ContentPresenter Content="{TemplateBinding Content}" />
</Panel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</ControlTheme>
<ControlTheme x:Key="{x:Type hc:SlidingTabContainer}"
BasedOn="{StaticResource SlidingTabContainerBaseStyle}"
TargetType="hc:SlidingTabContainer" />
<ControlTheme x:Key="TabItemSliding"
TargetType="TabItem">
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="MinHeight"
Value="{StaticResource DefaultControlHeight}" />
<Setter Property="Padding"
Value="10,0" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="rootBorder"
CornerRadius="{TemplateBinding CornerRadius}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter Name="PART_ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
CornerRadius="{TemplateBinding CornerRadius}"
RecognizesAccessKey="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style Selector="^:disabled">
<Setter Property="Opacity"
Value="0.4" />
</Style>
<Style Selector="^:pointerover">
<Setter Property="Opacity"
Value="0.9" />
</Style>
<Style Selector="^:selected">
<Setter Property="Foreground"
Value="{Binding $self.(hc:TitleElement.Foreground)}" />
</Style>
</ControlTheme>
<ControlTheme x:Key="TabControlSliding"
TargetType="TabControl">
<Setter Property="BorderThickness"
Value="1" />
<Setter Property="BorderBrush"
Value="{DynamicResource BorderBrush}" />
<Setter Property="Background"
Value="{DynamicResource SecondaryRegionBrush}" />
<Setter Property="ItemContainerTheme"
Value="{StaticResource TabItemSliding}" />
<Setter Property="CornerRadius"
Value="{Binding $self.(hc:BorderElement.CornerRadius)}" />
<Setter Property="hc:BorderElement.CornerRadius"
Value="{StaticResource DefaultCornerRadius}" />
<Setter Property="hc:TitleElement.Background"
Value="{DynamicResource RegionBrush}" />
<Setter Property="hc:TitleElement.Foreground"
Value="{DynamicResource PrimaryTextBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<hc:SlidingTabContainer x:Name="tabContainer"
Background="{TemplateBinding Background}">
<ItemsPresenter Name="PART_ItemsPresenter"
ItemsPanel="{TemplateBinding ItemsPanel}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</hc:SlidingTabContainer>
<Border Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter Name="PART_SelectedContentHost"
Margin="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectedContent}"
ContentTemplate="{TemplateBinding SelectedContentTemplate}" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style Selector="^[TabStripPlacement=Top] /template/ hc|SlidingTabContainer#tabContainer">
<Setter Property="DockPanel.Dock"
Value="Top" />
<Setter Property="Margin"
Value="0,0,0,6" />
</Style>
<Style Selector="^[TabStripPlacement=Bottom] /template/ hc|SlidingTabContainer#tabContainer">
<Setter Property="DockPanel.Dock"
Value="Bottom" />
<Setter Property="Margin"
Value="0,6,0,0" />
</Style>
<Style Selector="^[TabStripPlacement=Left] /template/ hc|SlidingTabContainer#tabContainer">
<Setter Property="DockPanel.Dock"
Value="Left" />
<Setter Property="Margin"
Value="0,0,6,0" />
</Style>
<Style Selector="^[TabStripPlacement=Right] /template/ hc|SlidingTabContainer#tabContainer">
<Setter Property="DockPanel.Dock"
Value="Right" />
<Setter Property="Margin"
Value="6,0,0,0" />
</Style>
<Style Selector="^[TabStripPlacement=Left], ^[TabStripPlacement=Right]">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style Selector="^[TabStripPlacement=Top], ^[TabStripPlacement=Bottom]">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</ControlTheme>
</ResourceDictionary>
@@ -0,0 +1,17 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.VisualTree;
namespace HandyControl.Tools;
public static class VisualHelper
{
public static T? GetParent<T>(Visual? visual) where T : Visual =>
visual switch
{
null => default,
T t => t,
Window _ => null,
_ => GetParent<T>(visual.GetVisualParent())
};
}
@@ -9,7 +9,7 @@ public class TabItemCapsuleThemeSelector : ThemeSelector
{
public override ControlTheme? SelectTheme(object? item, AvaloniaObject? container)
{
if (container is not TabItem { Parent: TabControl tabControl } tabItem)
if (container is not TabItem tabItem || VisualHelper.GetParent<TabControl>(tabItem) is not { } tabControl)
{
return null;
}
@@ -37,14 +37,16 @@ public class SlidingTabContainer : ContentControl
_tabControl = VisualHelper.GetParent<System.Windows.Controls.TabControl>(this);
_sliding = GetTemplateChild(ElementSliding) as FrameworkElement;
if (_tabControl is not null)
if (_tabControl is null)
{
_tabControl.SelectionChanged += OnSelectionChanged;
return;
}
if (IsLoaded)
{
OnSelectionChanged(null, null);
}
_tabControl.SelectionChanged += OnSelectionChanged;
if (IsLoaded)
{
OnSelectionChanged(null, null);
}
}
@@ -80,31 +82,31 @@ public class SlidingTabContainer : ContentControl
if (useAnimation)
{
var storyboard = new Storyboard();
var easingFuction = new PowerEase
var easingFunction = new PowerEase
{
EasingMode = EasingMode.EaseOut,
};
var widthAnimation = AnimationHelper.CreateAnimation(tabWidth);
widthAnimation.EasingFunction = easingFuction;
widthAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(widthAnimation, _sliding);
Storyboard.SetTargetProperty(widthAnimation, new PropertyPath(WidthProperty.Name));
storyboard.Children.Add(widthAnimation);
var heightAnimation = AnimationHelper.CreateAnimation(tabHeight);
heightAnimation.EasingFunction = easingFuction;
heightAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(heightAnimation, _sliding);
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(HeightProperty.Name));
storyboard.Children.Add(heightAnimation);
var xAnimation = AnimationHelper.CreateAnimation(offset.X);
xAnimation.EasingFunction = easingFuction;
xAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(xAnimation, _sliding);
Storyboard.SetTargetProperty(xAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.X)"));
storyboard.Children.Add(xAnimation);
var yAnimation = AnimationHelper.CreateAnimation(offset.Y);
yAnimation.EasingFunction = easingFuction;
yAnimation.EasingFunction = easingFunction;
Storyboard.SetTarget(yAnimation, _sliding);
Storyboard.SetTargetProperty(yAnimation, new PropertyPath("(UIElement.RenderTransform).(TranslateTransform.Y)"));
storyboard.Children.Add(yAnimation);