我遇到了同样的问题,还有一个限制:用户不选择项目,而只是滚动。这就是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 添加到私有 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 的引用。在我的情况下,项目被添加到集合的开头。问题是视口位置不会改变。位置为 0 时可以:视口中的第一个元素将始终是第一项。但是当视口中的第一个元素有另一个索引而不是 0 并且添加了新项目时 - 该元素的索引会增加,因此它会向下滚动。所以bool值表示ScrollViewer的垂直偏移量是否不为0,如果为0则不需要做任何事情,如果不为0则需要保留其相对于viewport中item的位置:
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来表示增加的item的数量。在我的情况下,项目只在集合的开头添加,所以我需要做的就是将 ScrollViewer 的 VerticalOffset 增加这个值。
最后一件事:使用。下面是 ListBox 的示例:
<Listbox ...>
<ListBox.Resourses>
<Style TargetType="ScrollViewer">
<Setter Property="local:ScrollPreserver.PreserveScroll" Value="True" />
</Style>
</ListBox.Resourses>
</ListBox>
哒哒!很好用:)