Reputation: 9141
I've a RichTextBox
in my app which is getting new content on certain events.
When new content is added, I'd like to scroll to the bottom, only if the scroll was at the bottom before.
How do I do this?
More specifically, the part that gives me trouble is determining the scroll position.
If it matters, the RichTextBox
is using the default style and template, a few brushes changed or set to null, vertical scrollbar visibility is Auto and it's read-only.
Upvotes: 4
Views: 2437
Reputation: 2533
Much simpler condition to scroll is VerticalOffset + ViewportHeight >= ExtentHeight
Example:
bool shouldScroll = rtbx.VerticalOffset + rtbx.ViewportHeight >=
rtbx.ExtentHeight;
// changes to RichTextBox
// ...
if(shouldScroll) rtbx.ScrollToEnd();
Works also for the 'scrollbar just appeared' case.
Upvotes: 3
Reputation: 18102
Try this extension method:
public static class RichTextBoxExtensions
{
public static void ScrollIfNeeded(this RichTextBox textBox)
{
var offset = textBox.VerticalOffset + textBox.ViewportHeight;
if (Math.Abs(offset - textBox.ExtentHeight) > double.Epsilon) return;
textBox.ScrollToEnd();
}
}
And use it like this:
textBox.AppendText(// Very long text here);
textBox.ScrollIfNeeded();
EDIT: Alternative including scrolling to bottom when the scrollbar becomes visible:
public static class RichTextBoxExtensions
{
public static void ScrollIfNeeded(this RichTextBox textBox)
{
var offset = textBox.VerticalOffset + textBox.ViewportHeight;
if (Math.Abs(offset - textBox.ExtentHeight) <= double.Epsilon)
{
textBox.ScrollToEnd();
}
else
{
var contentIsLargerThatViewport = textBox.ExtentHeight > textBox.ViewportHeight;
if (Math.Abs(textBox.VerticalOffset - 0) < double.Epsilon && contentIsLargerThatViewport)
{
textBox.ScrollToEnd();
}
}
}
}
Upvotes: 0
Reputation: 6466
If you want a rich textbox to auto scroll with new added text only when the scrollbar has been dragged to the bottom add the following class to your project
public class RichTextBoxThing : DependencyObject
{
public static bool GetIsAutoScroll(DependencyObject obj)
{
return (bool)obj.GetValue(IsAutoScrollProperty);
}
public static void SetIsAutoScroll(DependencyObject obj, bool value)
{
obj.SetValue(IsAutoScrollProperty, value);
}
public static readonly DependencyProperty IsAutoScrollProperty =
DependencyProperty.RegisterAttached("IsAutoScroll", typeof(bool), typeof(RichTextBoxThing), new PropertyMetadata(false, new PropertyChangedCallback((s, e) =>
{
RichTextBox richTextBox = s as RichTextBox;
if (richTextBox != null)
{
if ((bool)e.NewValue)
richTextBox.TextChanged += richTextBox_TextChanged;
else if ((bool)e.OldValue)
richTextBox.TextChanged -= richTextBox_TextChanged;
}
})));
static void richTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
RichTextBox richTextBox = sender as RichTextBox;
if ((richTextBox.VerticalOffset + richTextBox.ViewportHeight) == richTextBox.ExtentHeight || richTextBox.ExtentHeight < richTextBox.ViewportHeight)
richTextBox.ScrollToEnd();
}
}
then on any rich textbox that you want the auto scroll behaviour add the IsAutoSroll property
<RichTextBox ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" local:RichTextBoxThing.IsAutoScroll="True"/>
Upvotes: 4
Reputation: 35584
You can basically do the following: Get the scrollbar, and subscribe to the changes of Value
, Maximum
and Minimum
(they are all dependency properties). This way you can control the position in the code-behind by setting the Value
to Maximum
whenever you need to.
Now, how can you access the scrollbar? There are several ways. If you are sure which is the control template of your RichTextBox
, you can get it using GetTemplateChild(name)
(you get the name by examining the template in e.g. Blend). If you are not sure, you should better create your own template (again, Blend would give you a good template to start off) and applying it to the RichTextBox
you're interested in.
Upvotes: 1