ChatGPT解决这个技术问题 Extra ChatGPT

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

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

在这里寻找答案,找不到它,所以当我想出来的时候,我想我会把它放在这里供未来的用户使用,或者如果其他人有更好的方法。
我需要在 VBA 中做同样的事情,它没有所有这些花哨的新 .NET 方法。对于未来的 google-fu,这里是咒语:TextBox1.Text = TextBox1.Text & "whatever"; TextBox1.SelStart = Len(TextBox1.Text); TextBox1.SetFocus; ...然后 .SetFocus 回到之前拥有焦点的任何控件。如果不将焦点放在 TextBox1 上,无论我做什么,它都不会更新它的滚动条。
@GordonBroom Whelp,多亏了我现在开始称“代码片段”“咒语”。干得好。 :D

C
Community

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

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

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

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

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

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

在内部,AppendText 执行以下操作:

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

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

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


正在吃自己试图用 tb.Text += .... 和 WndProc 和 marshals 做到这一点现在我觉得很愚蠢 :D
仍然,对我(.NET 3.5)来说,只有当我将带有 SelectionStart 和 ScrollToCaret 的建议代码添加到 TextChanged 事件处理程序(见下文)时,事情才起作用,因为否则在某些时候(并非总是如此),滚动将被重置到开头(可能最好的解决方案是覆盖该默认代码..)
textarea 也需要聚焦,我第一次这样做时它没有滚动,因为它没有焦点。
textBox.VisibleChanged 不起作用。但我将其更改为 txtResponse.TextChanged 并且它有效。
AppendText 没有自动滚动我的 ReadOnly TextBox,而是添加了 TextBox.ScrollToEnd();在 AppendText 调用成功之后。
G
GWLlosa

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

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

它将自动滚动到最后。


在这里寻找答案,找不到它,所以当我想出来的时候,我想我会把它放在这里供未来的用户使用,或者如果其他人有更好的方法。
这可能是当时最好的答案,但现在我认为 Bob 的答案是对 OP 问题的更好解决方案。
P
Peter Mortensen

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

textBox1.ScrollToEnd();

请注意,该方法位于 System.Windows.Controls.Primitives 命名空间(PresentationFramework 程序集,WPF)的 TextBoxBase 类中。此方法不存在且在 WinForms 中不起作用,其 TextBox 类继承自 System.Windows.Forms 命名空间中的 TextBoxBaseSystem.Windows.Forms 程序集,WinForms)。
请注意,ScrollToEnd() 的性能可能非常差。在我的应用程序中,它占分析时间的 50% 以上。
G
GWLlosa

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

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

S
Stefan Steiger
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 也有同样的问题,您的解决方法在这里也可以正常工作。
为我工作(Windows 10)谢谢
其他答案无效,这个有效。视窗 10、4.7.2。
T
Tim Cooper

我需要添加一个刷新:

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

P
Pete

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

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

编辑

仅在第一次触发表单的 Activated 事件时(而不是在后续激活时)执行此滚动非常重要,否则它会在您的表单被激活时滚动每次,您可能不会这样做想。

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

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

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

此外,如果您的 TextBox 位于不是 SelectedTab 的选项卡上,则 ScrollToCarat() 将不起作用。因此,您至少需要在滚动时将其设为选定选项卡。如果您在执行此操作时表单闪烁,您可以将代码包装在 YourTab.SuspendLayout();YourTab.ResumeLayout(false); 对中。

编辑结束

希望这可以帮助!


您可能更喜欢重写 OnShown 方法:“protected override void OnShown (EventArgs e)”,而不是为 Activate 放置事件处理程序。
s
sam byte

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

txtTCPTxRx.AppendText(newText);

下面是我使用的实际代码

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

l
larrycook99

关于皮特关于选项卡上的文本框的评论,我让它工作的方式是添加

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

到选项卡的 Layout 事件。


T
Tom Fuller

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

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

在 Visual Studio Enterprise 2017 上测试


m
midoriha_senpai

对于希望看到网络表单实施的其他任何人,您希望使用页面请求管理器的 endRequest 事件处理程序 (https://stackoverflow.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);

T
TooGeeky

这只对我有用......

txtSerialLogging->Text = "";

txtSerialLogging->AppendText(s);

我尝试了上述所有情况,但问题是在我的情况下 text 可以减少,增加并且也可以长时间保持静止。静态意味着,静态长度(行),但内容不同。

所以,当长度(线)在一段时间内保持不变时,我最后面临着一种跳线的情况......


我知道,它类似于鲍勃的回答,但解释了一个具体案例。而且我无法评论鲍勃的回答...卡在stackoverflow规则中:(
D
DMike92

我为此使用了一个函数:

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}