WPF - Animate ListBox.ScrollViewer.HorizontOffset? - PullRequest
13 голосов
/ 20 марта 2009

У меня есть коллекция Visual s в ListBox. Мне нужно найти XPosition элемента внутри него, а затем анимировать HorizontalOffset ListBox ScrollViewer. По сути, я хочу создать анимированный ScrollIntoView метод.

Это дает мне пару проблем. Во-первых, как я могу получить ссылку на ListBox s scrollviewer? Во-вторых, как я могу получить относительный XPosition или HozintalOfffset произвольного элемента в ListBox?

Я не отвечаю ни на какие входные данные на самом ListBox, поэтому не могу использовать Mouse связанные свойства.

Ответы [ 2 ]

33 голосов
/ 20 марта 2009

Не думаю, что вы сможете использовать раскадровку WPF для анимации, потому что раскадровки анимируют свойства зависимостей WPF. Вам нужно будет позвонить ScrollViewer.ScrollToHorizontalOffset(double) для прокрутки.

Вы можете попробовать создать собственное свойство зависимостей, которое вызывает SetHorizontOffset в функции OnDependencyPropertyChanged (). Тогда вы можете анимировать это свойство.

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

Чтобы получить средство просмотра прокрутки, вы можете использовать VisualTreeHelper для поиска визуальных потомков ListBox. Сохраните ссылку на ScrollViewer, потому что она понадобится вам позже. Попробуйте это:

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

Эта функция возвращает первого визуального потомка типа параметра. Вызовите FindVisualChild<ScrollViewer>(ListBox), чтобы получить ScrollViewer.

Наконец, попробуйте использовать UIElement.TranslatePoint(Point, UIElement), чтобы получить позицию X элемента. Вызовите эту функцию для элемента, передайте 0,0 для точки и передайте ScrollViewer.

Надеюсь, это поможет.

1 голос
/ 09 февраля 2015

Я не уверен, что мой метод является хорошей практикой, но в течение ограниченного времени, которое я имел, он работал нормально. Вместо использования раскадровки я просто использовал 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();
               }
           };
       });

Убедитесь, что это DispatchTimer, чтобы поток мог контролировать элемент пользовательского интерфейса

Также не забудьте привязать свой объект к вашему виду!

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