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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|