ListBox拖放自动滚动问题

本文关键字:滚动 问题 拖放 ListBox | 更新日期: 2023-09-27 18:00:28

我已经成功地使用在ListBox中实现了拖放

Bea Stollnitz-如何在数据绑定的ItemsControls之间拖放项目:http://www.zagstudio.com/blog/488#.U15ozvldU9Y

但是,当列表有一个滚动条,而我在列表的末尾时,当我拖动项目时,ListBox不会自动滚动
我该如何融入这种行为?

编辑:当我超过列表的底部边界时,我希望它滚动,这样我就可以放在较低的项目上?

ListBox拖放自动滚动问题

我使用附加的属性来实现这一点,该属性添加了在拖动时鼠标向控件顶部或底部移动时滚动控件的功能。以下是所附的属性类:-

public class DragDropAttProps
{
    public static readonly DependencyProperty ScrollOnDragDropProperty =
        DependencyProperty.RegisterAttached(
            "ScrollOnDragDrop",
            typeof(bool),
            typeof(DragDropAttProps),
            new PropertyMetadata(false, HandleScrollOnDragDropChanged));
    public static bool GetScrollOnDragDrop(DependencyObject element)
    {
        return (bool)element.GetValue(ScrollOnDragDropProperty);
    }
    public static void SetScrollOnDragDrop(DependencyObject element, bool value)
    {
        element.SetValue(ScrollOnDragDropProperty, value);
    }
    private static void HandleScrollOnDragDropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var container = d as FrameworkElement;
        if (d == null)
        {
            Debug.Fail("Invalid type!");
        }
        Unsubscribe(container);
        if (true.Equals(e.NewValue))
        {
            Subscribe(container);
        }
    }
    private static void Subscribe(FrameworkElement container)
    {
        container.PreviewDragOver += OnContainerPreviewDragOver;
    }
    private static void Unsubscribe(FrameworkElement container)
    {
        container.PreviewDragOver -= OnContainerPreviewDragOver;
    }
    private static void OnContainerPreviewDragOver(object sender, DragEventArgs e)
    {
        const double Tolerance = 60;
        const double Offset = 20;
        var container = sender as FrameworkElement;
        if (container == null)
        {
            return;
        }
        var scrollViewer = GetFirstVisualChild<ScrollViewer>(container); 
        if (scrollViewer == null)
        {
            return;
        }
        var verticalPos = e.GetPosition(container).Y;
        if (verticalPos < Tolerance)
        {
            // Top of visible list? Scroll up.
            scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - Offset);
        }
        else if (verticalPos > container.ActualHeight - Tolerance) 
        {
            // Bottom of visible list? Scroll down.
            scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + Offset);
        }
    }
    private static T GetFirstVisualChild<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj != null)
        {
            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);
                if (child is T)
                {
                    return (T)child;
                }
                var childItem = GetFirstVisualChild<T>(child);
                if (childItem != null)
                {
                    return childItem;
                }
            }
        }
        return null;
    }
}

最后,在控件的XAML上指定附加的属性。免责声明:我在TreeView上使用这种方法,但它应该适用于任何可滚动控件,如列表框:-

<UserControl xmlns:attProps="clr-namespace:MyAssembly.AttachedProperties;assembly=MyAssembly">
    <TreeView attProps:DragDropAttProps.ScrollOnDragDrop="True">

这是一个使用ListBox并进行自动滚动的简单而完整的拖放示例。我认为Andrew Stephens的回答已经在回答你的问题,因为它管理自动滚动,而且它使用attached properties,这是一个很好的做法。

这是一个完整拖拽的例子;放在示例窗口中,从和到同一列表框。

代码背后:

public partial class MainWindow : Window
{
    private const int dragMargin = 10;
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ListBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton != MouseButtonState.Pressed || list.SelectedItem == null)
            return;
        if (e.MouseDevice.DirectlyOver != null)
        {
            DragDrop.DoDragDrop(list, list.SelectedItem, DragDropEffects.All);
        }
    }
    private void list_Drop(object sender, DragEventArgs e)
    {
        var source = sender as ListBox;
        if (source == null)
            return;
        var item = e.Data.GetData(typeof (ContentControl));
        if (item == null)
            return;
        source.Items.Remove(item);
        var listAsTarget = sender as ListBox;
        if (listAsTarget == null)
            return;
        var mouseDirectitem = e.OriginalSource;
        var listItem = VisualFindParentFromType<ListBoxItem>(mouseDirectitem as UIElement);
        if (listItem == null)
        {
            listAsTarget.Items.Insert(0, item);
            return;
        }
        var insertBefore = true;
        var pointReletiveItem = e.GetPosition(listItem);
        Debug.WriteLine("Item Height: {0}", listItem.ActualHeight);
        Debug.WriteLine("Y: {0}", pointReletiveItem.Y);
        if (pointReletiveItem.Y > listItem.ActualHeight / 2)
            insertBefore = false;
        var index = listAsTarget.Items.IndexOf(listItem.Content);
        if (index >= 0)
        {
            listAsTarget.Items.Insert(insertBefore ? index : index + 1, item);
            return;
        }
        listAsTarget.Items.Add(item);
    }
    private T VisualFindParentFromType<T>(DependencyObject element) where T : UIElement
    {
        if (element == null)
            return default(T);
        if (element is T)
            return (T)element;
        return VisualFindParentFromType<T>(VisualTreeHelper.GetParent(element));
    }
    private T FindChildrenFromType<T>(DependencyObject element) where T : UIElement
    {
        if (element == null)
            return default(T);
        if (element is T)
            return (T) element;
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
        {
            var result = FindChildrenFromType<T>(VisualTreeHelper.GetChild(element, i));
            if (!Equals(default(T), result))
                return result;
        }
        return default(T);
    }
    private void list_DragOver(object sender, DragEventArgs e)
    {
        var source = e.Source as ListBox;
        if (source == null)
            return;
        var point = e.GetPosition(source);
        var scrollViewer = FindChildrenFromType<ScrollViewer>(source);
        if (scrollViewer == null)
            return;
        if (point.Y < dragMargin)
        {
            scrollViewer.LineUp();
        }
        else if (point.Y > Math.Abs(source.ActualHeight - dragMargin))
        {
            scrollViewer.LineDown();
        }
    }
}

Xaml:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <ListBox x:Name="list" Height="90" AllowDrop="True" MouseMove="ListBox_MouseMove" Drop="list_Drop" PreviewDragOver="list_DragOver">
        <ContentControl>
            Item 1
        </ContentControl>
        <ContentControl>
            Item 2
        </ContentControl>
        <ContentControl>
            Item 3
        </ContentControl>
        <ContentControl>
            Item 4
        </ContentControl>
        <ContentControl>
            Item 5
        </ContentControl>
        <ContentControl>
            Item 6
        </ContentControl>
        <ContentControl>
            Item 7
        </ContentControl>
        <ContentControl>
            Item 8
        </ContentControl>
        <ContentControl>
            Item 9
        </ContentControl>
    </ListBox>
</Grid>

希望这能帮助。。。