WPF - Animate ListBox.ScrollViewer.HorizontalOffset?

19,981

Solution 1

I don't think you will be able to use a WPF storyboard for the animation because storyboards animate WPF dependency properties. You will need to call ScrollViewer.ScrollToHorizontalOffset(double) to scroll.

You could try creating a custom dependency property that calls SetHorizontalOffset in the OnDependencyPropertyChanged() function. Then you could animate this property.

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);
}

To get the scroll viewer you can use the VisualTreeHelper to search the visual children of the ListBox. Save a reference to the ScrollViewer because you will need it later. Try this:

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

This function returns the first visual child of the parameter type. Call FindVisualChild<ScrollViewer>(ListBox) to get the ScrollViewer.

Finally, try using UIElement.TranslatePoint(Point, UIElement) to get the X position of the item. Call this function on the item, pass in 0,0 for the point, and pass in the ScrollViewer.

Hope this helps.

Solution 2

I'm not sure if my method is good practice but for the limited time I had it seemed to work okay. Instead of using a story board I just used a DispatcherTimer instead.

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();
               }
           };
       });

Make sure it's a DispatchTimer so the thread is able to take control of the UI element

Also remember to bind to your object in your view!

<Button CommandParameter="{Binding ElementName=MyScrollViewer }"
        Command="{Binding ScrollLeftButtonCommand }"/>
Share:
19,981

Related videos on Youtube

EightyOne Unite
Author by

EightyOne Unite

Updated on March 12, 2020

Comments

  • EightyOne Unite
    EightyOne Unite about 4 years

    I have a collection of Visuals in a ListBox. I need to find the XPosition of an element inside it and then animate the HorizontalOffset of the ListBox's ScrollViewer. Essentially I want to created an animated ScrollIntoView method.

    This gives me a couple of problems. Firstly, how can I get a reference to the ListBoxs scrollviewer? Secondly, how can i get the relative XPosition or HozintalOfffset of an arbitrary element in the ListBox?

    I'm not reponding to any input on the ListBox itself so I can't use Mouse related properties.

  • EightyOne Unite
    EightyOne Unite about 15 years
    My Lord, that's quite a job! Thanks for the help Josh, it's pointed me in the right direction at least.
  • romu
    romu over 14 years
    Do you mean ScrollToHorizontalOffset instead of SetHorizontalOffset?
  • babatto
    babatto over 14 years
    Great idea. It helped me out!
  • Josh G
    Josh G over 14 years
    It's possible that this property could be an attached DependencyProperty. That would allow this code to be reused on any control that has a ScrollView in it's visual tree.
  • Flatliner DOA
    Flatliner DOA almost 14 years
    You actually want to find the ItemsPresenter when translating the position of the item. e.g. var itemContainer = listBox.ItemContainerGenerator.ContainerFromItem(listBox.Sel‌​ectedItem); itemContainer.TranslatePoint(new Point(0, 0), FindVisualChild<ItemsPresenter>(scrollViewer));

Related