Как автоматически прокрутить до конца многострочного текстового поля? - PullRequest
261 голосов
/ 22 мая 2009

У меня есть текстовое поле со свойством .Multiline, установленным в true. Я регулярно добавляю новые строки текста. Я хотел бы, чтобы текстовое поле автоматически прокручивалось до самой нижней записи (самой новой) при добавлении новой строки. Как мне это сделать?

Ответы [ 11 ]

373 голосов
/ 15 февраля 2013

Через равные промежутки времени я добавляю новые строки текста. Я хотел бы, чтобы текстовое поле автоматически прокручивалось до самой нижней записи (самой новой) при добавлении новой строки.

Если вы используете 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;

Но не должно быть никаких причин делать это вручную.

(Если вы декомпилируете его самостоятельно, вы увидите, что он использует некоторые, возможно, более эффективные внутренние методы и имеет, как представляется, незначительный особый случай.)

140 голосов
/ 22 мая 2009

Вы можете использовать следующий фрагмент кода:

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

, который автоматически прокрутится до конца.

39 голосов
/ 13 января 2013

Кажется, интерфейс изменился в .NET 4.0. Существует следующий метод , который достигает всего вышеперечисленного. Как предложил Томми Энгебретсен, включение его в обработчик событий TextChanged делает его автоматическим.

textBox1.ScrollToEnd();
15 голосов
/ 29 августа 2009

Попробуйте добавить предложенный код к событию TextChanged:

private void textBox1_TextChanged(object sender, EventArgs e)
{
  textBox1.SelectionStart = textBox1.Text.Length;
  textBox1.ScrollToCaret();
}
8 голосов
/ 15 апреля 2015
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
8 голосов
/ 24 августа 2011

Мне нужно было добавить обновление:

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
3 голосов
/ 22 апреля 2015

Я обнаружил простую разницу, которая не была рассмотрена в этой теме.

Если вы делаете все вызовы ScrollToCarat() как часть события Load() вашей формы, это не сработает. Я только добавил свой ScrollToCarat() вызов к событию Activated() моей формы, и он отлично работает.

Редактировать

Важно, чтобы эта прокрутка выполнялась только в первый раз, когда событие Activated формы запускается (не при последующих активациях), или оно будет прокручиваться каждый при активации вашей формы. не хочу.

Так что, если вы перехватываете событие Activated() только для прокрутки текста при загрузке программы, вы можете просто отписаться от события внутри самого обработчика события, таким образом:

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

Если у вас есть другие вещи, которые вам нужно делать при каждой активации формы, вы можете установить значение bool в значение true при первом запуске события Activated(), поэтому вы не прокручиваете последующие активации, но все еще может делать другие вещи, которые вам нужно сделать.

Кроме того, если ваш TextBox находится на вкладке, которая не является SelectedTab, ScrollToCarat() не будет иметь никакого эффекта. Так что вам нужно, по крайней мере, сделать его выбранной вкладкой во время прокрутки. Вы можете заключить код в пару YourTab.SuspendLayout(); и YourTab.ResumeLayout(false);, если ваша форма мерцает при этом.

Конец редактирования

Надеюсь, это поможет!

1 голос
/ 14 февраля 2018

Для тех, кто находится здесь, ожидая увидеть реализацию веб-форм, вы хотите использовать обработчик событий 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);
1 голос
/ 16 апреля 2017

Это прокрутит до конца текстового поля, когда текст будет изменен, но все же позволит пользователю прокрутить вверх

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

протестировано на Visual Studio Enterprise 2017

0 голосов
/ 18 декабря 2014

Я использую функцию для этого:

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