如何自动滚动到多行文本框的底部?

我有一个文本框的. multiline属性设置为true。每隔一段时间,我都会向它添加新的文本行。我希望文本框自动滚动到最底部的条目(最新的一个)每当添加一个新的行。我该怎么做呢?

315097 次浏览

您可以使用以下代码片段:

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

它会自动滚动到最后。

尝试将建议的代码添加到TextChanged事件:

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

我需要添加一个刷新:

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

net 4.0中,接口似乎发生了变化。下面的方法实现了上述所有功能。正如Tommy Engebretsen所建议的,将其放在TextChanged事件处理程序中可以使其自动完成。

textBox1.ScrollToEnd();

每隔一段时间,我都会向它添加新的文本行。我希望文本框自动滚动到最底部的条目(最新的一个)每当添加一个新的行。

如果使用TextBox.AppendText(string text),它将自动滚动到新添加的文本的末尾。如果在循环中调用它,它可以避免滚动条的闪烁。

它也恰好比连接到.Text属性快一个数量级。尽管这可能取决于你调用它的频率;我在用一个紧循环测试。


如果它在文本框显示之前被调用,或者如果文本框不可见(例如在TabPanel的不同选项卡中),它将不会滚动。看到TextBox.AppendText()不是自动滚动。这可能很重要,也可能不重要,这取决于当用户看不到文本框时是否需要自动滚动。

在这种情况下,其他答案的替代方法似乎也不管用。一种解决方法是在VisibleChanged事件上执行额外的滚动:

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

在内部,AppendText做了这样的事情:

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

但不应该有理由手动完成。

(如果你自己反编译它,你会发现它使用了一些可能更有效的内部方法,并且似乎有一个小的特殊情况。)

这只对我有用……

txtserialloging ->Text = "";

txtSerialLogging - >追加文本(年代);

我尝试了上面所有的情况,但问题是在我的情况下,文本可以减少,增加,也可以长时间保持静态。 静态的意思是,静态的长度(行),但内容不同

所以,当长度(行)在一段时间内保持不变时,我在最后面临一个行跳转的情况……

我使用了一个函数:

private void Log (string s) {
TB1.AppendText(Environment.NewLine + s);
TB1.ScrollToCaret();
}
textBox1.Focus()
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();

不适合我(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

我发现了一个在这篇文章中没有提到的简单的区别。

如果你把所有的ScrollToCarat()调用作为表单的Load()事件的一部分,它就不起作用了。我刚刚将我的ScrollToCarat()调用添加到窗体的Activated()事件中,它工作正常。

编辑

重要的是,只有在表单的Activated事件第一次被触发时(而不是在后续激活时)才这样滚动,否则它将在表单被激活时滚动每一个,这是您可能不希望看到的情况。

因此,如果你只是在程序加载时捕获Activated()事件来滚动文本,那么你可以在事件处理程序本身中取消订阅该事件,从而:

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

如果每次激活表单时都有其他需要做的事情,可以在第一次触发Activated()事件时将bool设置为true,这样就不必在后续激活时滚动,但仍然可以做其他需要做的事情。

此外,如果你的TextBox在一个不是SelectedTab的选项卡上,ScrollToCarat()将不起作用。所以你至少需要让它成为滚动时的选定选项卡。如果在执行此操作时表单会闪烁,则可以将代码包装为YourTab.SuspendLayout();YourTab.ResumeLayout(false);对。

编辑结束

希望这能有所帮助!

当文本被更改时,这将滚动到文本框的末尾,但仍然允许用户向上滚动

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

在Visual Studio Enterprise 2017上测试

对于登陆这里希望看到webforms实现的任何人,你想要使用页面请求管理器的endRequest事件处理程序(https://stackoverflow.com/a/1388170/1830512)。以下是我在母版页的内容页中为我的文本框所做的,请忽略我没有为控件使用变量的事实:

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);

关于皮特关于一个标签上的文本框的评论,我得到的工作方式是添加

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

到选项卡的Layout事件。

我用这个。简单、干净、快捷!

txtTCPTxRx.AppendText(newText);

下面是我使用的实际代码

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