有时候我们在动态添加内容时,需要将滚动条滚动到指定内容处。
一般我们会调用ScrollViewer的ScrollToVerticalOffset(垂直方向)函数和ScrollToHorizontalOffset(水平方向)函数来控制滚动条滚动到指定位置。
例如我们界面上有一个ListBox,我们想让滚动条滚动到指定项
XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" Name="scroll">
<ListBox Name="list" Background="Transparent"></ListBox>
</ScrollViewer>
<Button Content="普通滚动" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="88" Click="Button_Click" Margin="-120,0,0,0"></Button>
<Button Content="平滑滚动" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="88" Click="Button_Click_1" Margin="120,0,0,0"></Button>
</Grid>.cs
//随机选中一项
Random r = new Random();
var item = this.list.Items[r.Next(0, 50)];
ListBoxItem listBoxItem = list.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
// 获取选择元素的位置
Point position = listBoxItem.TranslatePoint(new Point(0, 0), list);
//滚动到指定位置
this.scroll.ScrollToVerticalOffset(position.Y);
this.list.SelectedItem = item;说明:ListBox提供了一个ScrollIntoView函数可以滚动到指定项,但是直接调用ScrollViewer的函数可以适用于所有出现滚动条的场景。
运行效果如下:
一开始我想的是通过一个循环,缓动增加Y的位置,这样就达到了动画效果。这种方案是可行的,示例代码如下
Random r = new Random();
var item = this.list.Items[r.Next(0, 50)];
ListBoxItem listBoxItem = list.ItemContainerGenerator.ContainerFromItem(item) as ListBoxItem;
// 获取选择元素的位置
Point position = listBoxItem.TranslatePoint(new Point(0, 0), list);
var gap = position.Y - this.scroll.VerticalOffset;
//假设分5次
var tick = (int)(gap / 5);
int y = (int)this.scroll.VerticalOffset;
for (int i = 0; i < 5; i++)
{
y += tick;
this.scroll.ScrollToVerticalOffset(y);
//缓慢滚动到指定位置
await Task.Delay(50);
}
this.scroll.ScrollToVerticalOffset(position.Y);
this.list.SelectedItem = item;我们也可以借助WPF的Animation来做,这样效果会更好。
实现原理如下:
1、新建一个辅助类,里面定义一个附加属性
2、当这个附加属性的值更新时,我们去调用ScrollToVerticalOffset进行滚动
3、用ScrollViewer对这个附加属性进行动画
public static class ScrollViewerHelper
{
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached(
"VerticalOffset",
typeof(double),
typeof(ScrollViewerHelper),
new PropertyMetadata(0.0, OnVerticalOffsetChanged));
}private static void OnVerticalOffsetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ScrollViewer scrollViewer)
{
scrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
}DoubleAnimation animation = new DoubleAnimation
{
From = scrollViewer.VerticalOffset,
To = targetOffset,
Duration = TimeSpan.FromSeconds(durationInSeconds),
EasingFunction = new QuadraticEase { EasingMode = EasingMode.EaseOut }
};
animation.Completed += (s, e) => scrollViewer.ScrollToVerticalOffset(targetOffset);
scrollViewer.BeginAnimation(ScrollViewerHelper.VerticalOffsetProperty, animation);演示效果
这里还可以进行一定的优化,可以让选中项始终居中。
https://files-cdn.cnblogs.com/files/zhaotianff/SmoothingScrollViewer.zip?t=1740556904&download=true