wpf - 当集合更改时保留绑定(bind)的 WPF ListBox 的滚动位置

标签 wpf listbox

我有一个绑定(bind)到 ObservableCollection 的 WPF ListBox。将内容添加到集合中时,ListBox 滚动位置会根据添加条目的大小进行移动。我希望能够保留滚动位置,这样即使将内容添加到列表中,当前 View 中的项目也不会移动。有办法实现吗?

最佳答案

我遇到了同样的问题和一个限制:用户不能选择项目,而只能滚动。这就是为什么 ScrollIntoView() 方法没有用。这是我的解决方案。

首先,我创建了从 DependencyObject 派生的类 ScrollPreserver,并附加了 bool 类型的依赖属性 PreserveScroll:

public class ScrollPreserver : DependencyObject
{
    public static readonly DependencyProperty PreserveScrollProperty =
        DependencyProperty.RegisterAttached("PreserveScroll", 
            typeof(bool),
            typeof(ScrollPreserver), 
            new PropertyMetadata(new PropertyChangedCallback(OnScrollGroupChanged)));

    public static bool GetPreserveScroll(DependencyObject invoker)
    {
        return (bool)invoker.GetValue(PreserveScrollProperty);
    }

    public static void SetPreserveScroll(DependencyObject invoker, bool value)
    {
        invoker.SetValue(PreserveScrollProperty, value);
    }

    ...
}

属性更改回调假定属性是由 ScrollViewer 设置的。回调方法将此 ScrollViewer 添加到私有(private) Dictionary 中,并向其中添加 ScrollChanged 事件处理程序:

private static Dictionary<ScrollViewer, bool> scrollViewers_States = 
    new Dictionary<ScrollViewer, bool>();

private static void OnScrollGroupChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ScrollViewer scrollViewer = d as ScrollViewer;
    if (scrollViewer != null && (bool)e.NewValue == true)
    {
        if (!scrollViewers_States.ContainsKey(scrollViewer))
        {
            scrollViewer.ScrollChanged += new    ScrollChangedEventHandler(scrollViewer_ScrollChanged);
            scrollViewers_States.Add(scrollViewer, false);
        }
    }
}

该字典将保存对使用此类的应用程序中所有 ScrollViewer 的引用。就我而言,项目被添加到集合的开头。问题是视口(viewport)位置没有改变。当位置为 0 时就可以了:视口(viewport)中的第一个元素将始终是第一个项目。但是,当视口(viewport)中的第一个元素的索引不是 0 并且添加新项目时,该元素的索引会增加,因此会向下滚动。所以 bool 值指示 ScrollViewer 的垂直偏移是否不为 0。如果为 0,则无需执行任何操作,如果不为 0,则需要保留其相对于视口(viewport)中项目的位置:

static void scrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
    if (scrollViewers_States[sender as ScrollViewer])
        (sender as ScrollViewer).ScrollToVerticalOffset(e.VerticalOffset + e.ExtentHeightChange);

    scrollViewers_States[sender as ScrollViewer] = e.VerticalOffset != 0;
}

ExtentHeightChange此处用于指示添加项目的数量。在我的例子中,项目仅添加在集合的开头,因此我所需要做的就是将 ScrollViewer 的 VerticalOffset 增加该值。 最后一件事:使用。这是列表框的示例:

<Listbox ...> 
    <ListBox.Resourses>
        <Style TargetType="ScrollViewer">
            <Setter Property="local:ScrollPreserver.PreserveScroll" Value="True" />
        </Style>
    </ListBox.Resourses>
</ListBox>

哒哒!效果很好:)

关于wpf - 当集合更改时保留绑定(bind)的 WPF ListBox 的滚动位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/860764/

相关文章:

wpf - 动态更改数据模板

c# - 访问嵌套的静态常量

c# - WPF 列表框项目消耗太多内存

javascript - 如何检查列表框项目是否被选中?

wpf - 元素主机 : Images in XAML Ribbon Causing Issues

c# - WPF 应用程序,字体图形错误

wpf - 在 WPF 中,什么将 ListBoxItem 与 ListBox 相关联?

c# - 列表框 WPF : change foreground color of SelectedItem and keep Material Design?

C#:用户代码未处理 ArgumentOutOfRangeException

wpf - ListBox ItemTemplate 中的两种方式绑定(bind)