After trying a bunch of things I think I've come up with a way to do this. The DragOver
and Drop
events send you a DragEventArgs
parameter. You use this to do hit-testing. If you hittest though, you are not likely to hit the item you want directly. Rather, you are going to be hitting something that forms part of the template of the item. To get to the TreeViewItems you are interested in, you can try and walk up the Visual Tree.
In this example the top-level TreeViewItems
are bound to GroupItem
instances and the child-nodes are bound to DragItems
instances. tv
is the name of the TreeView element itself. In this code it is safe to assume that I will find it, since the event handlers are defined on this element.
I created the following code that walks up the tree.
private void FindDropTarget(
out TreeViewItem pGroupingNode,
out TreeViewItem pItemNode,
DragEventArgs pDragEventArgs)
{
pItemNode = null;
pGroupingNode = null;
DependencyObject k = VisualTreeHelper.HitTest(tv, pDragEventArgs.GetPosition(tv)).VisualHit;
while (k != null)
{
if (k is TreeViewItem)
{
TreeViewItem treeNode = k as TreeViewItem;
if (treeNode.DataContext is GroupItem)
{
pGroupingNode = treeNode;
}
else if (treeNode.DataContext is DragItems)
{
pItemNode = treeNode;
}
}
else if (k == tv)
{
Console.WriteLine("Found treeview instance");
return;
}
k = VisualTreeHelper.GetParent(k);
}
}
Consume it like this. Notice the check for IsVisible
which is important:
private void tv_DragOver(object sender, DragEventArgs e)
{
TreeViewItem groupingNode, itemNode;
FindDropTarget(out groupingNode, out itemNode, e);
GroupItem groupingData = (groupingNode != null ? groupingNode.DataContext as GroupItem : null);
DragItems dragItem = (itemNode != null && itemNode.IsVisible ? itemNode.DataContext as DragItems : null);
Console.WriteLine("Hovering ...");
Console.WriteLine(
"Grouping Node = {0}, Item Node = {1}",
groupingData != null ? groupingData.Title : "null",
dragItem != null ? dragItem.Id : "null");
}
If you want to give some kind of visual indication of where the item will drop, consider using an adorner like Bea Stollnitz explains here. You may also consider changing some kind of value on the Bound data classes (like having an IsHovering
property which can highlight the item via databinding)