Disable drag and drop when scrolling

2019-05-25 06:09发布

问题:

I have implemented drag and drop functionality for a TreeView in WPF.

All works fine but when the scrolls are shown in the tree view, if I select an item in the tree and then and try to scroll (vertical or horizontal) the TreeView tries to perform a drag drop operation.

Here is the source code:

class TreeViewRearranger
{
    private TreeView mTreeView;

    private int MOVE_TOLERANCE = 10;

    private TreeViewItem mDraggedItem = null;
    private TreeViewItem mTargetDrop = null;
    private DropAdorner mDropAdorner = null;

    public void Initialize(TreeView treeView)
    {
        mTreeView = treeView;

        SetupDragDropEvents();
    }

    private void SetupDragDropEvents()
    {
        mTreeView.MouseDown += mTreeView_MouseDown;
        mTreeView.MouseMove += mTreeView_MouseMove;
        mTreeView.DragOver += mTreeView_DragOver;
        mTreeView.Drop += mTreeView_Drop;
        mTreeView.DragEnter += mTreeView_DragEnter;
        mTreeView.DragLeave += mTreeView_DragLeave;
    }

    void mTreeView_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton != MouseButton.Left)
            return;

        mLastMouseDown = e.GetPosition(mTreeView);
    }

    void mTreeView_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton != MouseButtonState.Pressed)
            return;

        Point currentPosition = e.GetPosition(mTreeView);

        if ((Math.Abs(currentPosition.X - mLastMouseDown.X) <= MOVE_TOLERANCE) &&
            (Math.Abs(currentPosition.Y - mLastMouseDown.Y) <= MOVE_TOLERANCE))

            return;

        StartDrag();
    }

    void mTreeView_DragLeave(object sender, DragEventArgs e)
    {
        RemoveAdorners();
    }

    void mTreeView_DragEnter(object sender, DragEventArgs e)
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(mTreeView);
        mDropAdorner = new DropAdorner(mTreeView);

        layer.Add(mDropAdorner);
    }

    private Point mLastMouseDown;

    private void StartDrag()
    {
        mDraggedItem = mTreeView.SelectedItem as TreeViewItem;

        if (mDraggedItem == null) return;

        DragDropEffects finalDropEffect =
            DragDrop.DoDragDrop(
                mTreeView, mTreeView.SelectedValue, DragDropEffects.Move);

        // Check that target is not null and item is 
        // dragging(moving)

        if ((finalDropEffect != DragDropEffects.Move) || (mTargetDrop == null))
            return;

        // A Move drop was accepted

        if (mDraggedItem.Header.ToString().Equals(mTargetDrop.Header.ToString()))
            return;

        PerformDragDrop(mDraggedItem, mTargetDrop);

        mTargetDrop = null;
        mDraggedItem = null;
    }

    private void PerformDragDrop(TreeViewItem source, TreeViewItem destination)
    {
        if (source == null || destination == null)
            return;

        OnRearrange(source, destination);
    }

    private static TreeViewItem GetParentTreeViewItem(DependencyObject item)
    {
        if (item == null)
            return null;

        DependencyObject parent = VisualTreeHelper.GetParent(item);
        TreeViewItem parentTreeViewItem = parent as TreeViewItem;
        return parentTreeViewItem ?? GetParentTreeViewItem(parent);
    }

    private void OnRearrange(TreeViewItem source, TreeViewItem destination)
    {
        if (source == null || destination == null)
            return;

        TreeNode sourceNode = source.Tag as TreeNode;
        TreeNode destinationNode = destination.Tag as TreeNode;

        TreeNode targetNode = destinationNode;

        if (!destination.IsExpanded || destination.Items.Count == 0)
        {
            TreeViewItem parentItem = GetParentTreeViewItem(destination);

            if (parentItem == null)
            {
                targetNode = mTreeView.Tag as TreeNode;
            }
            else
            {
                targetNode = parentItem.Tag as TreeNode;
            }
        }

        int index = targetNode.Children.IndexOf(destinationNode) + 1;

        // performed a rearrange from sourceNode to targetNode at index
    }

    void mTreeView_DragOver(object sender, DragEventArgs e)
    {
        Point currentPosition = e.GetPosition(mTreeView);

        if ((Math.Abs(currentPosition.X - mLastMouseDown.X) <= MOVE_TOLERANCE) &&
            (Math.Abs(currentPosition.Y - mLastMouseDown.Y) <= MOVE_TOLERANCE))

            return;

        // Verify that this is a valid drop and then store the drop target

        TreeViewItem item = GetNearestContainer(e.OriginalSource as UIElement);

        if (CheckDropTarget(mDraggedItem, item))
        {
            e.Effects = DragDropEffects.Move;
            UpdateDropAdorner(item);
        }
        else
        {
            e.Effects = DragDropEffects.None;
            UpdateDropAdorner(null);
        }

        e.Handled = true;
    }

    void mTreeView_Drop(object sender, DragEventArgs e)
    {
        e.Effects = DragDropEffects.None;
        e.Handled = true;

        // Verify that this is a valid drop and then store the drop target
        TreeViewItem targetItem = GetNearestContainer
            (e.OriginalSource as UIElement);

        if (targetItem == null || mDraggedItem == null)
            return;

        mTargetDrop = targetItem;
        e.Effects = DragDropEffects.Move;

        UpdateDropAdorner(mTargetDrop);

        RemoveAdorners();
    }

    private void UpdateDropAdorner(TreeViewItem targetItem)
    {
        mDropAdorner.UpdateTargetPosition(targetItem);
    }

    private TreeViewItem GetNearestContainer(UIElement element)
    {
        // Walk up the element tree to the nearest tree view item.
        TreeViewItem container = element as TreeViewItem;
        while ((container == null) && (element != null))
        {
            element = VisualTreeHelper.GetParent(element) as UIElement;
            container = element as TreeViewItem;
        }
        return container;
    }

    private bool CheckDropTarget(TreeViewItem sourceItem, TreeViewItem targetItem)
    {
        if (sourceItem == null || targetItem == null)
            return false;

        if (sourceItem.Header.ToString().Equals(targetItem.Header.ToString()))
            return false;

        if (targetItem.IsDescendantOf(sourceItem))
            return false;

        return true;
    }

    private void RemoveAdorners()
    {
        AdornerLayer layer = AdornerLayer.GetAdornerLayer(mTreeView);

        layer.Remove(mDropAdorner);

        mDropAdorner = null;
    }
}

回答1:

You may have to attach yourself to the scrollviewers ScrollChanged event and when that event is triggered you disable drag and drop. I haven't tried it but I think that it might be a starting place to try

ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(mTreeView);
        scrollViewer.ScrollChanged += new ScrollChangedEventHandler(scrollViewer