I have a WPF app that has a ListBox
. The drag mechanism is already implemented, but when the list is too long and I want to move an item to a position not visible I can't.
For example, the screen shows 10 items. And I have 20 items. If I want to drag the last item to the first position I must drag to the top and drop. Scroll up and drag again.
How can I make the ListBox
auto scroll?
Got it. Used the event DragOver
of the ListBox
, used the function found here to get the scrollviewer
of the listbox and after that its just a bit of juggling with the Position.
private void ItemsList_DragOver(object sender, System.Windows.DragEventArgs e) { ListBox li = sender as ListBox; ScrollViewer sv = FindVisualChild<ScrollViewer>(ItemsList); double tolerance = 10; double verticalPos = e.GetPosition(li).Y; double offset = 3; if (verticalPos < tolerance) // Top of visible list? { sv.ScrollToVerticalOffset(sv.VerticalOffset - offset); //Scroll up. } else if (verticalPos > li.ActualHeight - tolerance) //Bottom of visible list? { sv.ScrollToVerticalOffset(sv.VerticalOffset + offset); //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; }
Based on this I have created an Attached Behavior which can easily be used like this -
<ListView xmlns:WpfExtensions="clr-namespace:WpfExtensions" WpfExtensions:DragDropExtension.ScrollOnDragDrop="True"
Here is the code for attached behavior -
/// <summary> /// Provides extended support for drag drop operation /// </summary> public static class DragDropExtension { public static readonly DependencyProperty ScrollOnDragDropProperty = DependencyProperty.RegisterAttached("ScrollOnDragDrop", typeof(bool), typeof(DragDropExtension), new PropertyMetadata(false, HandleScrollOnDragDropChanged)); public static bool GetScrollOnDragDrop(DependencyObject element) { if (element == null) { throw new ArgumentNullException("element"); } return (bool)element.GetValue(ScrollOnDragDropProperty); } public static void SetScrollOnDragDrop(DependencyObject element, bool value) { if (element == null) { throw new ArgumentNullException("element"); } element.SetValue(ScrollOnDragDropProperty, value); } private static void HandleScrollOnDragDropChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { FrameworkElement container = d as FrameworkElement; if (d == null) { Debug.Fail("Invalid type!"); return; } Unsubscribe(container); if (true.Equals(e.NewValue)) { Subscribe(container); } } private static void Subscribe(FrameworkElement container) { container.PreviewDragOver += OnContainerPreviewDragOver; } 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? { scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - offset); //Scroll up. } else if (verticalPos > container.ActualHeight - tolerance) //Bottom of visible list? { scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); //Scroll down. } } private static void Unsubscribe(FrameworkElement container) { container.PreviewDragOver -= OnContainerPreviewDragOver; } private static T GetFirstVisualChild<T>(DependencyObject depObj) where T : DependencyObject { if (depObj != null) { for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { DependencyObject child = VisualTreeHelper.GetChild(depObj, i); if (child != null && child is T) { return (T)child; } T childItem = GetFirstVisualChild<T>(child); if (childItem != null) { return childItem; } } } return null; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With