WPF - reset ListBox scroll position when ItemsSource changes

前端 未结 5 2386
有刺的猬
有刺的猬 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:09

    Improved Fredrik Hedblad's answer to work with 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(itemsControl);
                        scrollViewer.ScrollToTop();
                        itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
                    }
                };
            itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
        }
    
        static T GetVisualChild(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(v);
                if (child != null)
                {
                    break;
                }
            }
            return child;
        }
    
        #endregion
    }
    

提交回复
热议问题