Scrolling while dragging and dropping (WPF)
I know this question really old, but here is the MVVM way as attached property:
using System.Windows;
using System.Windows.Controls;
namespace AndroidCtrlUI.XTools.Behaviors
{
///<summary>
/// TreeItemAttach
///<para/> TreeViewItem
///</summary>
public sealed class TreeItemAttach
{
#region BringIntoView
///<summary>
/// DependencyProperty
///</summary>
public static readonly DependencyProperty BringIntoViewProperty = DependencyProperty.RegisterAttached("BringIntoView", typeof(bool), typeof(TreeItemAttach), new UIPropertyMetadata(false, (s, e) =>
{
if ((bool)e.NewValue != (bool)e.OldValue && s is TreeViewItem t)
{
if ((bool)e.NewValue)
{
t.Selected += BringIntoView;
}
else
{
t.Selected -= BringIntoView;
}
}
}));
///<summary>
/// Get
///</summary>
///<param name="target">DependencyObject</param>
///<returns>ICommand</returns>
public static bool GetBringIntoView(DependencyObject target)
{
return (bool)target.GetValue(BringIntoViewProperty);
}
///<summary>
/// Set
///</summary>
///<param name="target">DependencyObject</param>
///<param name="value">ICommand</param>
public static void SetBringIntoView(DependencyObject target, bool value)
{
target.SetValue(BringIntoViewProperty, value);
}
private static void BringIntoView(object sender, RoutedEventArgs e)
{
if (e.Source is TreeViewItem s)
{
double h = s.ActualHeight;
if (s.IsExpanded && s.Items.Count > 0)
{
h = s.ActualHeight / TreeWalker(s);
}
s.BringIntoView(new Rect(0, h * -1, s.ActualWidth, h * 2.5));
}
}
private static long TreeWalker(TreeViewItem item)
{
long c = item.Items.Count;
foreach (object i in item.Items)
{
if (i != null && item.ItemContainerGenerator.ContainerFromItem(i) is TreeViewItem t && t.IsExpanded && t.Items.Count > 0)
{
c += TreeWalker(t);
}
}
return c;
}
#endregion
}
}
And it can be used like:
<Style x:Key="TreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="tool:TreeItemAttach.BringIntoView" Value="True"/>
</Style>
I have created an attached property for achieving this behavior, have a look at my post here -
Attached Behavior for auto scrolling containers while doing Drag & Drop
Main logic is something like this -
private static void OnContainerPreviewDragOver(object sender, DragEventArgs e)
{
FrameworkElement container = sender as FrameworkElement;
if (container == null) { return; }
ScrollViewer scrollViewer = GetFirstVisualChild<ScrollViewer>(container);
if (scrollViewer == null) { return; }
double tolerance = 60;
double verticalPos = e.GetPosition(container).Y;
double offset = 20;
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);
}
}
Similar questions on SO (although they are mostly for ListBox
/ListView
but should work for TreeView
too) -
WPF Listbox auto scroll while dragging
WPF ListView Databound Drag/Drop Auto Scroll
WPF Drag-to-scroll doesn't work correctly