Artur's answer was great in that it got me on the right path, but I found a few problems.
First, the code has an error here:
ScrollViewer sv = FindVisualChild<ScrollViewer>(ItemsList);
It should pass in li
.
Also, the code above does not properly consider whether the ListBox has a horizontal scroll bar. I've modified the code with both fixes as shown here:
private void MainTreeView_DragOver(object sender, DragEventArgs e)
{
ListBox li = sender as ListBox;
ScrollViewer sv = FindVisualChild<ScrollViewer>(li);
double tolerance = 24;
double verticalPos = e.GetPosition(li).Y;
double topMargin = tolerance;
var bottomMargin = li.ActualHeight - tolerance;
if(sv.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
{
var horizontalScrollBar = sv.Template.FindName("PART_HorizontalScrollBar", sv) as System.Windows.Controls.Primitives.ScrollBar;
if(horizontalScrollBar != null)
{
bottomMargin -= horizontalScrollBar.ActualHeight;
}
}
double distanceToScroll = 3;
if (verticalPos < topMargin) // Top of visible list?
{
sv.ScrollToVerticalOffset(sv.VerticalOffset - distanceToScroll); //Scroll up.
}
else if (verticalPos > bottomMargin) //Bottom of visible list?
{
sv.ScrollToVerticalOffset(sv.VerticalOffset + distanceToScroll); //Scroll down.
}
}
public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
{
// Search immediate children first (breadth-first)
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;