WPF列表框在拖动时自动滚动
Posted
技术标签:
【中文标题】WPF列表框在拖动时自动滚动【英文标题】:WPF Listbox auto scroll while dragging 【发布时间】:2010-11-21 22:08:08 【问题描述】:我有一个带有ListBox
的 WPF 应用程序。拖动机制已经实现,但是当列表太长并且我想将项目移动到不可见的位置时,我不能。
例如,屏幕显示 10 个项目。我有20个项目。如果我想将最后一个项目拖到第一个位置,我必须拖到顶部并放下。向上滚动并再次拖动。
如何使ListBox
自动滚动?
【问题讨论】:
【参考方案1】:知道了。使用ListBox
的事件DragOver
,使用找到的函数here 来获取列表框的scrollviewer
,然后它只是有点与位置的杂耍。
private void ItemsList_DragOver(object sender, System.Windows.DragEventArgs e)
ListBox li = sender as ListBox;
ScrollViewer sv = FindVisualChild<ScrollViewer>(ItemsList);
double tolerance = 10;
double verticalPos = e.GetPosition(li).Y;
double offset = 3;
if (verticalPos < tolerance) // Top of visible list?
sv.ScrollToVerticalOffset(sv.VerticalOffset - offset); //Scroll up.
else if (verticalPos > li.ActualHeight - tolerance) //Bottom of visible list?
sv.ScrollToVerticalOffset(sv.VerticalOffset + offset); //Scroll down.
public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
// Search immediate children first (breadth-first)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
return null;
【讨论】:
我试过你的方法,效果很好。但是,当在同一个列表中拖动对象时,在拖放后,它会返回到我想查看拖放项的原始对象。你有这个吗,你改正了吗? @DavidBrunelle 我不记得了,抱歉。 +1 很好的答案,虽然这是深度优先搜索,而不是所示的广度优先搜索。 我的“FindVisualChild”从不返回 ScrollViewer,但我将 ListBox 放在 XAML 中的 ScrollViewer 中,并改用“FindVisualParent”来解决问题。 @miriyo 嗨。我正在尝试类似的东西。可以发一下代码吗?【参考方案2】:基于此,我创建了一个Attached Behavior,可以像这样轻松使用 -
<ListView
xmlns:WpfExtensions="clr-namespace:WpfExtensions"
WpfExtensions:DragDropExtension.ScrollOnDragDrop="True"
这是附加行为的代码 -
/// <summary>
/// Provides extended support for drag drop operation
/// </summary>
public static class DragDropExtension
public static readonly DependencyProperty ScrollOnDragDropProperty =
DependencyProperty.RegisterAttached("ScrollOnDragDrop",
typeof(bool),
typeof(DragDropExtension),
new PropertyMetadata(false, HandleScrollOnDragDropChanged));
public static bool GetScrollOnDragDrop(DependencyObject element)
if (element == null)
throw new ArgumentNullException("element");
return (bool)element.GetValue(ScrollOnDragDropProperty);
public static void SetScrollOnDragDrop(DependencyObject element, bool value)
if (element == null)
throw new ArgumentNullException("element");
element.SetValue(ScrollOnDragDropProperty, value);
private static void HandleScrollOnDragDropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
FrameworkElement container = d as FrameworkElement;
if (d == null)
Debug.Fail("Invalid type!");
return;
Unsubscribe(container);
if (true.Equals(e.NewValue))
Subscribe(container);
private static void Subscribe(FrameworkElement container)
container.PreviewDragOver += OnContainerPreviewDragOver;
private static void OnContainerPreviewDragOver(object sender, DragEventArgs e)
FrameworkElement container = sender as FrameworkElement;
if (container == null)
return;
ScrollViewer scrollViewer = GetFirstVisualChild<ScrollViewer>(container);
if (scrollViewer == null)
return;
double tolerance = 60;
double verticalPos = e.GetPosition(container).Y;
double offset = 20;
if (verticalPos < tolerance) // Top of visible list?
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - offset); //Scroll up.
else if (verticalPos > container.ActualHeight - tolerance) //Bottom of visible list?
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); //Scroll down.
private static void Unsubscribe(FrameworkElement container)
container.PreviewDragOver -= OnContainerPreviewDragOver;
private static T GetFirstVisualChild<T>(DependencyObject depObj) where T : DependencyObject
if (depObj != null)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
return (T)child;
T childItem = GetFirstVisualChild<T>(child);
if (childItem != null)
return childItem;
return null;
【讨论】:
非常好的解决方案。如果您想要平滑滚动,请不要忘记可以将“ScrollViewer.CanContentScroll="False"" 放在 ListBox/ListView 上。 @Pak 还值得一提的是,当您将 CanContentScroll 设置为 false 时,您将同时禁用虚拟化。以上是关于WPF列表框在拖动时自动滚动的主要内容,如果未能解决你的问题,请参考以下文章