【问题标题】:How do I automatically scroll to the bottom of a multiline text box?如何自动滚动到多行文本框的底部?
【发布时间】:2010-10-28 06:02:54
【问题描述】:

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

【问题讨论】:

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

标签: c# winforms textbox scroll


【解决方案1】:

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

如果您使用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 调用成功之后。
【解决方案2】:

可以使用以下代码sn-p:

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

它会自动滚动到最后。

【讨论】:

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

.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% 以上。
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:
    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)谢谢
    • 其他答案无效,这个有效。 Windows 10、4.7.2。
    【解决方案6】:

    我需要添加刷新:

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

    【讨论】:

      【解决方案7】:

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

      如果您将所有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 放置事件处理程序。
      【解决方案8】:

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

      txtTCPTxRx.AppendText(newText);
      

      下面是我使用的实际代码

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

      【讨论】:

        【解决方案9】:

        关于 Pete 对选项卡上的 TextBox 的评论,我让它工作的方式是添加

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

        到标签的布局事件。

        【讨论】:

          【解决方案10】:

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

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

          在 Visual Studio Enterprise 2017 上测试

          【讨论】:

            【解决方案11】:

            对于其他希望看到网络表单实现的人,您希望使用页面请求管理器的 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);
            

            【讨论】:

              【解决方案12】:

              这只对我有用……

              txtSerialLogging->Text = "";

              txtSerialLogging->AppendText(s);

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

              所以,当长度(行)保持相同一段时间时,我最后面临着一个跳线的情况......

              【讨论】:

              • 我知道,这与鲍勃的回答类似,但解释了一个具体案例。而且我无法评论 Bob 的回答...卡在 stackoverflow 规则中:(
              【解决方案13】:

              我为此使用了一个函数:

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

              【讨论】:

                猜你喜欢
                • 2014-12-22
                • 2012-03-01
                • 2017-02-07
                • 1970-01-01
                • 2013-11-12
                • 2011-09-30
                • 2010-12-31
                • 2010-11-26
                • 2014-12-20
                相关资源
                最近更新 更多