WPF - 在ItemsSource更改时重置ListBox滚动位置

dev*_*tal 18 wpf scroll listbox itemssource

我目前有一个ListBox,其ItemsSource集合绑定到我的viewmodel上的一个属性,类型为IEnumerable.当preoprty的引用发生更改时,ListBox会按预期更新,但是我有一个问题,如果我有大量项目并滚动到ListBox的底部,然后将引用更改为包含,例如,1项目的另一个集合,ListBox视图为空,不显示滚动条.然后我必须用鼠标滚轮向上滚动列表框,直到1项进入视图.

因此,我认为我所追求的是,​​只要ItemsSource属性发生更改,就会将ListBox的滚动位置重置为顶部,这样无论收集的大小有多少,都会始终显示某些内容.

Fre*_*lad 21

我无法重现您的问题(对我而言,ListBox更改时会滚动到新集合中的最后一项ItemsSource).无论如何,要在ListBox每次ItemsSource更改时滚动到顶部,您可以使用一些代码.首先听取其中的更改,ItemsSourceProperty然后ListBox在生成项目后滚动到顶部

更新

做了一个附加行为,这样做可以避免代码落后.它可以像这样使用

<ListBox ...
         behaviors:ScrollToTopBehavior.ScrollToTop="True"/>
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并实现了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;
}
Run Code Online (Sandbox Code Playgroud)


Ere*_*mez 7

迟到的答案:

一个简单的解决方案是为事件添加事件处理程序TargetUpdated,并NotifyOnTargetUpdated=TrueItemsSource绑定上设置:

<ListBox x:Name="listBox" 
         ItemsSource="{Binding MySource, NotifyOnTargetUpdated=True}"
         TargetUpdated="ListBox_TargetUpdated"/>
Run Code Online (Sandbox Code Playgroud)

并在事件处理程序中,滚动到顶部项目:

private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e)
{
    if (listBox.Items.Count > 0)
    {
        listBox.ScrollIntoView(listBox.Items[0]);
    }
}
Run Code Online (Sandbox Code Playgroud)