WPF - сброс позиции прокрутки ListBox при изменении ItemsSource - PullRequest
17 голосов
/ 25 января 2011

В настоящее время у меня есть ListBox, чья коллекция ItemsSource связана со свойством в моей модели представления типа IEnumerable.При изменении ссылки этого preoprty список ListBox обновляется, как и ожидалось, однако у меня есть проблема в том, что если у меня есть большая коллекция элементов, прокрутите до нижней части ListBox, а затем измените ссылку на другую коллекцию, содержащую, скажем, 1 элементпредставление ListBox пустое и полоса прокрутки не отображается.Затем мне нужно прокрутить список с помощью колесика мыши, пока не появится 1 элемент.

Итак, то, что я думаю, это способ сброса позиции прокрутки ListBox наtop, при каждом изменении свойства ItemsSource, чтобы всегда отображалось что-то, независимо от того, насколько большой или маленький размер коллекции.

Ответы [ 5 ]

21 голосов
/ 25 января 2011

Я не могу воспроизвести вашу проблему (для меня ListBox прокручивается до последнего элемента в новой коллекции при изменении ItemsSource). В любом случае, для прокрутки ListBox к вершине каждый раз, когда меняется ItemsSource, вы можете использовать некоторый код позади. Сначала прослушайте изменения в ItemsSourceProperty, а затем прокрутите ListBox до верха, как только его элементы будут сгенерированы

Обновление

Сделал прикрепленное поведение, которое делает это, чтобы избежать кода позади. Может использоваться следующим образом

<ListBox ...
         behaviors:ScrollToTopBehavior.ScrollToTop="True"/>

ScrollToTopBehavior

public static class ScrollToTopBehavior 
{
    public static readonly DependencyProperty ScrollToTopProperty = 
        DependencyProperty.RegisterAttached 
        (
            "ScrollToTop", 
            typeof(bool),
            typeof(ScrollToTopBehavior),
            new UIPropertyMetadata(false, OnScrollToTopPropertyChanged) 
        );
    public static bool GetScrollToTop(DependencyObject obj) 
    {
        return (bool)obj.GetValue(ScrollToTopProperty); 
    }
    public static void SetScrollToTop(DependencyObject obj, bool value) 
    {
        obj.SetValue(ScrollToTopProperty, value); 
    }
    private static void OnScrollToTopPropertyChanged(DependencyObject dpo, 
                                                     DependencyPropertyChangedEventArgs e) 
    {
        ItemsControl itemsControl = dpo as ItemsControl;
        if (itemsControl != null) 
        {
            DependencyPropertyDescriptor dependencyPropertyDescriptor =
                    DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
            if (dependencyPropertyDescriptor != null)
            {
                if ((bool)e.NewValue == true) 
                {
                    dependencyPropertyDescriptor.AddValueChanged(itemsControl, ItemsSourceChanged);
                }
                else 
                {
                    dependencyPropertyDescriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged);
                }
            } 
        } 
    }
    static void ItemsSourceChanged(object sender, EventArgs e)
    {
        ItemsControl itemsControl = sender as ItemsControl;
        EventHandler eventHandler = null;
        eventHandler = new EventHandler(delegate
        {
            if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
            {
                ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(itemsControl) as ScrollViewer;
                scrollViewer.ScrollToTop();
                itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
            }
        });
        itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
    }
}

И реализация GetVisualChild

private 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;
}
7 голосов
/ 12 февраля 2015

Поздний ответ :

Простым решением является добавление обработчика событий для события TargetUpdated и установка NotifyOnTargetUpdated=True в привязке ItemsSource:

<ListBox x:Name="listBox" 
         ItemsSource="{Binding MySource, NotifyOnTargetUpdated=True}"
         TargetUpdated="ListBox_TargetUpdated"/>

и в обработчике событий выделите верхний элемент:

private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e)
{
    if (listBox.Items.Count > 0)
    {
        listBox.ScrollIntoView(listBox.Items[0]);
    }
}
1 голос
/ 25 января 2011

Попробуйте это:

if (listBox.Items.Count > 0) {
    listBox.ScrollIntoView(listBox.Items[0]); 
}
0 голосов
/ 11 мая 2017

Когда вы форматируете элемент управления, вы выбираете диапазон ячеек в качестве вариантов выбора, которые затем перечисляются в списке.Вы также выбираете ячейку в качестве ссылки на выбранные варианты, в которых число будет отображаться в зависимости от позиции выбора в списке.1 для первого в списке, 2 для второго и т. Д. Код довольно прост: -

Range ("A1") Select

Selection = 1

Change ("A1 ") к ячейке, к которой вы привязали, и измените 1 на позицию в списке, который вы хотите выбрать.

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

0 голосов
/ 22 июля 2015

Улучшен ответ Фредрика Хедблада для работы с ObservableCollection:

public static class ItemsControlAttachedProperties
{
    #region ScrollToTopOnItemsSourceChange Property

    public static readonly DependencyProperty ScrollToTopOnItemsSourceChangeProperty =
        DependencyProperty.RegisterAttached(
            "ScrollToTopOnItemsSourceChange",
            typeof(bool),
            typeof(ItemsControlAttachedProperties),
            new UIPropertyMetadata(false, OnScrollToTopOnItemsSourceChangePropertyChanged));

    public static bool GetScrollToTopOnItemsSourceChange(DependencyObject obj)
    {
        return (bool) obj.GetValue(ScrollToTopOnItemsSourceChangeProperty);
    }

    public static void SetScrollToTopOnItemsSourceChange(DependencyObject obj, bool value)
    {
        obj.SetValue(ScrollToTopOnItemsSourceChangeProperty, value);
    }

    static void OnScrollToTopOnItemsSourceChangePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var itemsControl = obj as ItemsControl;
        if (itemsControl == null)
        {
            throw new Exception("ScrollToTopOnItemsSourceChange Property must be attached to an ItemsControl based control.");
        }

        DependencyPropertyDescriptor descriptor =
            DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
        if (descriptor != null)
        {
            if ((bool) e.NewValue)
            {
                descriptor.AddValueChanged(itemsControl, ItemsSourceChanged);
            }
            else
            {
                descriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged);
            }
        }
    }

    static void ItemsSourceChanged(object sender, EventArgs e)
    {
        var itemsControl = sender as ItemsControl;
        DoScrollToTop(itemsControl);

        var collection = itemsControl.ItemsSource as INotifyCollectionChanged;
        if (collection != null)
        {
            collection.CollectionChanged += (o, args) => DoScrollToTop(itemsControl);
        }
    }

    static void DoScrollToTop(ItemsControl itemsControl)
    {
        EventHandler eventHandler = null;
        eventHandler =
            delegate
            {
                if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
                {
                    var scrollViewer = GetVisualChild<ScrollViewer>(itemsControl);
                    scrollViewer.ScrollToTop();
                    itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
                }
            };
        itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
    }

    static T GetVisualChild<T>(DependencyObject parent) where T : Visual
    {
        T child = default(T);
        int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
        for (var i = 0; i < numVisuals; i++)
        {
            var v = (Visual) VisualTreeHelper.GetChild(parent, i);
            child = v as T ?? GetVisualChild<T>(v);
            if (child != null)
            {
                break;
            }
        }
        return child;
    }

    #endregion
}
...