Come scorrere automaticamente fino alla fine di una casella di testo multilinea?


295

Ho una casella di testo con la proprietà .Multiline impostata su true. A intervalli regolari, sto aggiungendo nuove righe di testo. Vorrei che la casella di testo scorresse automaticamente fino alla voce più in basso (la più recente) ogni volta che viene aggiunta una nuova riga. Come posso farlo?


6
Ho cercato qui la risposta, non sono riuscito a trovarla, quindi quando l'ho capito, ho pensato di metterlo qui per i futuri utenti, o se forse qualcun altro avesse un approccio migliore.
GWLlosa,

2
Avevo bisogno di fare la stessa cosa in VBA, che non ha tutti questi nuovi metodi .NET fantasiosi. Per il futuro google-fu, ecco l'incantesimo: TextBox1.Text = TextBox1.Text & "qualunque"; TextBox1.SelStart = Len (TextBox1.Text); TextBox1.SetFocus; ... e poi un .SetFocus di nuovo su qualunque controllo avesse prima messo a fuoco. Senza concentrarsi su TextBox1, non aggiornerebbe mai le sue barre di scorrimento, qualunque cosa io abbia fatto.
Gordon Broom,

1
@GordonBroom Whelp, grazie a ciò inizierò a chiamare "frammenti di codice" "incantesimi" ora. Buon lavoro. : D
Sidney,

Risposte:


425

A intervalli regolari, sto aggiungendo nuove righe di testo. Vorrei che la casella di testo scorresse automaticamente fino alla voce più in basso (la più recente) ogni volta che viene aggiunta una nuova riga.

Se lo usi TextBox.AppendText(string text), scorrerà automaticamente fino alla fine del testo appena aggiunto. Evita la barra di scorrimento tremolante se la stai chiamando in un ciclo.

Capita anche di essere un ordine di grandezza più veloce del concatenarsi sulla .Textproprietà. Anche se ciò potrebbe dipendere da quanto spesso lo chiami; Stavo testando con un circuito chiuso.


Questo non scorrerà se viene chiamato prima della visualizzazione della casella di testo o se la casella di testo non è altrimenti visibile (ad esempio in una scheda diversa di un TabPanel). Vedere TextBox.AppendText () non autoscrolling . Ciò può essere o non essere importante, a seconda che sia necessario lo scorrimento automatico quando l'utente non può vedere la casella di testo.

Sembra che anche il metodo alternativo dalle altre risposte non funzioni in questo caso. Un modo per aggirarlo è eseguire ulteriori scroll VisibleChangedsull'evento:

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

Internamente, AppendTextfa qualcosa del genere:

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

Ma non ci dovrebbero essere motivi per farlo manualmente.

(Se lo decompili da solo, vedrai che utilizza alcuni metodi interni forse più efficienti e ha quello che sembra essere un caso speciale minore.)


7
Questo metodo è molto più veloce e fluido. Non c'è 'sfarfallio' della barra di scorrimento (che è più evidente quando si effettuano molte chiamate in rapida successione).
TallGuy

3
Questa è una soluzione molto migliore.
Jeff,

3
tb.Text += ....Stavo mangiando me stesso cercando di farlo con WndProc e marescialli Ora mi sento stupido: D
Saeid Yazdani,

3
Anche la textarea deve essere focalizzata, la prima volta che l'ho fatto non scorreva perché non aveva il focus.
Qwerty01

3
AppendText non ha fatto scorrere automaticamente il mio TextBox ReadOnly, ma ha aggiunto TextBox.ScrollToEnd (); dopo la chiamata AppendText ha fatto il trucco.
Brandon Barkley,

143

Puoi utilizzare il seguente frammento di codice:

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

che scorrerà automaticamente fino alla fine.


5
Ho cercato qui la risposta, non sono riuscito a trovarla, quindi quando l'ho capito, ho pensato di metterlo qui per i futuri utenti, o se forse qualcun altro avesse un approccio migliore.
GWLlosa,

4
Questa potrebbe essere stata la risposta migliore al momento, ma ora penso che la risposta di Bob sia una soluzione migliore al problema del PO.
Tomsv,

38

Sembra che l'interfaccia sia cambiata in .NET 4.0. Esiste il seguente metodo che ottiene tutto quanto sopra. Come suggerito Tommy Engebretsen, inserirlo in un gestore di eventi TextChanged lo rende automatico.

textBox1.ScrollToEnd();

21
Si noti che quel metodo è nella TextBoxBaseclasse nello System.Windows.Controls.Primitivesspazio dei nomi ( PresentationFrameworkassembly, WPF). Questo metodo non esiste e non funzionerà in WinForms, la cui TextBoxclasse eredita dallo spazio TextBoxBasedei System.Windows.Formsnomi ( System.Windows.Formsassembly, WinForms).
Bob

1
Nota che ScrollToEnd()può essere estremamente scarso. Nella mia app ha rappresentato oltre il 50% del tempo di profilazione.
ergohack,

16

Prova ad aggiungere il codice suggerito all'evento 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();

non ha funzionato per me (Windows 8.1, qualunque sia il motivo).
E poiché sono ancora su .NET 2.0, non posso usare ScrollToEnd.

Ma questo funziona:

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

Ha avuto lo stesso problema con Windows 10, la soluzione alternativa funziona bene anche qui.
Hannes,

Nessuno lavorava per me, ma questo. Dio benedica la tua anima
Emirhan Özlen,

9

Avevo bisogno di aggiungere un aggiornamento:

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

4

Ho trovato una semplice differenza che non è stata affrontata in questo thread.

Se stai effettuando tutte le ScrollToCarat()chiamate come parte Load()dell'evento del tuo modulo , non funziona. Ho appena aggiunto la mia ScrollToCarat()chiamata Activated()all'evento del mio modulo e funziona benissimo.

modificare

È importante eseguire questo scorrimento solo quando l' Activatedevento del modulo viene generato per la prima volta (non nelle attivazioni successive), oppure scorrerà ogni volta che il modulo viene attivato, cosa che probabilmente non vorrai.

Quindi, se intrappoli l' Activated()evento solo per scorrere il testo quando il programma viene caricato, puoi semplicemente annullare l'iscrizione all'evento all'interno del gestore eventi stesso, quindi:

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

Se hai altre cose che devi fare ogni volta che il tuo modulo è attivato, puoi impostare a boolsu vero la prima volta che il tuo Activated()evento viene generato, quindi non scorri sulle attivazioni successive, ma puoi comunque fare le altre cose che devi fare fare.

Inoltre, se sei TextBoxsu una scheda che non è la SelectedTab, ScrollToCarat()non avrà alcun effetto. Quindi devi almeno renderlo la scheda selezionata mentre stai scorrendo. Puoi racchiudere il codice in a YourTab.SuspendLayout();e YourTab.ResumeLayout(false);accoppiare se il modulo sfarfalla quando lo fai.

Fine della modifica

Spero che questo ti aiuti!


1

Questo scorrerà fino alla fine della casella di testo quando il testo viene modificato, ma consente comunque all'utente di scorrere verso l'alto

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

testato su Visual Studio Enterprise 2017


1

Per chiunque atterra qui in attesa di vedere un'implementazione di moduli web, si desidera utilizzare il gestore eventi endRequest del gestore richieste pagina ( https://stackoverflow.com/a/1388170/1830512 ). Ecco cosa ho fatto per la mia TextBox in una pagina di contenuto da una pagina principale, per favore ignora il fatto che non ho usato una variabile per il controllo:

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

Questo ha funzionato solo per me ...

txtSerialLogging-> Text = "";

txtSerialLogging-> appendText (s);

Ho provato tutti i casi precedenti, ma il problema è che nel mio caso i messaggi di testo possono diminuire, aumentare e possono anche rimanere statici per lungo tempo. significa statico, lunghezza statica (linee) ma il contenuto è diverso.

Quindi, stavo affrontando una situazione di salto di linea alla fine quando la lunghezza (linee) rimane la stessa per alcune volte ...


Lo so, è simile alla risposta di Bob, ma spiega un caso specifico. E non posso commentare la risposta di Bob ... Bloccato con le regole dello
stackoverflow

0

Uso una funzione per questo:

private void Log (string s) {
    TB1.AppendText(Environment.NewLine + s);
    TB1.ScrollToCaret();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.