Как автоматически прокрутить до конца многострочного текстового поля?

295

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

GWLlosa
источник
6
Ищите здесь ответ, не могли его найти, поэтому, когда я выяснил это, я решил, что выложу его здесь для будущих пользователей, или, может быть, у кого-то еще был лучший подход.
GWLlosa
2
Мне нужно было сделать то же самое в VBA, в которой нет всех этих модных новых методов .NET. Для будущего гугл-фу вот заклинание: TextBox1.Text = TextBox1.Text & "what"; TextBox1.SelStart = Len (TextBox1.Text); TextBox1.SetFocus; ... а затем .SetFocus вернуться к тому элементу управления, который имел фокус раньше. Без акцента на TextBox1 он никогда не обновит свои полосы прокрутки, что бы я ни делал.
Гордон Брум
1
@GordonBroom Whelp, благодаря этому я собираюсь начать называть "фрагменты кода" "заклинаниями" сейчас. Хорошая работа. : D
Сидни

Ответы:

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 и маршалами Теперь я чувствую себя глупо: D
Саид Яздани
3
Текстовая область также должна быть сфокусированной, в первый раз, когда я сделал это, она не прокручивалась, потому что у нее не было фокуса.
Qwerty01
3
AppendText не прокручивал мой текстовый блок ReadOnly автоматически, но добавлял TextBox.ScrollToEnd (); после вызова AppendText сделал свое дело.
Брэндон Баркли
143

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

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

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

GWLlosa
источник
5
Ищите здесь ответ, не могли его найти, поэтому, когда я выяснил это, я решил, что выложу его здесь для будущих пользователей, или, может быть, у кого-то еще был лучший подход.
GWLlosa
4
Возможно, это был лучший ответ в то время, но теперь я думаю, что ответ Боба - лучшее решение проблемы ОП.
tomsv
38

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

textBox1.ScrollToEnd();
JohnDRoach
источник
21
Обратите внимание, что этот метод находится в TextBoxBaseклассе в System.Windows.Controls.Primitivesпространстве имен ( PresentationFrameworkсборка, WPF). Этот метод не существует и не будет работать в WinForms, чей TextBoxкласс наследуется отTextBoxBase в System.Windows.Formsпространстве имен ( System.Windows.Formsсборка, WinForms).
Боб
1
Обратите внимание, что это ScrollToEnd()может быть очень плохое исполнение. В моем приложении это составляло более 50% времени профилирования.
ergohack
16

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

private void textBox1_TextChanged(object sender, EventArgs e)
{
  textBox1.SelectionStart = textBox1.Text.Length;
  textBox1.ScrollToCaret();
}
GWLlosa
источник
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, ваш обходной путь работает и здесь.
Ханнес
Никто не работал на меня, но это. Да благословит Бог твою душу
Эмирхан Озлен
9

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

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
h4nd
источник
4

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

Если вы делаете все 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

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

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

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

Эрик Шрив
источник
1

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

Это работало только для меня ...

txtSerialLogging-> Text = "";

txtSerialLogging-> AppendText (ы);

Я перепробовал все вышеописанные случаи, но проблема в том, что в моем случае текст может уменьшаться, увеличиваться и также может оставаться статичным в течение длительного времени. статические средства, статическая длина (строки), но содержание другое.

Итак, я столкнулся с ситуацией скачка одной строки в конце, когда длина (строки) остается неизменной в течение нескольких раз ...

TooGeeky
источник
Я знаю, это похоже на ответ Боба, но объясняет конкретный случай. И я не могу комментировать ответ Боба ... Застрял в правилах
стекопотока
0

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

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
DMike92
источник