ListBox DragDrop自动滚动问题?

时间:2014-04-28 14:44:40

标签: c# wpf

我使用以下方法在ListBox中成功实现了拖放:

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

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

编辑:当我越过列表的底部边框时,我希望它滚动,以便我可以放在较低项目的某处?

2 个答案:

答案 0 :(得分:2)

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

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">

答案 1 :(得分:0)

这是一个使用ListBox并进行自动滚动的简单而完整的拖放示例。我认为安德鲁斯蒂芬斯的回答是在回答你的问题,因为它管理自动滚动,并使用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>

希望这会有所帮助......