2009-08-22 11 views
20

खींचते समय मेरे पास एक WPF ऐप है जिसमें ListBox है। ड्रैग तंत्र पहले ही लागू हो चुका है, लेकिन जब सूची बहुत लंबी है और मैं किसी आइटम को उस स्थिति में स्थानांतरित करना चाहता हूं जो दृश्यमान नहीं है, तो मैं नहीं कर सकता।WPF Listbox ऑटो स्क्रॉल

उदाहरण के लिए, स्क्रीन 10 आइटम दिखाती है। और मेरे पास 20 आइटम हैं। अगर मैं अंतिम आइटम को पहली स्थिति में खींचाना चाहता हूं तो मुझे शीर्ष पर खींचना चाहिए और छोड़ना चाहिए। ऊपर स्क्रॉल करें और फिर खींचें।

मैं ListBox ऑटो स्क्रॉल कैसे बना सकता हूं?

उत्तर

25

समझ गया। ListBox के इवेंट DragOver का उपयोग किया गया, फ़ंक्शनबॉक्स के scrollviewer प्राप्त करने के लिए फ़ंक्शन का उपयोग here और उसके बाद स्थिति के साथ थोड़ी सी जुगलिंग प्राप्त करने के लिए किया गया।

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

मैं तुम्हें विधि और इसे करने की कोशिश -

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

यहाँ संलग्न व्यवहार के लिए कोड है काम करता है। हालांकि, ड्रॉपिंग के बाद, उसी सूची के आस-पास ऑब्जेक्ट्स खींचते समय, यह मूल ऑब्जेक्ट पर वापस आता है जहां मैं गिराए गए आइटम को देखना चाहता हूं। क्या आपके पास यह था और क्या आपने इसे सही किया? –

+0

@ डेविड ब्रुनेल मुझे याद नहीं है, क्षमा करें। –

+0

+1 बहुत अच्छा जवाब है, हालांकि यह गहराई से पहली खोज है, न कि चौड़ाई वाला - पहला संकेत दिया गया है। – Cameron

13

इस आधार पर मैं एक Attached Behavior जो आसानी से इस तरह इस्तेमाल किया जा सकता बनाया है -

/// <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

बहुत अच्छा समाधान। यह न भूलें कि अगर आप एक चिकनी स्क्रॉल चाहते हैं तो आप अपने ListBox/ListView पर "ScrollViewer.CanContentScroll =" गलत "डाल सकते हैं। – Pak