This commit is contained in:
ShrlAlgo
2025-08-20 12:10:35 +08:00
parent fcd306b0f7
commit 955a01f564
962 changed files with 7893 additions and 127784 deletions

View File

@@ -0,0 +1,138 @@
using System.Windows.Media.Animation;
namespace NeoUI.Behaviors;
/// <summary>
/// ListBoxSlideBehavior 类提供了附加属性 EnableSlideAnimation用于控制 ListBox 控件是否启用滑动动画效果。
/// 通过设置该属性,可以为 ListBox 添加平滑的滑动过渡效果,增强用户体验。
/// </summary>
public static class ListBoxSlideBehavior
{
/// <summary>
/// 获取指定对象的滑动动画启用状态。
/// </summary>
/// <param name="obj">要获取属性值的对象。</param>
/// <returns>如果为 true则表示启用了滑动动画否则为 false。</returns>
public static bool GetEnableSlideAnimation(DependencyObject obj)
{
return (bool)obj.GetValue(EnableSlideAnimationProperty);
}
/// <summary>
/// 设置指定对象的滑动动画启用状态。
/// </summary>
/// <param name="obj">要设置属性值的对象。</param>
/// <param name="value">一个布尔值,表示是否启用滑动动画。如果为 true则启用滑动动画否则不启用。</param>
public static void SetEnableSlideAnimation(DependencyObject obj, bool value)
{
obj.SetValue(EnableSlideAnimationProperty, value);
}
/// <summary>
/// 用于控制 ListBox 控件是否启用滑动动画效果的依赖属性。通过设置此属性,可以为 ListBox 添加平滑的滑动过渡效果,从而增强用户体验。
/// </summary>
public static readonly DependencyProperty EnableSlideAnimationProperty =
DependencyProperty.RegisterAttached("EnableSlideAnimation", typeof(bool), typeof(ListBoxSlideBehavior), new PropertyMetadata(false, OnEnableSlideAnimationChanged));
private static void OnEnableSlideAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not ListBox listBox) return;
if ((bool)e.NewValue)
{
listBox.Loaded += ListBox_Loaded;
listBox.SelectionChanged += ListBox_SelectionChanged;
}
else
{
listBox.Loaded -= ListBox_Loaded;
listBox.SelectionChanged -= ListBox_SelectionChanged;
}
}
private static void ListBox_Loaded(object sender, RoutedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox != null && listBox.IsVisible)
{
UpdateIndicator(listBox, false);
}
}
private static void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox != null && listBox.IsLoaded)
{
UpdateIndicator(listBox, true);
}
}
private static void UpdateIndicator(ListBox? listBox, bool useAnimation)
{
if (listBox?.SelectedItem == null) return;
var indicator = listBox.Template.FindName("PART_SelectionIndicator", listBox) as FrameworkElement;
var itemsPresenter = FindVisualChild<ItemsPresenter>(listBox);
if (indicator == null || itemsPresenter == null) return;
var selectedItemUI = listBox.ItemContainerGenerator.ContainerFromItem(listBox.SelectedItem) as FrameworkElement;
if (selectedItemUI == null) return;
var position = selectedItemUI.TransformToAncestor(itemsPresenter).Transform(new Point(0, 0));
indicator.Width = selectedItemUI.ActualWidth;
indicator.Height = selectedItemUI.ActualHeight;
var transform = indicator.RenderTransform as TranslateTransform;
// 如果 transform 为 null或者它是一个被冻结的只读实例
// 那么我们就需要创建一个新的、可写的实例来替换它。
if (transform == null || transform.IsFrozen)
{
transform = new TranslateTransform();
indicator.RenderTransform = transform;
}
if (useAnimation)
{
var animationX = new DoubleAnimation(position.X, new Duration(TimeSpan.FromMilliseconds(300)))
{
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
var animationY = new DoubleAnimation(position.Y, new Duration(TimeSpan.FromMilliseconds(300)))
{
EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut }
};
transform.BeginAnimation(TranslateTransform.XProperty, animationX);
transform.BeginAnimation(TranslateTransform.YProperty, animationY);
}
else
{
// 现在 transform 保证是可写的,所以这里不会再抛出异常
transform.X = position.X;
transform.Y = position.Y;
}
}
private static T? FindVisualChild<T>(DependencyObject? parent) where T : Visual
{
if (parent == null) return null;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T correctlyTyped)
{
return correctlyTyped;
}
var descendent = FindVisualChild<T>(child);
if (descendent != null)
{
return descendent;
}
}
return null;
}
}