202 lines
7.2 KiB
C#
202 lines
7.2 KiB
C#
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当属性值改变时触发
|
||
/// </summary>
|
||
/// <param name="d"></param>
|
||
/// <param name="e"></param>
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理滚动事件
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 添加获取 ScrollViewer 的辅助方法
|
||
/// </summary>
|
||
/// <param name="element"></param>
|
||
/// <returns></returns>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找父级 ListBox
|
||
/// </summary>
|
||
/// <param name="child"></param>
|
||
/// <returns></returns>
|
||
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<DataGrid>(listBox);
|
||
foreach (var dataGrid in dataGrids)
|
||
{
|
||
SetSynchronizeWithParent(dataGrid, true);
|
||
}
|
||
}
|
||
|
||
/// 查找所有指定类型的可视化子元素
|
||
private static IEnumerable<T> FindVisualChildren<T>(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<T>(child))
|
||
{
|
||
yield return childOfChild;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |