From 3e2869865d37f936b34d5639524fbc65482b43e0 Mon Sep 17 00:00:00 2001
From: NaBian <836904362@qq.com>
Date: Tue, 11 Feb 2025 21:34:21 +0800
Subject: [PATCH] chore: add avalonia TabControlSliding demo.
---
.../Styles/NativeTabControlDemo.axaml | 87 ++++++++
.../TabControl/SlidingTabContainer.cs | 113 +++++++++++
.../Themes/Styles/TabControl.axaml | 192 ++++++++++++++++++
.../Tools/Helper/VisualHelper.cs | 17 ++
.../TabItemCapsuleStyleSelector.cs | 2 +-
.../TabControl/SlidingTabContainer.cs | 24 ++-
6 files changed, 423 insertions(+), 12 deletions(-)
create mode 100644 src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs
create mode 100644 src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs
diff --git a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml
index 13e7a7b6..3cd28289 100644
--- a/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml
+++ b/src/Avalonia/HandyControlDemo_Avalonia/UserControl/Styles/NativeTabControlDemo.axaml
@@ -9,6 +9,93 @@
ItemHeight="200"
MaxWidth="832"
ChildWrapping="Wrap">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs b/src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs
new file mode 100644
index 00000000..8c9174de
--- /dev/null
+++ b/src/Avalonia/HandyControl_Avalonia/Controls/TabControl/SlidingTabContainer.cs
@@ -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(this);
+ if (_tabControl is null)
+ {
+ return;
+ }
+
+ _tabControl.SelectionChanged += OnSelectionChanged;
+
+ _sliding = e.NameScope.Find(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);
+ }
+}
diff --git a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml
index cf180748..57cf7619 100644
--- a/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml
+++ b/src/Avalonia/HandyControl_Avalonia/Themes/Styles/TabControl.axaml
@@ -402,4 +402,196 @@
Value="{DynamicResource TextIconBrush}" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs b/src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs
new file mode 100644
index 00000000..9e3aab41
--- /dev/null
+++ b/src/Avalonia/HandyControl_Avalonia/Tools/Helper/VisualHelper.cs
@@ -0,0 +1,17 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.VisualTree;
+
+namespace HandyControl.Tools;
+
+public static class VisualHelper
+{
+ public static T? GetParent(Visual? visual) where T : Visual =>
+ visual switch
+ {
+ null => default,
+ T t => t,
+ Window _ => null,
+ _ => GetParent(visual.GetVisualParent())
+ };
+}
diff --git a/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs b/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs
index 7faa5930..40c9e78b 100644
--- a/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs
+++ b/src/Avalonia/HandyControl_Avalonia/Tools/StyleSelector/TabItemCapsuleStyleSelector.cs
@@ -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(tabItem) is not { } tabControl)
{
return null;
}
diff --git a/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs b/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs
index edb75ebd..afb1526e 100644
--- a/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs
+++ b/src/Shared/HandyControl_Shared/Controls/TabControl/SlidingTabContainer.cs
@@ -37,14 +37,16 @@ public class SlidingTabContainer : ContentControl
_tabControl = VisualHelper.GetParent(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);