using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; namespace Sinvo.EplanHpD.Plugin.WPFUI.Utils { public static class ScrollSynchronizer { // 附加属性定义 public static readonly DependencyProperty SynchronizeWithParentProperty = DependencyProperty.RegisterAttached( "SynchronizeWithParent", typeof(bool), typeof(ScrollSynchronizer), new PropertyMetadata(false, OnSynchronizeWithParentChanged)); // 为 ListBox 添加自动寻找内部 DataGrid 的附加属性 public static readonly DependencyProperty AutoSynchronizeChildrenProperty = DependencyProperty.RegisterAttached( "AutoSynchronizeChildren", typeof(bool), typeof(ScrollSynchronizer), new PropertyMetadata(false, OnAutoSynchronizeChildrenChanged)); // Getter 和 Setter public static bool GetSynchronizeWithParent(DependencyObject obj) { return (bool)obj.GetValue(SynchronizeWithParentProperty); } public static void SetSynchronizeWithParent(DependencyObject obj, bool value) { obj.SetValue(SynchronizeWithParentProperty, value); } /// /// 当属性值改变时触发 /// /// /// private static void OnSynchronizeWithParentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is UIElement element) { if ((bool)e.NewValue) { // 添加事件处理 element.PreviewMouseWheel += Element_PreviewMouseWheel; } else { // 移除事件处理 element.PreviewMouseWheel -= Element_PreviewMouseWheel; } } } /// /// 处理滚动事件 /// /// /// private static void Element_PreviewMouseWheel(object sender, MouseWheelEventArgs e) { if (sender is UIElement element) { // 向上查找父级 ListBox var listBox = FindParentListBox(element); if (listBox != null) { // 直接获取 ListBox 的 ScrollViewer ScrollViewer scrollViewer = GetScrollViewer(listBox); if (scrollViewer != null) { // 直接控制滚动位置 if (e.Delta < 0) { scrollViewer.LineDown(); } else { scrollViewer.LineUp(); } // 标记事件已处理 e.Handled = true; } else { // 如果找不到 ScrollViewer,回退到事件传递 var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta); eventArg.RoutedEvent = UIElement.MouseWheelEvent; eventArg.Source = sender; listBox.RaiseEvent(eventArg); e.Handled = true; } } } } /// /// 添加获取 ScrollViewer 的辅助方法 /// /// /// private static ScrollViewer GetScrollViewer(DependencyObject element) { if (element is ScrollViewer scrollViewer) return scrollViewer; // 尝试从控件模板中查找 ScrollViewer ScrollViewer result = null; for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element) && result == null; i++) { var child = VisualTreeHelper.GetChild(element, i); result = GetScrollViewer(child); // 递归查找 } return result; } /// /// 查找父级 ListBox /// /// /// private static ListBox FindParentListBox(DependencyObject child) { var parent = VisualTreeHelper.GetParent(child); while (parent != null && !(parent is ListBox)) { parent = VisualTreeHelper.GetParent(parent); } return parent as ListBox; } public static bool GetAutoSynchronizeChildren(DependencyObject obj) { return (bool)obj.GetValue(AutoSynchronizeChildrenProperty); } public static void SetAutoSynchronizeChildren(DependencyObject obj, bool value) { obj.SetValue(AutoSynchronizeChildrenProperty, value); } private static void OnAutoSynchronizeChildrenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is ListBox listBox && (bool)e.NewValue) { // 监听 ListBox 的 Loaded 事件 listBox.Loaded += ListBox_Loaded; // 监听 ItemContainerGenerator 状态变化,处理动态加载的项 listBox.ItemContainerGenerator.StatusChanged += (s, args) => { if (listBox.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) { AttachToAllDataGrids(listBox); } }; } } private static void ListBox_Loaded(object sender, RoutedEventArgs e) { if (sender is ListBox listBox) { AttachToAllDataGrids(listBox); } } private static void AttachToAllDataGrids(ListBox listBox) { var dataGrids = FindVisualChildren(listBox); foreach (var dataGrid in dataGrids) { SetSynchronizeWithParent(dataGrid, true); } } /// 查找所有指定类型的可视化子元素 private static IEnumerable FindVisualChildren(DependencyObject obj) where T : DependencyObject { if (obj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if (child != null && child is T) { yield return (T)child; } foreach (T childOfChild in FindVisualChildren(child)) { yield return childOfChild; } } } } } }