2009-08-22 7 views
20

Mam aplikację WPF, która ma ListBox. Mechanizm przeciągania jest już zaimplementowany, ale gdy lista jest zbyt długa i chcę przenieść element do niewidocznej pozycji, nie mogę.Automatyczne przewijanie listy rozwijanej WPF podczas przeciągania

Na przykład ekran pokazuje 10 elementów. I mam 20 przedmiotów. Jeśli chcę przeciągnąć ostatni element na pierwszą pozycję, muszę przeciągnąć do góry i upuścić. Przewiń w górę i przeciągnij ponownie.

Jak mogę automatycznie przewinąć ?

Odpowiedz

25

Rozumiem. Wykorzystał zdarzenie DragOver z ListBox, użył funkcji znalezionej here, aby uzyskać scrollviewer z listy, a po niej jest tylko trochę żonglowania z Pozycją.

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; 
} 
+1

Próbowałem wam metodę i ją Prace. Jednak podczas przeciągania obiektów wokół tej samej listy po upuszczeniu powraca do oryginalnego obiektu, w którym chciałbym zobaczyć upuszczony element. Czy masz to i czy to poprawiłeś? –

+0

@DavidBrunelle Nie pamiętam, przepraszam. –

+0

+1 wspaniała odpowiedź, ale to jest pierwsze wyszukiwanie, a nie pierwsza jak wskazano. – Cameron

13

Na tej podstawie ja stworzyliśmy Attached Behavior, które mogą być łatwo wykorzystane w ten sposób -

<ListView 
    xmlns:WpfExtensions="clr-namespace:WpfExtensions" 
    WpfExtensions:DragDropExtension.ScrollOnDragDrop="True" 

Oto kod na załączonym zachowań -

/// <summary> 
/// Provides extended support for drag drop operation 
/// </summary> 
public static class DragDropExtension 
{ 
    public static read-only 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; 
    } 

    public 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; 
    } 
} 
+2

Bardzo ładne rozwiązanie. Nie zapominaj, że możesz umieścić "ScrollViewer.CanContentScroll =" False "" na liście ListBox/ListView, jeśli chcesz płynnego przewijania. – Pak