How do I automatically scroll to the bottom of a multiline text box?

300,578

Solution 1

At regular intervals, I am adding new lines of text to it. I would like the textbox to automatically scroll to the bottom-most entry (the newest one) whenever a new line is added.

If you use TextBox.AppendText(string text), it will automatically scroll to the end of the newly appended text. It avoids the flickering scrollbar if you're calling it in a loop.

It also happens to be an order of magnitude faster than concatenating onto the .Text property. Though that might depend on how often you're calling it; I was testing with a tight loop.


This will not scroll if it is called before the textbox is shown, or if the textbox is otherwise not visible (e.g. in a different tab of a TabPanel). See TextBox.AppendText() not autoscrolling. This may or may not be important, depending on if you require autoscroll when the user can't see the textbox.

It seems that the alternative method from the other answers also don't work in this case. One way around it is to perform additional scrolling on the VisibleChanged event:

textBox.VisibleChanged += (sender, e) =>
{
    if (textBox.Visible)
    {
        textBox.SelectionStart = textBox.TextLength;
        textBox.ScrollToCaret();
    }
};

Internally, AppendText does something like this:

textBox.Select(textBox.TextLength + 1, 0);
textBox.SelectedText = textToAppend;

But there should be no reason to do it manually.

(If you decompile it yourself, you'll see that it uses some possibly more efficient internal methods, and has what seems to be a minor special case.)

Solution 2

You can use the following code snippet:

myTextBox.SelectionStart = myTextBox.Text.Length;
myTextBox.ScrollToCaret();

which will automatically scroll to the end.

Solution 3

It seems the interface has changed in .NET 4.0. There is the following method that achieves all of the above. As Tommy Engebretsen suggested, putting it in a TextChanged event handler makes it automatic.

textBox1.ScrollToEnd();

Solution 4

textBox1.Focus()
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();

didn't work for me (Windows 8.1, whatever the reason).
And since I'm still on .NET 2.0, I can't use ScrollToEnd.

But this works:

public class Utils
{
    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern int SendMessage(System.IntPtr hWnd, int wMsg, System.IntPtr wParam, System.IntPtr lParam);

    private const int WM_VSCROLL = 0x115;
    private const int SB_BOTTOM = 7;

    /// <summary>
    /// Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    /// </summary>
    /// <param name="tb">The text box to scroll</param>
    public static void ScrollToBottom(System.Windows.Forms.TextBox tb)
    {
        if(System.Environment.OSVersion.Platform != System.PlatformID.Unix)
             SendMessage(tb.Handle, WM_VSCROLL, new System.IntPtr(SB_BOTTOM), System.IntPtr.Zero);
    }


}

VB.NET:

Public Class Utils
    <System.Runtime.InteropServices.DllImport("user32.dll", CharSet := System.Runtime.InteropServices.CharSet.Auto)> _
    Private Shared Function SendMessage(hWnd As System.IntPtr, wMsg As Integer, wParam As System.IntPtr, lParam As System.IntPtr) As Integer
    End Function

    Private Const WM_VSCROLL As Integer = &H115
    Private Const SB_BOTTOM As Integer = 7

    ''' <summary>
    ''' Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    ''' </summary>
    ''' <param name="tb">The text box to scroll</param>
    Public Shared Sub ScrollToBottom(tb As System.Windows.Forms.TextBox)
        If System.Environment.OSVersion.Platform <> System.PlatformID.Unix Then
            SendMessage(tb.Handle, WM_VSCROLL, New System.IntPtr(SB_BOTTOM), System.IntPtr.Zero)
        End If
    End Sub


End Class

Solution 5

I needed to add a refresh:

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
Share:
300,578
GWLlosa
Author by

GWLlosa

Widely experienced .NET/Data developer moving to Denver for a fresh start.

Updated on July 08, 2022

Comments

  • GWLlosa
    GWLlosa almost 2 years

    I have a textbox with the .Multiline property set to true. At regular intervals, I am adding new lines of text to it. I would like the textbox to automatically scroll to the bottom-most entry (the newest one) whenever a new line is added. How do I accomplish this?

  • GWLlosa
    GWLlosa almost 15 years
    Looked here for the answer, couldn't find it, so when I figured it out, I figured I'd put it up here for future users, or if maybe someone else had a better approach.
  • Bob
    Bob about 11 years
    Note that that method is in the TextBoxBase class in the System.Windows.Controls.Primitives namespace (PresentationFramework assembly, WPF). This method does not exist and will not work in WinForms, whose TextBox class inherits from TextBoxBase in the System.Windows.Forms namespace (System.Windows.Forms assembly, WinForms).
  • tomsv
    tomsv almost 11 years
    This may have been the best answer at the time, but now I think Bob's answer is a better solution to the OP's problem.
  • EMBEDONIX
    EMBEDONIX over 10 years
    Was eating myself trying to make it with tb.Text += .... and WndProc and marshals Now I feel stupid :D
  • hello_earth
    hello_earth about 10 years
    still, for me (.NET 3.5) things only worked when I added the suggested code with SelectionStart & ScrollToCaret to TextChanged event handler (see below), because otherwise at some point (not always), the scroll would be reset to the beginning (probably the best solution would be to override that default code..)
  • Bob
    Bob about 10 years
    @hello_earth I have updated with a workaround for the case where the textbox is not scrolling while invisible. You can try and see if that works for you.
  • Qwerty01
    Qwerty01 about 10 years
    The textarea also needs to be focused, the first time I did this it did not scroll because it did not have the focus.
  • Shawn Kovac
    Shawn Kovac over 9 years
    i was setting the text in the Form's Load event. i was unable to get my TextBox to scroll to the bottom when i put txtInfo.SelectionStart = txtInfo.Text.Length; txtInfo.ScrollToCaret(); txtInfo.Refresh(); in the Load event. i tried to put this code in the TextBox's VisibleChanged event too, to no avail. what i did that worked was putting this code (txtInfo.SelectionStart = txtInfo.Text.Length; txtInfo.ScrollToCaret();) in a timer tick event & then start that timer at the end of the Load event. it worked even with 1 ms delay. not sure why it just didn't work for me in the Load event.
  • TooGeeky
    TooGeeky over 9 years
    I know, it is similar to Bob's answer, but explains a specific case. AND I can't comment on Bob's answer... Stuck with stackoverflow rules :(
  • Elshan
    Elshan almost 9 years
    textBox.VisibleChanged not work.But I change that into txtResponse.TextChanged and it's work.
  • John
    John over 8 years
    @Bob thanks for the tip. Works great! I would add that you might want to add Environment.Newline to each string added.
  • Hannes
    Hannes over 8 years
    Had the same issue with Windows 10, your workaround works fine here too.
  • Hugo Yates
    Hugo Yates over 8 years
    I had the issue where it would refuse to scroll to the end. I was loading in a log file at the start of my application. The solution was to put the code from above answer in the 'Shown' event (ie MyForm_Shown(object sender, EventArgs e)).
  • Brandon Barkley
    Brandon Barkley over 6 years
    AppendText did not automatically scroll my ReadOnly TextBox, but adding TextBox.ScrollToEnd(); after the AppendText call did the trick.
  • ergohack
    ergohack over 6 years
    Note that ScrollToEnd() can be extremely poor performing. In my app it accounted for over 50% of the profiling time.
  • Leonardo Hernández
    Leonardo Hernández over 2 years
    You probably prefer to override the OnShown method: "protected override void OnShown (EventArgs e)" instead of putting an event handler for Activate.
  • Xavi
    Xavi over 2 years
    Works for me (Windows 10) Thx
  • TByte
    TByte about 2 years
    Other answers didn't work, this one did. Windows 10, 4.7.2.