Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Listbox auto scroll while dragging

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?

like image 684
Artur Carvalho Avatar asked Aug 22 '09 15:08

Artur Carvalho


2 Answers

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; } 
like image 78
Artur Carvalho Avatar answered Oct 12 '22 12:10

Artur Carvalho


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;     } } 
like image 21
akjoshi Avatar answered Oct 12 '22 10:10

akjoshi