2009-03-20 15 views
12

Mam kolekcję Visual s w ListBox. Muszę znaleźć XPosition elementu wewnątrz, a następnie animować HorizontalOffset z ListBox 's ScrollViewer. Zasadniczo chcę stworzyć animowaną metodę ScrollIntoView.WPF - Animate ListBox.ScrollViewer.HorizontalOffset?

Daje mi to kilka problemów. Po pierwsze, jak mogę uzyskać odniesienie do scrollviewera ListBox s? Po drugie, jak mogę uzyskać względny XPosition lub HozintalOfffset dowolnego elementu w ListBox?

Nie ponawiam żadnych danych wejściowych w samym ListBox, więc nie mogę używać właściwości powiązanych z Mouse.

Odpowiedz

32

Nie sądzę, że będzie można użyć scenariusza WPF do animacji, ponieważ storyboardy animują właściwości zależności WPF. Aby przewinąć, musisz zadzwonić pod numer ScrollViewer.ScrollToHorizontalOffset(double).

Można spróbować utworzyć niestandardową właściwość zależności, która wywołuje SetHorizontalOffset w funkcji OnDependencyPropertyChanged(). Następnie możesz animować tę właściwość.

public static readonly DependencyProperty ScrollOffsetProperty = 
    DependencyProperty.Register("ScrollOffset", typeof(double), typeof(YOUR_TYPE), 
    new FrameworkPropertyMetadata(0.0, new PropertyChangedCallback(OnScrollOffsetChanged))); 


public double ScrollOffset 
{ 
    get { return (double)GetValue(ScrollOffsetProperty); } 
    set { SetValue(ScrollOffsetProperty, value); } 
} 

private static void OnScrollOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
{ 
    YOUR_TYPE myObj = obj as YOUR_TYPE; 

    if (myObj != null) 
     myObj.SCROLL_VIEWER.ScrollToHorizontalOffset(myObj.ScrollOffset); 
} 

Aby uzyskać przeglądarkę przewijania, można użyć VisualTreeHelper do przeszukiwania wizualnych elementów podrzędnych obiektu ListBox. Zapisz odniesienie do ScrollViewer, ponieważ będziesz go potrzebować później. Spróbuj:

public static childItem FindVisualChild<childItem>(DependencyObject obj) 
    where childItem : DependencyObject 
{ 
    // Iterate through all immediate children 
    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; 
} 

Ta funkcja zwraca pierwsze wizualne dziecko typu parametru. Zadzwoń pod numer FindVisualChild<ScrollViewer>(ListBox), aby uzyskać przeglądarkę ScrollViewer.

Na koniec spróbuj użyć UIElement.TranslatePoint(Point, UIElement), aby uzyskać pozycję X przedmiotu. Wywołaj tę funkcję na przedmiocie, podaj 0,0 punktu i przeprowadź w ScrollViewer.

Mam nadzieję, że to pomoże.

+0

Mój panie, to niezła robota! Dzięki za pomoc, Josh, wskazał mi przynajmniej właściwy kierunek. – Stimul8d

+0

Masz na myśli ScrollToHorizontalOffset zamiast SetHorizontalOffset? –

+0

Tak, masz rację. Dzięki! –

1

Nie jestem pewien, czy moja metoda jest dobrą praktyką, ale przez ograniczony czas wydawało mi się, że działa dobrze. Zamiast używać storyboardu użyłem zamiast tego DispatcherTimer.

ScrollLeftButtonCommand = new DelegateCommand(
    o => 
     { 
      var scrollViewer = (ScrollViewer)o; 

      scrollTimer = new DispatcherTimer(); 

      scrollTimer.Start(); 

      scrollTimer.Interval = TimeSpan.FromMilliseconds(30); 

      scrollTimer.Tick += (s, e) => 
      {   
       scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - 50); 

       if (scrollViewer.HorizontalOffset <= 0) 
       { 
        scrollTimer.Stop(); 
       } 
      }; 
     }); 

Upewnij się, że jest to DispatchTimer więc wątek jest w stanie przejąć kontrolę nad elementu UI

również pamiętać, aby powiązać do obiektu w widoku!

<Button CommandParameter="{Binding ElementName=MyScrollViewer }" 
     Command="{Binding ScrollLeftButtonCommand }"/>