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

C#WinformsTextboxScroll

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

> 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 https://stackoverflow.com/questions/18260702/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 - C#

You can use the following code snippet:

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

which will automatically scroll to the end.

Solution 3 - C#

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 - C#

Try to add the suggested code to the TextChanged event:

private void textBox1_TextChanged(object sender, EventArgs e)
{
  textBox1.SelectionStart = textBox1.Text.Length;
  textBox1.ScrollToCaret();
}

Solution 5 - C#

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 6 - C#

I needed to add a refresh:

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

Solution 7 - C#

I found a simple difference that hasn't been addressed in this thread.

If you're doing all the ScrollToCarat() calls as part of your form's Load() event, it doesn't work. I just added my ScrollToCarat() call to my form's Activated() event, and it works fine.

Edit

It's important to only do this scrolling the first time form's Activated event is fired (not on subsequent activations), or it will scroll every time your form is activated, which is something you probably don't want.

So if you're only trapping the Activated() event to scroll your text when your program loads, then you can just unsubscribe to the event inside the event handler itself, thusly:

Activated -= new System.EventHandler(this.Form1_Activated);

If you have other things you need to do each time your form is activated, you can set a bool to true the first time your Activated() event is fired, so you don't scroll on subsequent activations, but can still do the other things you need to do.

Also, if your TextBox is on a tab that isn't the SelectedTab, ScrollToCarat() will have no effect. So you need at least make it the selected tab while you're scrolling. You can wrap the code in a YourTab.SuspendLayout(); and YourTab.ResumeLayout(false); pair if your form flickers when you do this.

End of edit

Hope this helps!

Solution 8 - C#

I use this. Simple, clean and fast!

txtTCPTxRx.AppendText(newText);

Below is the actual code I use

ThreadSafe(() =>
      {
          string newLog = $"{DateTime.Now:HH:mm:ss:ffff->}{dLog}{Environment.NewLine}";
          txtTCPTxRx.AppendText(newLog);
      });

Solution 9 - C#

With regards to the comment by Pete about a TextBox on a tab, the way I got that to work was adding

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

to the tab's Layout event.

Solution 10 - C#

This will scroll to the end of the textbox when the text is changed, but still allows the user to scroll up

outbox.SelectionStart = outbox.Text.Length;
outbox.ScrollToEnd();

tested on Visual Studio Enterprise 2017

Solution 11 - C#

For anyone else landing here expecting to see a webforms implementation, you want to use the Page Request Manager's endRequest event handler (https://stackoverflow.com/a/1388170/1830512). Here's what I did for my TextBox in a Content Page from a Master Page, please ignore the fact that I didn't use a variable for the control:

var prm = Sys.WebForms.PageRequestManager.getInstance();

function EndRequestHandler() {
	if ($get('<%= ((TextBox)StatusWindow.FindControl("StatusTxtBox")).ClientID %>') != null) {
		$get('<%= ((TextBox)StatusWindow.FindControl("StatusTxtBox")).ClientID %>').scrollTop = 
		$get('<%= ((TextBox)StatusWindow.FindControl("StatusTxtBox")).ClientID %>').scrollHeight;
	}
}

prm.add_endRequest(EndRequestHandler);

Solution 12 - C#

This only worked for me...

txtSerialLogging->Text = "";

txtSerialLogging->AppendText(s);

I tried all the cases above, but the problem is in my case text s can decrease, increase and can also remain static for a long time. static means , static length(lines) but content is different.

So, I was facing one line jumping situation at the end when the length(lines) remains same for some times...

Solution 13 - C#

I use a function for this :

private void Log (string s)	{
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGWLlosaView Question on Stackoverflow
Solution 1 - C#BobView Answer on Stackoverflow
Solution 2 - C#GWLlosaView Answer on Stackoverflow
Solution 3 - C#JohnDRoachView Answer on Stackoverflow
Solution 4 - C#Tommy EngebretsenView Answer on Stackoverflow
Solution 5 - C#Stefan SteigerView Answer on Stackoverflow
Solution 6 - C#h4ndView Answer on Stackoverflow
Solution 7 - C#PeteView Answer on Stackoverflow
Solution 8 - C#sam byteView Answer on Stackoverflow
Solution 9 - C#larrycook99View Answer on Stackoverflow
Solution 10 - C#user7350806View Answer on Stackoverflow
Solution 11 - C#midoriha_senpaiView Answer on Stackoverflow
Solution 12 - C#TooGeekyView Answer on Stackoverflow
Solution 13 - C#DMike92View Answer on Stackoverflow