Як автоматично прокручувати донизу багаторядкового текстового поля?


295

У мене є текстове поле з властивістю .Multiline, встановленим на true. Через рівні проміжки часу я додаю до нього нові рядки тексту. Я хотів би, щоб текстове поле автоматично прокручувалось до самого нижнього запису (найновішого) кожного разу, коли буде доданий новий рядок. Як я це досягну?


6
Тут я шукав відповідь, не зміг її знайти, тому, коли я це зрозумів, я зрозумів, що я викладу це для майбутніх користувачів або, якщо, можливо, хтось інший має кращий підхід.
GWLlosa

2
Мені потрібно було зробити те ж саме у VBA, в якому немає всіх цих ошатних штанів .NET. Для майбутнього google-fu ось заклик: TextBox1.Text = TextBox1.Text & "що завгодно"; 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
Textarea також має бути зосередженим, перший раз, коли я це зробив, він не прокручувався, тому що не мав фокусу.
Qwerty01

3
AppendText не автоматично прокручував мій ReadOnly TextBox, але додав TextBox.ScrollToEnd (); після дзвінка AppendText зробив свою справу.
Брендон Барклі

143

Ви можете використовувати такий фрагмент коду:

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

яка автоматично прокручується до кінця.


5
Тут я шукав відповідь, не зміг її знайти, тому, коли я зрозумів, я зрозумів, що я викладу це для майбутніх користувачів або, якщо, можливо, хтось інший матиме кращий підхід.
GWLlosa

4
Можливо, це була найкраща відповідь на той час, але зараз я думаю, що відповідь Боба - це краще рішення проблеми ОП.
tomsv

38

Здається, інтерфейс змінився в .NET 4.0. Існує наступний метод, який дозволяє досягти всього вищесказаного. Як запропонував Томмі Енгебрецен, розміщення його в обробці подій TextChanged робить його автоматичним.

textBox1.ScrollToEnd();

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();
}

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();

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);

0

Це працювало тільки для мене ...

txtSerialLogging-> Text = "";

txtSerialLogging-> AppendText (s);

Я спробував усі випадки вище, але проблема полягає в тому, що в моєму випадку текст може зменшуватися, збільшуватися, а також може залишатися статичним протягом тривалого часу. статичні засоби, статична довжина (лінії), але зміст різний.

Отже, я стикався із ситуацією зі стрибками однієї лінії в кінці, коли довжина (лінії) деякий час залишається однаковою ...


Я знаю, це схоже на відповідь Боба, але пояснює конкретний випадок. І я не можу коментувати відповідь Боба ... Застряг у правилах
stackoverflow

0

Я використовую для цього функцію:

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.