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列表框在拖动时自动滚动的主要内容,如果未能解决你的问题,请参考以下文章

wpf 一行图片无缝的滚动

在 WPF ListView 中,如何防止自动滚动?

jQuery UI,可拖动,可放置,自动滚动

WPF拖动DataGrid滚动条时内容混乱的解决方法

jQueryUI 拖动时自动滚动

ScrollViewer 鼠标滚轮不滚动