locked
Анимация скроллинга LongListSelector (или ListBox) RRS feed

  • Вопрос

  • Привет. Задачу поставил себе следующую:

    Есть LongListSelector. Как только в него загрузились данные, из кода нужно проскроллить до определенного элемента. 

    Я использовал ScrollTo. Функция работает, но мне показалось этого мало. Решил погуглить как можно это дело красиво анимировать. Есть некая депрекейтнутая функция AnimateTo. Попробовал - эффект как и ScrollTo, никаких анимаций.

    Дальшейшее гугление подало идею добавть свою DependencyProperty на вертикальный офсет скрола:

    public partial class LongListSelector : Control
        {
            public static childItem FindVisualChild<childItem>(DependencyObject obj) where childItem : DependencyObject
            {
                for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; 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;
            }
    
            public static readonly DependencyProperty VerticalOffsetProperty = DependencyProperty.Register("VerticalOffset",
               typeof(double), typeof(LongListSelector), new PropertyMetadata(VerticalOffsetPropertyChanged));
            public double VerticalOffset
            {
                get { return (double)this.GetValue(VerticalOffsetProperty); }
                set { this.SetValue(VerticalOffsetProperty, value); }
            }
            private static void VerticalOffsetPropertyChanged(object sender, DependencyPropertyChangedEventArgs args)
            {
                LongListSelector cThis = sender as LongListSelector;
                ScrollViewer LScrollViewer = FindVisualChild<ScrollViewer>(cThis);
                LScrollViewer.ScrollToVerticalOffset((double)args.NewValue);
            }
            public static readonly DependencyProperty ContentAreaProperty = DependencyProperty.Register("ContentArea", typeof(object), typeof(LongListSelector), null);
            public object ContentArea
            {
                get { return (object)GetValue(ContentAreaProperty); }
                set { SetValue(ContentAreaProperty, value); }
            }
    ...

    Сделано. Пробую проскролить куда-нибудь...

                DoubleAnimation verticalAnimation = new DoubleAnimation();
                verticalAnimation.From = 0;
                verticalAnimation.To = 25;
                verticalAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(5000));
                CubicEase ea = new CubicEase();
                ea.EasingMode = EasingMode.EaseInOut;
                verticalAnimation.EasingFunction = ea;
                var sb = new Storyboard();
                sb.Children.Add(verticalAnimation);
                Storyboard.SetTarget(verticalAnimation, buddies);
                Storyboard.SetTargetProperty(verticalAnimation, new PropertyPath(LongListSelector.VerticalOffsetProperty));
                sb.Begin();

    Все классно, плавно скроллит. А вот теперь собвстевнно вопрос:

    Как правильно определить verticalAnimation.To ? К прмеру, для огромного списка интервал скроллирования (ScrollViewer.ScrollableHeight) получился от 0 до 119. Почему 119? В чем это измеряется? По факту весь список занимает экранов дцать, т.е много тысяч пикселей. И как мне вычислить это значение, если я, напрмер, хочу проскролить к N элементу в списке?

    10 ноября 2014 г. 15:52

Все ответы

  • Нет там никаких тысяч пикселей, потому что виртуализация. Если бы Вы выкинули на UI 20000 айтемов без виртуализации, Вы бы споймали outofmemory.

    Выковыряйте исходники LLS контрола и посмотрите на метод ScrollTo. Там он дергает внутренний листбокс и делает скроллинг несколько раз, пока не подгрузит нужный айтем.

    11 ноября 2014 г. 8:39
  • Я уже смотрел его исходники. Там ничего полезного.

                    foreach (LongListSelectorItem tuple in _listBox.ItemsSource)
                    {
                        if (tuple.Item != null && tuple.Item.Equals(item))
                        {
                            _listBox.ScrollIntoView(tuple);
                            break;
                        }
                    }
    Мне больше интересно что такое 119? Как это можно сматчить на айтемы в lls?

    11 ноября 2014 г. 9:39
  • Не, такие внутренности я уже не вспомню. Помню с ViewPort игрался у него, когда Incremental loading реализовывали. Нужно будет код поковырять.
    11 ноября 2014 г. 10:22