Files
29WxOrder/WxOrder.Framework/Commons/ScrollViewerBehavior.cs
T

110 lines
2.6 KiB
C#

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace WxOrder.Framework.Commons
{
/// <summary>
/// 让页面级 ScrollViewer 在鼠标悬停在子控件(如 DataGrid、图表等)上时也能随鼠标滚轮滚动。
/// 用法:在 ScrollViewer 上设置 common:ScrollViewerBehavior.FixMouseWheel="True"。
/// </summary>
public static class ScrollViewerBehavior
{
public static readonly DependencyProperty FixMouseWheelProperty =
DependencyProperty.RegisterAttached(
"FixMouseWheel",
typeof(bool),
typeof(ScrollViewerBehavior),
new PropertyMetadata(false, OnFixMouseWheelChanged));
public static bool GetFixMouseWheel(DependencyObject obj)
=> (bool)obj.GetValue(FixMouseWheelProperty);
public static void SetFixMouseWheel(DependencyObject obj, bool value)
=> obj.SetValue(FixMouseWheelProperty, value);
private static void OnFixMouseWheelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is ScrollViewer scrollViewer))
{
return;
}
if ((bool)e.NewValue)
{
scrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
}
else
{
scrollViewer.PreviewMouseWheel -= OnPreviewMouseWheel;
}
}
private static void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Handled || !(sender is ScrollViewer scrollViewer) || scrollViewer.ScrollableHeight <= 0)
{
return;
}
if (!(e.OriginalSource is DependencyObject original))
{
return;
}
// 从鼠标所在控件向上查找,确认它属于当前 ScrollViewer
var current = original;
var inside = false;
while (current != null)
{
if (ReferenceEquals(current, scrollViewer))
{
inside = true;
break;
}
// 若鼠标位于一个能自行纵向滚动的内部 ScrollViewer 上(例如下拉列表、
// 启用了滚动的表格),则交给内部处理,避免抢走它的滚动。
if (current is ScrollViewer inner &&
inner.ComputedVerticalScrollBarVisibility == Visibility.Visible &&
inner.ScrollableHeight > 0)
{
return;
}
current = VisualTreeHelper.GetParent(current);
}
if (!inside)
{
return;
}
// 按系统默认的滚轮行数滚动当前页面
var lines = (int)(e.Delta / Mouse.MouseWheelDeltaForOneLine * SystemParameters.WheelScrollLines);
if (lines == 0)
{
lines = e.Delta > 0 ? 1 : -1;
}
if (lines < 0)
{
for (var i = 0; i < -lines; i++)
{
scrollViewer.LineDown();
}
}
else
{
for (var i = 0; i < lines; i++)
{
scrollViewer.LineUp();
}
}
e.Handled = true;
}
}
}