Files
ShrlAlgoToolkit/NeuWPF/NeoUI/Behaviors/ListBoxSlideBehavior.cs
ShrlAlgo 955a01f564 整理
2025-08-20 12:10:35 +08:00

139 lines
5.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}