Matten Matten - 28 days ago 6
C# Question

Drag&Drop in a WPF TreeView on the Scrollbar

we're using the MVVM pattern in our application and in a window, we have two

TreeView
s allowing to drag items from the first and drop it on the second tree. To avoid code behind, we're using behaviours to bind the drag and drop against the ViewModel.

The behaviour is implemented pretty much like this example and working like a charm, with one bug.

The scenario is a tree which is bigger than the window displaying it, therefore it has a vertical scroll bar. When an item is selected and the user wants to scroll, the program starts drag and drop (which prevents the actual scrolling and therefore isn't what we want).

This isn't very surprising as the scrollbar is contained in the
TreeView
control. But I'm unable to determine safely if the mouse is over the scrollbar or not.

The
TreeViewItems
are represented by a theme using Borders, Panels and so on, so a simple
InputHitTest
isn't as simple as one may think.

Has anybody already encountered the same problem?

If more code coverage of the problem is required, I can paste some lines from the .xaml.




Edit

Incorporating Nikolays link I solved the problem using a
IsMouseOverScrollbar
method, if anyone has this problem in the future the code from above must be altered in the following way:

private static void PreviewMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton != MouseButtonState.Pressed || startPoint == null)
return;

if (!HasMouseMovedFarEnough(e))
return;

if (IsMouseOverScrollbar(sender, e.GetPosition(sender as IInputElement)))
{
startPoint = null;
return;
}

var dependencyObject = (FrameworkElement)sender;
var dataContext = dependencyObject.GetValue(FrameworkElement.DataContextProperty);
var dragSource = GetDragSource(dependencyObject);

if (dragSource.GetDragEffects(dataContext) == DragDropEffects.None)
return;

DragDrop.DoDragDrop(
dependencyObject, dragSource.GetData(dataContext), dragSource.GetDragEffects(dataContext));
}


private static bool IsMouseOverScrollbar(object sender, Point mousePosition)
{
if (sender is Visual)
{
HitTestResult hit = VisualTreeHelper.HitTest(sender as Visual, mousePosition);

if (hit == null) return false;

DependencyObject dObj = hit.VisualHit;
while(dObj != null)
{
if (dObj is ScrollBar) return true;

if ((dObj is Visual) || (dObj is Visual3D)) dObj = VisualTreeHelper.GetParent(dObj);
else dObj = LogicalTreeHelper.GetParent(dObj);
}
}

return false;
}

Answer

Take a look at this implementation of Drag and Drop behaviour for ListView by Josh Smith. It has code to deal with scrollbars and some other unobvious problems of DnD (like drag treshold, precise mouse coordinates and such). This behaviour can be easily adopted to work with TreeViews too.