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

GWL*_*osa 278 c# scroll textbox winforms

我有一个文本框,其.Multiline属性设置为true.我会定期添加新的文本行.我希望每当添加新行时,文本框都会自动滚动到最底部的条目(最新的条目).我该如何做到这一点?

Bob*_*Bob 399

我会定期添加新的文本行.我希望每当添加新行时,文本框都会自动滚动到最底部的条目(最新的条目).

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

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


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

似乎其他答案中的替代方法在这种情况下也不起作用.一种方法是在VisibleChanged事件上执行额外的滚动:

textBox.VisibleChanged += (sender, e) =>
{
    if (textBox.Visible)
    {
        textBox.SelectionStart = textBox.TextLength;
        textBox.ScrollToCaret();
    }
};
Run Code Online (Sandbox Code Playgroud)

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

textBox.Select(textBox.TextLength + 1, 0);
textBox.SelectedText = textToAppend;
Run Code Online (Sandbox Code Playgroud)

但是没有理由手动完成.

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

  • 这种方法*快*更快,更顺畅.滚动条没有"闪烁"(当快速连续拨打多个电话时更加明显). (7认同)
  • 这是一个**更好的解决方案. (3认同)
  • 正在吃自己试图用'tb.Text + = ....`和WndProc和元帅现在我觉得愚蠢:D (3认同)
  • textarea 也需要聚焦,我第一次这样做时它没有滚动,因为它没有焦点。 (3认同)
  • AppendText没有自动滚动我的ReadOnly TextBox,而是添加了TextBox.ScrollToEnd(); 在AppendText调用之后完成了这个技巧. (2认同)

GWL*_*osa 143

您可以使用以下代码段:

myTextBox.SelectionStart = myTextBox.Text.Length;
myTextBox.ScrollToCaret();
Run Code Online (Sandbox Code Playgroud)

它将自动滚动到结尾.

  • 看到这里的答案,找不到它,所以当我弄明白时,我想我会把它放在这里为未来的用户,或者如果其他人有更好的方法. (5认同)
  • 这可能是当时最好的答案,但现在我认为Bob的答案是解决OP问题的更好方法. (4认同)

Joh*_*ach 38

看来.NET 4.0中的界面发生了变化.有以下方法可以实现上述所有目的.正如Tommy Engebretsen所建议的那样,将它放在TextChanged事件处理程序中会使其自动化.

textBox1.ScrollToEnd();
Run Code Online (Sandbox Code Playgroud)

  • 请注意,该方法位于`System.Windows.Controls.Primitives`命名空间(`PresentationFramework` assembly,WPF)中的`TextBoxBase`类中.此方法不存在,并且在WinForms中不起作用,WinForms的`TextBox`类继承自`System.Windows.Forms`命名空间中的`TextBoxBase`(`System.Windows.Forms`程序集,WinForms). (21认同)

小智 16

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

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


Ste*_*ger 9

textBox1.Focus()
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
Run Code Online (Sandbox Code Playgroud)

对我不起作用(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);
    }


}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)


小智 8

我需要添加刷新:

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
Run Code Online (Sandbox Code Playgroud)


sam*_*yte 8

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

txtTCPTxRx.AppendText(newText);
Run Code Online (Sandbox Code Playgroud)

下面是我实际使用的代码

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


Pet*_*ete 6

我发现了一个简单的区别,在这个线程中没有解决。

如果您将所有ScrollToCarat()调用作为表单Load()事件的一部分进行,则它不起作用。我刚刚将我的ScrollToCarat()电话添加到我的表单Activated()事件中,它工作正常。

编辑

重要的是只在第一次Activated触发表单事件时滚动(而不是在后续激活时),否则每次激活表单时它都会滚动,这可能是您不想要的。

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

Activated -= new System.EventHandler(this.Form1_Activated);
Run Code Online (Sandbox Code Playgroud)

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

另外,如果您TextBox是一个标签,是不是在SelectedTabScrollToCarat()不会有任何效果。因此,您至少需要在滚动时将其设为选定的选项卡。如果执行此操作时表单闪烁,您可以将代码包装在 aYourTab.SuspendLayout();YourTab.ResumeLayout(false);pair 中。

编辑结束

希望这可以帮助!