Looking at this solution for a better autoscroll I thought myself to be so clever to find an easier solution, but it works only in a debug 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;
}
While adding content to the textblock in this ScrollViewer, autoscroll works, the bottom of the text stays in view. When the user scrolls upwards, and more content is added, the bottom goes out of view, autoscroll is off, which is fine. When the user scrolls back to the bottom, ScrollToEnd() should turn autoscroll back on, but when more content is added, the bottom still scrolls out of view.
When I set the breakpoint, I can verify that ScrollToEnd() is indeed called. Then, after removing the breakpoint, and adding more content, autoscroll works again.
I add content by pressing a button, with code in the ViewModel, and Binding. So I am sure there is no concurrency problem. There is much time between adding content and manual scrolling.
This really baffles me, while I was so happy with my simple autoscroll solution. How can this not work?
edit:
I found out that autoscroll works again after scrolling back to the bottom, but somehow it is not so easy to really hit the bottom. I need to move the slider way down, AND click the down arrow of the scrollbar. I will now experiment with replacing the ==
sign in my code to allow a few pixels difference.
edit:
Would this problem be caused by the fact that the content is a TextBlock with a multi-line text string and TextWrap?
<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:
The problem went away with changing the formula in the event handler to:
sv.ScrollableHeight - sv.VerticalOffset < 20
I experimented already with < 10
but pushpraj (see answer below) made me try larger numbers. Still unclear why this works, as the problem is not that ScrollToEnd()
was not called.
about the solution:
The <20
is not needed because it is about fractional numbers. In general, two real numbers are never equal, but here that is not true. The double
numbers for offset and height are really equal when the slider is at the end.
The problem is that, apparently, ScrollToEnd/Bottom()
does not work while scrolling with the slider. That's it. I would call it a bug, but it might as well be a 'feature': one should not change the behaviour of the slider while the user is sliding it and expecting to be in control.
The fix is that first we slide the slider to the end, making Offset == Height. Step two is that adding content will increase Height, due to above bug the slider will move up just a little, in my case about 15 points. This raises a ScrollChanged event and the threshold of <20
is ample enough to get a second call of ScrollToBottom
. This step two happens each time content is added.
My earlier edit mentioning clicking the down button works similar. Apparently, ScrollToEnd works for the down button.
The catch of course is that a bug is a bug. When adding more content at once, the threshold may not work, and autoscroll might stop.
The ultimate solution, not as simple as I was hoping for, but still not too complex, should be the one in my answer below.