Подтвердить что ты не робот

WPF - reset Позиция прокрутки ListBox при изменении ItemsSource

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

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

4b9b3361

Ответ 1

Я не могу воспроизвести вашу проблему (для меня 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;
}

Ответ 2

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

Простым решением является добавление обработчика событий для события 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]);
    }
}

Ответ 3

Попробуйте следующее:

if (listBox.Items.Count > 0) {
    listBox.ScrollIntoView(listBox.Items[0]); 
}

Ответ 4

Усовершенствован Fredrik Hedblad для работы с 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
}

Ответ 5

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

Range ( "A1" ) Выберите

Выбор = 1

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

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