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?
我有一个文本框,其.Multiline属性设置为true。我定期添加新的文本行。我希望每当添加新行时,文本框都会自动滚动到最底部的条目(最新的条目)。我该如何做到这一点?
11 个解决方案
#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.
如果使用TextBox.AppendText(字符串文本),它将自动滚动到新附加文本的末尾。如果你在循环中调用它,它可以避免闪烁的滚动条。
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.
它也恰好比连接到.Text属性快一个数量级。虽然这可能取决于你调用它的频率;我正在测试一个紧凑的循环。
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.
如果在显示文本框之前调用它,或者如果文本框不可见(例如,在TabPanel的不同选项卡中),则不会滚动。请参见TextBox.AppendText()而不是自动滚动。这可能重要,也可能不重要,具体取决于当用户无法看到文本框时是否需要自动滚动。
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:
似乎其他答案中的替代方法在这种情况下也不起作用。解决它的一种方法是在VisibleChanged事件上执行额外的滚动:
textBox.VisibleChanged += (sender, e) =>
{
if (textBox.Visible)
{
textBox.SelectionStart = textBox.TextLength;
textBox.ScrollToCaret();
}
};
Internally, AppendText
does something like this:
在内部,AppendText执行以下操作:
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.)
(如果你自己反编译,你会发现它使用了一些可能更有效的内部方法,并且具有似乎是一个小的特殊情况。)
#2
You can use the following code snippet:
您可以使用以下代码段:
myTextBox.SelectionStart = myTextBox.Text.Length;
myTextBox.ScrollToCaret();
which will automatically scroll to the end.
它将自动滚动到结尾。
#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.
看来.NET 4.0中的界面发生了变化。有以下方法可以实现上述所有目的。正如Tommy Engebretsen建议的那样,将它放在TextChanged事件处理程序中会使其自动化。
textBox1.ScrollToEnd();
#4
Try to add the suggested code to the TextChanged event:
尝试将建议的代码添加到TextChanged事件:
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
}
#5
I needed to add a refresh:
我需要添加刷新:
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
#6
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:
对我不起作用(Windows 8.1,不管是什么原因)。因为我还在使用.NET 2.0,所以我不能使用ScrollToEnd。但这有效:
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
#7
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.
如果您在表单的Load()事件中执行所有ScrollToCarat()调用,则它不起作用。我刚刚将我的ScrollToCarat()调用添加到我的表单的Activated()事件中,它运行正常。
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.
重要的是只在第一次触发表单的Activated事件时进行滚动(而不是在后续激活中),或者每次激活表单时它都会滚动,这是您可能不想要的。
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()事件以在程序加载时滚动文本,那么您可以取消订阅事件处理程序本身内的事件,因此:
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.
如果每次激活表单时都需要执行其他操作,则可以在第一次触发Activated()事件时将bool设置为true,这样就不会在后续激活时滚动,但仍可以执行其他操作你需要做的事情。
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.
此外,如果TextBox位于不是SelectedTab的选项卡上,则ScrollToCarat()将不起作用。因此,在滚动时至少需要将其设为选定的选项卡。您可以将代码包装在YourTab.SuspendLayout()中;和YourTab.ResumeLayout(false);如果您的表单在执行此操作时闪烁,请配对。
End of edit
编辑结束
Hope this helps!
希望这可以帮助!
#8
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
在Visual Studio Enterprise 2017上测试
#9
For anyone else landing here expecting to see a webforms implementation, you want to use the Page Request Manager's endRequest event handler (https://*.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:
对于任何希望看到webforms实现的人来说,您希望使用Page Request Manager的endRequest事件处理程序(https://*.com/a/1388170/1830512)。这是我在母版页的内容页面中为我的TextBox所做的,请忽略我没有使用变量作为控件的事实:
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);
#10
This only worked for me...
这对我有用...
txtSerialLogging->Text = "";
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.
我尝试了上面的所有情况,但问题是在我的情况下,文本s可以减少,增加并且还可以长时间保持静态。静态意味着,静态长度(行)但内容是不同的。
So, I was facing one line jumping situation at the end when the length(lines) remains same for some times...
所以,当长度(线)在一段时间内保持相同时,我在最后面临一条线跳跃的情况......
#11
I use a function for this :
我为此使用了一个函数:
private void Log (string s) {
TB1.AppendText(Environment.NewLine + s);
TB1.ScrollToCaret();
}
#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.
如果使用TextBox.AppendText(字符串文本),它将自动滚动到新附加文本的末尾。如果你在循环中调用它,它可以避免闪烁的滚动条。
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.
它也恰好比连接到.Text属性快一个数量级。虽然这可能取决于你调用它的频率;我正在测试一个紧凑的循环。
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.
如果在显示文本框之前调用它,或者如果文本框不可见(例如,在TabPanel的不同选项卡中),则不会滚动。请参见TextBox.AppendText()而不是自动滚动。这可能重要,也可能不重要,具体取决于当用户无法看到文本框时是否需要自动滚动。
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:
似乎其他答案中的替代方法在这种情况下也不起作用。解决它的一种方法是在VisibleChanged事件上执行额外的滚动:
textBox.VisibleChanged += (sender, e) =>
{
if (textBox.Visible)
{
textBox.SelectionStart = textBox.TextLength;
textBox.ScrollToCaret();
}
};
Internally, AppendText
does something like this:
在内部,AppendText执行以下操作:
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.)
(如果你自己反编译,你会发现它使用了一些可能更有效的内部方法,并且具有似乎是一个小的特殊情况。)
#2
You can use the following code snippet:
您可以使用以下代码段:
myTextBox.SelectionStart = myTextBox.Text.Length;
myTextBox.ScrollToCaret();
which will automatically scroll to the end.
它将自动滚动到结尾。
#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.
看来.NET 4.0中的界面发生了变化。有以下方法可以实现上述所有目的。正如Tommy Engebretsen建议的那样,将它放在TextChanged事件处理程序中会使其自动化。
textBox1.ScrollToEnd();
#4
Try to add the suggested code to the TextChanged event:
尝试将建议的代码添加到TextChanged事件:
private void textBox1_TextChanged(object sender, EventArgs e)
{
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
}
#5
I needed to add a refresh:
我需要添加刷新:
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
#6
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:
对我不起作用(Windows 8.1,不管是什么原因)。因为我还在使用.NET 2.0,所以我不能使用ScrollToEnd。但这有效:
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
#7
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.
如果您在表单的Load()事件中执行所有ScrollToCarat()调用,则它不起作用。我刚刚将我的ScrollToCarat()调用添加到我的表单的Activated()事件中,它运行正常。
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.
重要的是只在第一次触发表单的Activated事件时进行滚动(而不是在后续激活中),或者每次激活表单时它都会滚动,这是您可能不想要的。
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()事件以在程序加载时滚动文本,那么您可以取消订阅事件处理程序本身内的事件,因此:
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.
如果每次激活表单时都需要执行其他操作,则可以在第一次触发Activated()事件时将bool设置为true,这样就不会在后续激活时滚动,但仍可以执行其他操作你需要做的事情。
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.
此外,如果TextBox位于不是SelectedTab的选项卡上,则ScrollToCarat()将不起作用。因此,在滚动时至少需要将其设为选定的选项卡。您可以将代码包装在YourTab.SuspendLayout()中;和YourTab.ResumeLayout(false);如果您的表单在执行此操作时闪烁,请配对。
End of edit
编辑结束
Hope this helps!
希望这可以帮助!
#8
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
在Visual Studio Enterprise 2017上测试
#9
For anyone else landing here expecting to see a webforms implementation, you want to use the Page Request Manager's endRequest event handler (https://*.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:
对于任何希望看到webforms实现的人来说,您希望使用Page Request Manager的endRequest事件处理程序(https://*.com/a/1388170/1830512)。这是我在母版页的内容页面中为我的TextBox所做的,请忽略我没有使用变量作为控件的事实:
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);
#10
This only worked for me...
这对我有用...
txtSerialLogging->Text = "";
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.
我尝试了上面的所有情况,但问题是在我的情况下,文本s可以减少,增加并且还可以长时间保持静态。静态意味着,静态长度(行)但内容是不同的。
So, I was facing one line jumping situation at the end when the length(lines) remains same for some times...
所以,当长度(线)在一段时间内保持相同时,我在最后面临一条线跳跃的情况......
#11
I use a function for this :
我为此使用了一个函数:
private void Log (string s) {
TB1.AppendText(Environment.NewLine + s);
TB1.ScrollToCaret();
}