using System.Windows.Media.Animation; namespace NeoUI.Behaviors; /// /// ListBoxSlideBehavior 类提供了附加属性 EnableSlideAnimation,用于控制 ListBox 控件是否启用滑动动画效果。 /// 通过设置该属性,可以为 ListBox 添加平滑的滑动过渡效果,增强用户体验。 /// public static class ListBoxSlideBehavior { /// /// 获取指定对象的滑动动画启用状态。 /// /// 要获取属性值的对象。 /// 如果为 true,则表示启用了滑动动画;否则为 false。 public static bool GetEnableSlideAnimation(DependencyObject obj) { return (bool)obj.GetValue(EnableSlideAnimationProperty); } /// /// 设置指定对象的滑动动画启用状态。 /// /// 要设置属性值的对象。 /// 一个布尔值,表示是否启用滑动动画。如果为 true,则启用滑动动画;否则不启用。 public static void SetEnableSlideAnimation(DependencyObject obj, bool value) { obj.SetValue(EnableSlideAnimationProperty, value); } /// /// 用于控制 ListBox 控件是否启用滑动动画效果的依赖属性。通过设置此属性,可以为 ListBox 添加平滑的滑动过渡效果,从而增强用户体验。 /// 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(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(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(child); if (descendent != null) { return descendent; } } return null; } }