gpt4 book ai didi

c# - 使用 ScrollViewer.ScrollToEnd() 执行 AutoScroll 仅在调试时有效,事件处理程序太简单

转载 作者:太空狗 更新时间:2023-10-30 01:18:47 29 4
gpt4 key购买 nike

查看this solution为了更好的自动滚动,我认为自己很聪明,找到了一个更简单的解决方案,但它只在调试 session 中有效:

    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 中向文本 block 添加内容时,自动滚动有效,文本底部保持在 View 中。当用户向上滚动并添加更多内容时,底部消失,自动滚动关闭,这很好。当用户滚动回到底部时,ScrollToEnd() 应该重新打开自动滚动,但是当添加更多内容时,底部仍然滚动到 View 之外。

当我设置断点时,我可以验证确实调用了 ScrollToEnd()。然后,在删除断点并添加更多内容后,自动滚动再次起作用。

我通过按下按钮、ViewModel 中的代码和绑定(bind)来添加内容。所以我确定没有并发问题。添加内容和手动滚动之间有很多时间。

这真的让我感到困惑,虽然我对我的简单自动滚动解决方案非常满意。这怎么行不通?


编辑:

我发现滚动回到底部后自动滚动又起作用了,但不知何故真正触底并不是那么容易。我需要向下移动 slider ,然后单击滚动条的向下箭头。我现在将尝试替换 ==登录我的代码以允许几个像素的差异。


编辑:

这个问题会不会是因为内容是一个包含多行文本字符串和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>

编辑:

将事件处理程序中的公式更改为:

 sv.ScrollableHeight - sv.VerticalOffset < 20

我已经尝试过 < 10但是 pushpraj(见下面的答案)让我尝试了更大的数字。仍然不清楚为什么会这样,因为问题不在于 ScrollToEnd()没有被调用。


关于解决方案:

<20不需要,因为它是关于小数的。一般来说,两个实数永远不会相等,但在这里情况并非如此。 double当 slider 位于末尾时,偏移量和高度的数字实际上是相等的。

问题是,显然,ScrollToEnd/Bottom()使用 slider 滚动时不起作用。就是这样。我会称之为错误,但它也可能是一个“功能”:当用户滑动 slider 并期望控制时,不应更改 slider 的行为。

解决方法是,首先我们将 slider 滑动到末端,使 Offset == Height。第二步是添加内容会增加高度,由于上面的错误, slider 会向上移动一点点,在我的例子中大约是 15 点。这会引发 ScrollChanged 事件和 <20 的阈值足以接到第二次电话 ScrollToBottom .每次添加内容时都会执行第二步。

我之前的编辑提到单击向下按钮的工作方式类似。显然,ScrollToEnd 适用于向下按钮。

问题当然是错误就是错误。一次添加更多内容时,阈值可能不起作用,并且自动滚动可能会停止。

最终的解决方案,不像我希望的那么简单,但也不会太复杂,应该是我下面的答案。

最佳答案

问题的原因是 ScrollToEnd() 与自动滚动无关。这个调用只是滚动到最后,就是这样。通过将调用放在事件处理程序中,它会经常滚动到末尾,但对于真正的自动滚动,有必要确定是谁触发了事件:用户通过移动 slider ,或者由于内容大小的变化而移动 slider 。不再通过查看 ExtentHeight 来忽略“无用”事件,而是使用此属性来确定触发事件的人或事件。

此解决方案将自动滚动位的状态保存在控件的标记中。将新的用户控件 AutoScrollViewer 子类化会更好。

毕竟,这个解决方案并不比问题中上面提到的以前的解决方案“简单”多少,它只是一个变体,但(希望)更准确。

    /// <summary>
/// If the scrollviewer is at the bottom, keep the bottom in view.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void scrollviewer_Messages_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer sv = sender as ScrollViewer;
bool AutoScrollToEnd = true;
if (sv.Tag != null)
{
AutoScrollToEnd = (bool)sv.Tag;
}
if (e.ExtentHeightChange == 0)// user scroll
{
AutoScrollToEnd = sv.ScrollableHeight == sv.VerticalOffset;
}
else// content change
{
if (AutoScrollToEnd)
{
sv.ScrollToEnd();
}
}
sv.Tag = AutoScrollToEnd;
return;
}

关于c# - 使用 ScrollViewer.ScrollToEnd() 执行 AutoScroll 仅在调试时有效,事件处理程序太简单,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25761795/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com