看着这个解决方案 https://stackoverflow.com/questions/2984803/how-to-automatically-scroll-scrollviewer-only-if-the-user-did-not-change-scrol为了更好的自动滚动,我认为自己很聪明,找到了一个更简单的解决方案,但它仅在调试会话中有效:
private void scrollviewer_Messages_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = sender as ScrollViewer;
if (sv.VerticalOffset == sv.ScrollableHeight)
{
sv.ScrollToEnd();//debug breakpoint
}
return;
}
在此 ScrollViewer 中向文本块添加内容时,自动滚动起作用,文本底部保持在视图中。当用户向上滚动并添加更多内容时,底部将消失,自动滚动关闭,这很好。当用户滚动回到底部时,ScrollToEnd() 应该重新打开自动滚动,但是当添加更多内容时,底部仍然滚动到视图之外。
当我设置断点时,我可以验证 ScrollToEnd() 确实被调用。然后,在删除断点并添加更多内容后,自动滚动再次起作用。
我通过按下按钮、ViewModel 中的代码和绑定来添加内容。所以我确信不存在并发问题。添加内容和手动滚动之间有很多时间。
这确实让我感到困惑,尽管我对简单的自动滚动解决方案非常满意。这怎么行不通呢?
edit:
我发现自动滚动在滚动回底部后再次起作用,但不知何故,真正到达底部并不那么容易。我需要向下移动滑块,然后单击滚动条的向下箭头。我现在将尝试替换==
登录我的代码以允许几个像素的差异。
edit:
这个问题是否是由于内容是带有多行文本字符串和 TextWrap 的 TextBlock 导致的?
<ScrollViewer Name="scrollviewer_Messages" DockPanel.Dock="Top"
Height="100" Width="200"
ScrollChanged="scrollviewer_Messages_ScrollChanged">
<TextBlock Name="tb_Message"
Margin="10" TextWrapping="Wrap"
Text="{Binding Path=Messages}">
</TextBlock>
</ScrollViewer>
edit:
将事件处理程序中的公式更改为:
sv.ScrollableHeight - sv.VerticalOffset < 20
我已经尝试过< 10
但pushpraj(见下面的答案)让我尝试更大的数字。仍然不清楚为什么会这样,因为问题不在于ScrollToEnd()
没有被调用。
关于解决方案:
The <20
不需要,因为它是关于分数的。一般来说,两个实数永远不会相等,但这里情况并非如此。这double
当滑块位于末尾时,偏移量和高度的数字实际上相等。
问题是,显然,ScrollToEnd/Bottom()
使用滑块滚动时不起作用。就是这样。我会称其为错误,但它也可能是一个“功能”:当用户滑动滑块并期望获得控制时,不应更改滑块的行为。
解决方法是,首先我们将滑块滑动到末尾,使 Offset == Height。第二步是添加内容会增加高度,由于上述错误,滑块会向上移动一点,在我的例子中大约是 15 点。这会引发 ScrollChanged 事件和阈值<20
足以接到第二次电话ScrollToBottom
。每次添加内容时都会发生第二步。
我之前的编辑提到单击向下按钮的效果类似。显然,ScrollToEnd 适用于向下按钮。
当然,问题是错误就是错误。一次添加更多内容时,阈值可能不起作用,并且自动滚动可能会停止。
最终的解决方案,不像我希望的那么简单,但仍然不太复杂,应该是我下面的答案中的解决方案。