Bagaimana cara saya menggulir ke bagian bawah kotak teks multiline secara otomatis?

295

Saya memiliki kotak teks dengan properti .Multiline diatur ke true. Secara berkala, saya menambahkan baris teks baru ke dalamnya. Saya ingin kotak teks untuk secara otomatis gulir ke entri paling bawah (yang terbaru) setiap kali baris baru ditambahkan. Bagaimana saya mencapai ini?

GWLlosa
sumber
6
Mencari di sini untuk jawabannya, tidak dapat menemukannya, jadi ketika saya menemukan jawabannya, saya pikir saya akan memasangnya di sini untuk pengguna di masa mendatang, atau jika mungkin orang lain memiliki pendekatan yang lebih baik.
GWLlosa
2
Saya perlu melakukan hal yang sama di VBA, yang tidak memiliki semua metode ini .NET. Untuk google-fu di masa mendatang, inilah mantra: TextBox1.Text = TextBox1.Text & "whatever"; TextBox1.SelStart = Len (TextBox1.Text); TextBox1.SetFocus; ... dan kemudian .SetFocus kembali ke kontrol apa pun yang memiliki fokus sebelumnya. Tanpa memberikan fokus ke TextBox1, itu tidak akan pernah memperbarui scrollbar-nya, apa pun yang saya lakukan.
Gordon Broom
1
@GordonBroom Whelp, terima kasih untuk itu saya akan mulai memanggil "cuplikan kode" "mantra" sekarang. Kerja bagus. : D
Sidney

Jawaban:

425

Secara berkala, saya menambahkan baris teks baru ke dalamnya. Saya ingin kotak teks untuk secara otomatis gulir ke entri paling bawah (yang terbaru) setiap kali baris baru ditambahkan.

Jika Anda menggunakan TextBox.AppendText(string text), itu akan secara otomatis gulir ke akhir teks yang baru ditambahkan. Ini menghindari scrollbar yang berkedip jika Anda memanggilnya dalam satu lingkaran.

Ini juga merupakan urutan besarnya lebih cepat daripada menggabungkan ke .Textproperti. Meskipun itu mungkin tergantung pada seberapa sering Anda menyebutnya; Saya sedang menguji dengan loop ketat.


Ini tidak akan menggulir jika dipanggil sebelum kotak teks ditampilkan, atau jika kotak teks tidak terlihat (misalnya di tab TabPanel yang berbeda). Lihat TextBox.AppendText () tidak autoscrolling . Ini mungkin atau mungkin tidak penting, tergantung pada apakah Anda memerlukan autoscroll ketika pengguna tidak dapat melihat kotak teks.

Tampaknya metode alternatif dari jawaban lain juga tidak berfungsi dalam kasus ini. Salah satu caranya adalah dengan melakukan pengguliran tambahan pada VisibleChangedacara tersebut:

textBox.VisibleChanged += (sender, e) =>
{
    if (textBox.Visible)
    {
        textBox.SelectionStart = textBox.TextLength;
        textBox.ScrollToCaret();
    }
};

Secara internal, AppendTextlakukan sesuatu seperti ini:

textBox.Select(textBox.TextLength + 1, 0);
textBox.SelectedText = textToAppend;

Tetapi seharusnya tidak ada alasan untuk melakukannya secara manual.

(Jika Anda mendekompilasi sendiri, Anda akan melihat bahwa ia menggunakan beberapa metode internal yang mungkin lebih efisien, dan memiliki apa yang tampaknya merupakan kasus khusus kecil.)

Bob
sumber
7
Metode ini jauh lebih cepat dan lebih lancar. Tidak ada 'kedipan' pada bilah gulir (yang lebih terlihat ketika melakukan banyak panggilan secara berurutan).
TallGuy
3
Ini adalah solusi yang jauh lebih baik.
Jeff
3
Sedang makan sendiri mencoba membuatnya dengan tb.Text += ....dan WndProc dan marshal Sekarang aku merasa bodoh: D
Saeid Yazdani
3
Textarea juga perlu fokus, pertama kali saya melakukan ini tidak menggulir karena tidak memiliki fokus.
Qwerty01
3
AppendText tidak secara otomatis menggulir ReadBox TextBox saya, tetapi menambahkan TextBox.ScrollToEnd (); setelah panggilan AppendText melakukan trik.
Brandon Barkley
143

Anda dapat menggunakan potongan kode berikut:

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

yang secara otomatis akan bergulir ke ujung.

GWLlosa
sumber
5
Mencari di sini untuk jawabannya, tidak dapat menemukannya, jadi ketika saya menemukan jawabannya, saya pikir saya akan memasangnya di sini untuk pengguna di masa mendatang, atau jika mungkin orang lain memiliki pendekatan yang lebih baik.
GWLlosa
4
Ini mungkin jawaban terbaik saat itu, tetapi sekarang saya pikir jawaban Bob adalah solusi yang lebih baik untuk masalah OP.
tomsv
38

Tampaknya antarmuka telah berubah di .NET 4.0. Ada metode berikut yang mencapai semua hal di atas. Seperti yang disarankan Tommy Engebretsen, memasukkannya ke dalam event handler TextChanged membuatnya otomatis.

textBox1.ScrollToEnd();
JohnDRoach
sumber
21
Perhatikan bahwa metode itu ada di TextBoxBasekelas di System.Windows.Controls.Primitivesnamespace ( PresentationFrameworkassembly, WPF). Metode ini tidak ada dan tidak akan berfungsi di WinForms, yang TextBoxkelasnya mewarisi dari TextBoxBasedalam System.Windows.Formsnamespace ( System.Windows.Formsassembly, WinForms).
Bob
1
Perhatikan bahwa ScrollToEnd()kinerjanya sangat buruk. Dalam aplikasi saya, itu menyumbang lebih dari 50% dari waktu pembuatan profil.
ergohack
16

Cobalah untuk menambahkan kode yang disarankan ke acara TextChanged:

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

tidak bekerja untuk saya (Windows 8.1, apa pun alasannya).
Dan karena saya masih menggunakan .NET 2.0, saya tidak bisa menggunakan ScrollToEnd.

Tapi ini berhasil:

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
Stefan Steiger
sumber
Memiliki masalah yang sama dengan Windows 10, solusi Anda berfungsi dengan baik di sini juga.
Hannes
Tidak ada yang bekerja untuk saya, tetapi ini. Tuhan memberkati jiwamu
Emirhan Özlen
9

Saya perlu menambahkan refresh:

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
h4
sumber
4

Saya menemukan perbedaan sederhana yang belum dibahas di utas ini.

Jika Anda melakukan semua ScrollToCarat()panggilan sebagai bagian dari Load()acara formulir Anda , itu tidak berfungsi. Saya baru saja menambahkan ScrollToCarat()panggilan saya ke Activated()acara formulir saya , dan itu berfungsi dengan baik.

Edit

Penting untuk hanya melakukan pengguliran ini saat Activatedacara formulir pertama kali diaktifkan (bukan pada aktivasi berikutnya), atau akan menggulir setiap kali formulir Anda diaktifkan, yang mungkin Anda tidak inginkan.

Jadi jika Anda hanya menjebak Activated()acara untuk menggulir teks ketika program Anda dimuat, maka Anda bisa berhenti berlangganan acara di dalam pengendali acara itu sendiri, dengan demikian:

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

Jika Anda memiliki hal-hal lain yang perlu Anda lakukan setiap kali formulir Anda diaktifkan, Anda dapat mengatur boolke true saat pertama kali Activated()acara Anda dipecat, sehingga Anda tidak menggulir pada aktivasi berikutnya, tetapi masih dapat melakukan hal-hal lain yang perlu Anda lakukan. melakukan.

Juga, jika Anda TextBoxberada di tab yang bukan SelectedTab, tidak ScrollToCarat()akan berpengaruh. Jadi, Anda setidaknya perlu menjadikannya tab yang dipilih saat Anda menggulir. Anda dapat membungkus kode dalam YourTab.SuspendLayout();dan YourTab.ResumeLayout(false);pasangan jika formulir Anda berkedip ketika Anda melakukan ini.

Akhir pengeditan

Semoga ini membantu!

Pete
sumber
1

Ini akan bergulir ke ujung kotak teks ketika teks diubah, tetapi masih memungkinkan pengguna untuk menggulir ke atas

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

diuji pada Visual Studio Enterprise 2017

Eric Shreve
sumber
1

Untuk siapa pun yang mendarat di sini mengharapkan untuk melihat implementasi formulir web, Anda ingin menggunakan pengendali event endRequest Page Request Manager ( https://stackoverflow.com/a/1388170/1830512 ). Inilah yang saya lakukan untuk TextBox saya di Halaman Konten dari Halaman Master, abaikan fakta bahwa saya tidak menggunakan variabel untuk kontrol:

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
sumber
0

Ini hanya bekerja untuk saya ...

txtSerialLogging-> Text = "";

txtSerialLogging-> AppendText (s);

Saya mencoba semua kasus di atas, tetapi masalahnya ada pada teks kasus saya s dapat menurun, meningkat dan juga dapat tetap statis untuk waktu yang lama. cara statis, panjang statis (garis) tetapi konten berbeda.

Jadi, saya menghadapi satu situasi melompat di akhir ketika panjang (garis) tetap sama untuk beberapa kali ...

Terlalu buruk
sumber
Saya tahu, ini mirip dengan jawaban Bob, tetapi menjelaskan kasus tertentu. DAN aku tidak bisa mengomentari jawaban Bob ... Terjebak dengan aturan stackoverflow :(
TooGeeky
0

Saya menggunakan fungsi untuk ini:

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