WPF - reset ListBox scroll position when ItemsSource changes

前端 未结 5 2406
有刺的猬
有刺的猬 2021-02-18 23:41

I currently have a ListBox whose ItemsSource collection is bound to a property on my viewmodel, of type IEnumerable. When that preoprty\'s reference changes, the ListBox update

5条回答
  •  误落风尘
    2021-02-19 00:00

    I'm unable to reproduce your problem (for me, the ListBox is scrolled to the last item in the new collection when changing ItemsSource). Anyway, to scroll the ListBox to the top every time its ItemsSource changes you can use some code behind. First listen to changes in the ItemsSourceProperty and then scroll the ListBox to the top once its items has been generated

    Update

    Made an attached behavior that does this instead to avoid code behind. It can be used like this

    
    

    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(itemsControl) as ScrollViewer;
                    scrollViewer.ScrollToTop();
                    itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
                }
            });
            itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
        }
    }
    

    And an implementation of GetVisualChild

    private T GetVisualChild(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(v);
            }
            if (child != null)
            {
                break;
            }
        }
        return child;
    }
    

提交回复
热议问题