Как перейти к следующей логической странице в ListBox с физической прокруткой - PullRequest
5 голосов
/ 31 августа 2011

У меня есть список, который должен иметь CanContentScroll == false, потому что мне нужно иметь возможность плавно прокручивать его. Это позволяет физическую прокрутку.

Я также хочу прокрутить список по страницам, но если я вызову метод PageDown для внутреннего ScrollViewer списка, первая строка будет обрезана, поскольку высота списка не кратна высоте строки.

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

Может кто-нибудь подсказать, как это сделать?

Ответы [ 2 ]

2 голосов
/ 06 сентября 2011

Вы получите тот же эффект для не виртуализации ItemsControl (ScrollViewer.CanContentScroll="False"), что и для виртуализации, если прокрутить вниз, а затем выбрать верхний видимый контейнер с помощью мыши.Это также может быть сделано в коде.

Когда для CanContentScroll установлено значение false, виртуализация отключена, поэтому все контейнеры будут создаваться всегда.Чтобы получить видимый сверху контейнер, мы можем перебирать контейнеры сверху, пока не достигнем VerticalOffset из ScrollViewer.Получив его, мы можем просто вызвать на нем BringIntoView, и он будет хорошо выравниваться сверху, как если бы использовалась виртуализация.

Пример

<ListBox ItemsSource="{Binding MyCollection}"
         ScrollViewer.CanContentScroll="False"
         ScrollViewer.ScrollChanged="listBox_ScrollChanged" >

Вызовите BringIntoView в верхнем видимом контейнере в обработчике событий

private void listBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    ItemsControl itemsControl = sender as ItemsControl;
    ScrollViewer scrollViewer = e.OriginalSource as ScrollViewer;
    FrameworkElement lastElement = null;
    foreach (object obj in itemsControl.Items)
    {
        FrameworkElement element = itemsControl.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
        double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
        if (offset > e.VerticalOffset)
        {
            if (lastElement != null)
                lastElement.BringIntoView();
            break;
        }
        lastElement = element;
    }
}

Чтобы добиться этого эффекта, только если вы хотите вызвать PageDown, вНапример, нажав кнопку, вы можете создать метод расширения для ListBox с именем LogicalPageDown.

listBox.LogicalPageDown();

ListBoxExtensions

public static class ListBoxExtensions
{
    public static void LogicalPageDown(this ListBox listBox)
    {
        ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(listBox);
        ScrollChangedEventHandler scrollChangedHandler = null;
        scrollChangedHandler = (object sender2, ScrollChangedEventArgs e2) =>
        {
            scrollViewer.ScrollChanged -= scrollChangedHandler;
            FrameworkElement lastElement = null;
            foreach (object obj in listBox.Items)
            {
                FrameworkElement element = listBox.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
                double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
                if (offset > scrollViewer.VerticalOffset)
                {
                    if (lastElement != null)
                        lastElement.BringIntoView();
                    break;
                }
                lastElement = element;
            }
        };
        scrollViewer.ScrollChanged += scrollChangedHandler;
        scrollViewer.PageDown();
    }
}

Я заметил в вашем вопросечто вы уже получили ScrollViewer, но я добавляю реализацию к GetVisualChild, если кто-то еще сталкивался с этим вопросом

public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
    T child = default(T);

    int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
    for (int i = 0; i < numVisuals; i++)
    {
        Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
        child = v as T;
        if (child == null)
        {
            child = GetVisualChild<T>(v);
        }
        if (child != null)
        {
            break;
        }
    }
    return child;
}
0 голосов
/ 31 августа 2011

Если вы прокрутите верхний элемент «новой страницы», достигнете ли вы того, что искали?См. ScrollIntoView .

...