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

时间:2009-05-22 14:57:55

标签: c# winforms textbox scroll

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

12 个答案:

答案 0 :(得分:371)

  

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

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

但是没有理由手动完成。

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

答案 1 :(得分:140)

您可以使用以下代码段:

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

会自动滚动到结尾。

答案 2 :(得分:39)

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

textBox1.ScrollToEnd();

答案 3 :(得分:15)

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

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

答案 4 :(得分:8)

我需要添加刷新:

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

答案 5 :(得分:8)

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

答案 6 :(得分:3)

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

如果您在表单的ALAlertBannerPosition position = ALAlertBannerPositionBottom; //ALAlertBannerStyle randomStyle = (ALAlertBannerStyle)(arc4random_uniform(4)); for (id view in self.view.subviews) { if([view isKindOfClass:[ALAlertBanner class]]) { [view removeFromSuperview]; } } ALAlertBanner *banner = [ALAlertBanner alertBannerForView:self.view style:ALAlertBannerStyleSuccess position:position title:nil subtitle:nil tappedBlock:^(ALAlertBanner *alertBanner) { NSLog(@"@@@@@@@Banner tapped@@@@@@!"); [alertBanner hide]; }]; banner.secondsToShow = self.secondsToShow; banner.backgroundColor=[UIColor whiteColor]; banner.showAnimationDuration = self.showAnimationDuration; banner.hideAnimationDuration = self.hideAnimationDuration; [banner show]; NSLog(@"@@@@@@@@@@View Subviews are:%@",self.view.subviews); 事件中执行了所有ScrollToCarat()次调用,则无效。我刚刚将Load()电话添加到我的表单的ScrollToCarat()事件中,它运行正常。

修改

重要的是,只有在第一次触发表单的Activated()事件时才会滚动(而不是在后续激活中),或者它会滚动每个时间激活表单,这是你可能不想要。

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

Activated()

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

此外,如果您的Activated()位于不属于TextBox的标签页上,则SelectedTab将无法生效。因此,在滚动时至少需要将其设为选定的选项卡。如果您的表单在执行此操作时闪烁,则可以将代码包装在ScrollToCarat()YourTab.SuspendLayout();对中。

编辑结束

希望这有帮助!

答案 7 :(得分:1)

当文本发生变化时,这将滚动到文本框的末尾,但仍允许用户向上滚动

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

在Visual Studio Enterprise 2017上测试

答案 8 :(得分:1)

对于任何希望看到webforms实现的人来说,您希望使用Page Request Manager的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);

答案 9 :(得分:1)

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

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

到选项卡的布局事件。

答案 10 :(得分:0)

这对我有用......

txtSerialLogging-&gt; Text =“”;

txtSerialLogging-&GT; AppendText通过(一个或多个);

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

所以,当长度(线条)在一段时间内保持相同时,我在最后面临一线跳跃情况......

答案 11 :(得分:0)

我使用了一个函数:

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