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


295

我有一个.Multiline属性设置为true的文本框。我会定期添加新的文本行。我希望文本框在添加新行时自动滚动到最底部的条目(最新条目)。我该如何完成?


6
在这里寻找答案,找不到答案,所以当我想出答案时,我想我会把它放在这里供将来的用户使用,或者也许还有其他更好的方法。
GWLlosa,2009年

2
我需要在VBA中做同样的事情,它没有所有这些新的.NET方法。对于将来的google-fu,这是咒语:TextBox1.Text = TextBox1.Text&“ whatever”; TextBox1.SelStart = Len(TextBox1.Text); TextBox1.SetFocus; ...然后将.SetFocus返回到以前具有焦点的任何控件。如果不将重点放在TextBox1上,无论我做什么,它都不会更新其滚动条。
Gordon Broom 2014年

1
@GordonBroom Whelp,由于这一点,我现在开始调用“代码段”“引语”。辛苦了 :D
悉尼(Sidney)

Answers:


425

我会定期添加新的文本行。我希望文本框在添加新行时自动滚动到最底部的条目(最新条目)。

如果使用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;

但是应该没有理由手动进行。

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


7
这种方法是很多更快更顺畅。滚动条没有“闪烁”(在快速连续进行多次呼叫时更明显)。
TallGuy

3
这是一个很多更好的解决方案。
杰夫,

3
一直在吃自己想tb.Text += ....和WndProc一起
进军,

3
文本区域也需要重点关注,我第一次这样做时,它没有滚动,因为它没有重点。
Qwerty01 2014年

3
AppendText不会自动滚动我的ReadOnly TextBox,而是添加TextBox.ScrollToEnd();。在AppendText调用完成之后。
布兰登·巴克利

143

您可以使用以下代码段:

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

它将自动滚动到末尾。


5
在这里寻找答案,找不到答案,所以当我想出答案时,我想我会把它放在这里供将来的用户使用,或者也许还有其他更好的方法。
GWLlosa,2009年

4
当时这可能是最好的答案,但是现在我认为Bob的答案是对OP问题的更好解决方案。
tomsv

38

似乎接口在.NET 4.0中已更改。通过以下方法可以实现上述所有目的。正如Tommy Engebretsen所建议的那样,将其放在TextChanged事件处理程序中会使它自动执行。

textBox1.ScrollToEnd();

21
请注意,该方法在名称空间(Assembly,WPF)的TextBoxBase类中。这种方法不存在,也不会在的WinForms,他们的工作类继承的命名空间(组装的WinForms)。System.Windows.Controls.PrimitivesPresentationFrameworkTextBoxTextBoxBaseSystem.Windows.FormsSystem.Windows.Forms
鲍勃

1
请注意,这ScrollToEnd()可能是非常糟糕的表现。在我的应用程序中,它占了分析时间的50%以上。
ergohack

16

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

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

10
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

Windows 10也有同样的问题,您的解决方法在这里也能正常工作。
汉尼斯,2015年

没有人为我工作,但这。上帝保佑你的灵魂
EmirhanÖzlen18年

9

我需要添加刷新:

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

4

我发现此线程中未解决的一个简单差异。

如果您将所有ScrollToCarat()呼叫作为表单Load()事件的一部分进行,则它将无法正常工作。我刚刚将ScrollToCarat()呼叫添加到表单的Activated()事件中,并且工作正常。

编辑

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

因此,如果您只是Activated()在程序加载时捕获事件以滚动文本,则可以在事件处理程序本身内部退订该事件,从而:

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

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

另外,如果您TextBox的标签不在上SelectedTabScrollToCarat()则不会起作用。因此,滚动时至少需要使其成为选定的选项卡。如果执行此操作时表格闪烁,则可以将代码包装为YourTab.SuspendLayout();YourTab.ResumeLayout(false);

编辑结束

希望这可以帮助!


1

更改文本后,它将滚动到文本框的末尾,但仍允许用户向上滚动

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

在Visual Studio Enterprise 2017上测试


1

对于希望在此查看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);

0

这只对我有用...

txtSerialLogging-> Text =“”;

txtSerialLogging-> AppendText(s);

我尝试了上述所有情况,但问题在于我的案文可以减少,增加,并且可以长时间保持静态。静态表示,静态长度(行),但内容不同。

所以,当长度(行)有时保持不变时,我最终遇到了跳线的情况。


我知道,这类似于鲍勃的答案,但解释了一个具体情况。而且我无法评论Bob的答案...卡在stackoverflow规则中:(
TooGeeky 2014年

0

我为此使用一个功能:

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.