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?
-
6Looked 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. – GWLlosa May 22 '09 at 14:59
-
3I needed to do the same thing in VBA, which doesn't have all these fancy-pants new .NET methods. For future google-fu, here is the incantation: TextBox1.Text = TextBox1.Text & "whatever"; TextBox1.SelStart = Len(TextBox1.Text); TextBox1.SetFocus; ... and then a .SetFocus back to whatever control had the focus before. Without giving the focus to TextBox1, it would never update its scrollbars no matter what I did. – Gordon Broom May 16 '14 at 20:05
-
1@GordonBroom Whelp, thanks to that I'm going to start calling "code snippets" "incantations" now. Good work. :D – Sidney Aug 19 '16 at 17:47
13 Answers
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.)
-
4Was eating myself trying to make it with `tb.Text += ....` and WndProc and marshals Now I feel stupid :D – Dumbo Nov 22 '13 at 23:35
-
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..) – hello_earth Mar 06 '14 at 12:27
-
@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. – Bob Mar 06 '14 at 14:00
-
3The textarea also needs to be focused, the first time I did this it did not scroll because it did not have the focus. – Qwerty01 Mar 13 '14 at 01:10
-
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. – Shawn Kovac Aug 15 '14 at 02:44
-
1`textBox.VisibleChanged` not work.But I change that into txtResponse.TextChanged and it's work. – Elshan Jun 26 '15 at 08:32
-
@Bob thanks for the tip. Works great! I would add that you might want to add Environment.Newline to each string added. – John Sep 27 '15 at 07:15
-
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)`). – Hugo Yates Dec 01 '15 at 12:05
-
3AppendText did not automatically scroll my ReadOnly TextBox, but adding TextBox.ScrollToEnd(); after the AppendText call did the trick. – Brandon Barkley Aug 25 '17 at 05:14
You can use the following code snippet:
myTextBox.SelectionStart = myTextBox.Text.Length;
myTextBox.ScrollToCaret();
which will automatically scroll to the end.

- 23,995
- 17
- 79
- 116
-
6Looked 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. – GWLlosa May 22 '09 at 14:59
-
4This 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. – tomsv Aug 07 '13 at 11:04
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();

- 30,738
- 21
- 105
- 131

- 704
- 7
- 7
-
22Note 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). – Bob Feb 15 '13 at 01:26
-
1Note that `ScrollToEnd()` can be extremely poor performing. In my app it accounted for over 50% of the profiling time. – ergohack Oct 25 '17 at 19:58
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();
}

- 23,995
- 17
- 79
- 116
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

- 78,642
- 66
- 377
- 442
-
Had the same issue with Windows 10, your workaround works fine here too. – Hannes Nov 17 '15 at 10:15
-
-
-
I needed to add a refresh:
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();

- 91
- 1
- 1
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);
});

- 697
- 9
- 11
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!

- 151
- 2
- 4
-
You probably prefer to override the OnShown method: "protected override void OnShown (EventArgs e)" instead of putting an event handler for Activate. – Leonardo Hernández Oct 01 '21 at 00:33
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.

- 57
- 6
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

- 5,291
- 7
- 33
- 42
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);

- 177
- 1
- 16
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...

- 133
- 2
- 10
-
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 :( – TooGeeky Oct 10 '14 at 09:53
I use a function for this :
private void Log (string s) {
TB1.AppendText(Environment.NewLine + s);
TB1.ScrollToCaret();
}

- 1