it-swarm.com.de

Wie scrolle ich automatisch zum Ende eines mehrzeiligen Textfelds?

Ich habe ein Textfeld mit der .Multiline-Eigenschaft auf true gesetzt. In regelmäßigen Abständen füge ich neue Textzeilen hinzu. Ich möchte, dass das Textfeld automatisch zum untersten Eintrag (dem neuesten) blättert, wenn eine neue Zeile hinzugefügt wird. Wie mache ich das?

271
GWLlosa

In regelmäßigen Abständen füge ich neue Textzeilen hinzu. Ich möchte, dass das Textfeld automatisch zum untersten Eintrag (dem neuesten) blättert, wenn eine neue Zeile hinzugefügt wird.

Wenn Sie TextBox.AppendText(string text) verwenden, wird automatisch zum Ende des neu angehängten Texts gescrollt. Die flackernde Bildlaufleiste wird vermieden, wenn Sie sie in einer Schleife aufrufen.

Es ist auch eine Größenordnung schneller als die Verkettung auf das .Text Eigentum. Das hängt allerdings davon ab, wie oft Sie anrufen. Ich habe mit einer engen Schleife getestet.


Dies führt keinen Bildlauf durch, wenn es aufgerufen wird, bevor das Textfeld angezeigt wird, oder wenn das Textfeld ansonsten nicht sichtbar ist (z. B. in einem anderen Tab eines TabPanels). Siehe TextBox.AppendText () nicht Autoscrolling . Dies kann wichtig oder unwichtig sein, je nachdem, ob Sie einen automatischen Bildlauf benötigen, wenn der Benutzer das Textfeld nicht sehen kann.

Es scheint, dass die alternative Methode aus den anderen Antworten auch in diesem Fall nicht funktioniert. Eine Möglichkeit, dies zu umgehen, besteht darin, beim Ereignis VisibleChanged einen zusätzlichen Bildlauf durchzuführen:

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

Intern macht AppendText so etwas:

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

Es sollte aber keinen Grund geben, dies manuell zu tun.

(Wenn Sie es selbst dekompilieren, werden Sie feststellen, dass es einige möglicherweise effizientere interne Methoden verwendet und einen geringfügigen Sonderfall aufweist.)

388
Bob

Sie können den folgenden Codeausschnitt verwenden:

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

dadurch wird automatisch bis zum Ende gescrollt.

141
GWLlosa

Es scheint, dass sich die Schnittstelle in . NET 4.0 geändert hat. Es gibt die folgende Methode , die alle oben genannten Ziele erreicht. Wie von Tommy Engebretsen vorgeschlagen, erfolgt dies automatisch, wenn Sie es in einen TextChanged-Ereignishandler einfügen.

textBox1.ScrollToEnd();
39
JohnDRoach

Versuchen Sie, den vorgeschlagenen Code zum TextChanged-Ereignis hinzuzufügen:

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

Ich musste eine Aktualisierung hinzufügen:

textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();
textBox1.Refresh();
8
h4nd
textBox1.Focus()
textBox1.SelectionStart = textBox1.Text.Length;
textBox1.ScrollToCaret();

hat bei mir nicht funktioniert (Windows 8.1, aus welchem ​​Grund auch immer).
Und da ich immer noch auf .NET 2.0 bin, kann ich ScrollToEnd nicht verwenden.

Aber das funktioniert:

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
Stefan Steiger

Ich habe einen einfachen Unterschied gefunden, der in diesem Thread nicht angesprochen wurde.

Wenn Sie alle ScrollToCarat() -Aufrufe als Teil des Load() -Ereignisses Ihres Formulars ausführen, funktioniert dies nicht. Ich habe gerade meinen ScrollToCarat() -Aufruf zum Activated() -Ereignis meines Formulars hinzugefügt, und es funktioniert einwandfrei.

Bearbeiten

Es ist wichtig, diesen Bildlauf nur beim Auslösen des Ereignisses Activated des ersten Formulars durchzuführen (nicht bei nachfolgenden Aktivierungen), oder es wird bei jeder Aktivierung des Formulars ein Bildlauf ausgeführt jedes Mal. Dies ist wahrscheinlich etwas, das Sie tun will nicht.

Wenn Sie also nur das Ereignis Activated() abfangen, um beim Laden Ihres Programms einen Bildlauf durch Ihren Text durchzuführen, können Sie das Ereignis einfach im Ereignishandler selbst abbestellen.

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

Wenn Sie bei jeder Aktivierung Ihres Formulars andere Dinge tun müssen, können Sie beim ersten Auslösen Ihres Ereignisses Activated() ein bool auf true setzen, damit Sie nicht weiterblättern nachfolgende Aktivierungen, können aber noch die anderen Dinge tun, die Sie tun müssen.

Wenn sich Ihr TextBox auf einer Registerkarte befindet, die nicht das SelectedTab ist, hat ScrollToCarat() keine Auswirkung. Sie müssen es also mindestens zur ausgewählten Registerkarte machen, während Sie scrollen. Sie können den Code in ein YourTab.SuspendLayout(); - und YourTab.ResumeLayout(false); -Paar einschließen, wenn Ihr Formular dabei flackert.

Ende der Bearbeitung

Hoffe das hilft!

3
Pete

Dadurch wird zum Ende des Textfelds gescrollt, wenn der Text geändert wird, der Benutzer kann jedoch trotzdem nach oben scrollen

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

getestet auf Visual Studio Enterprise 2017

1
Eric Shreve

Für alle anderen, die hier landen und eine Webforms-Implementierung erwarten, möchten Sie den endRequest-Ereignishandler des Seitenanforderungs-Managers verwenden ( https://stackoverflow.com/a/1388170/1830512 ). Folgendes habe ich für meine TextBox in einer Inhaltsseite von einer Masterseite getan. Bitte ignorieren Sie die Tatsache, dass ich keine Variable für das Steuerelement verwendet habe:

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
G.P. Greenleaf

Das hat nur bei mir funktioniert ...

txtSerialLogging-> Text = "";

txtSerialLogging-> AppendText (s);

Ich habe alle oben genannten Fälle ausprobiert, aber das Problem ist, dass in meinem Fall der Text s abnehmen, zunehmen und auch lange statisch bleiben kann. statisch bedeutet, statische Länge (Linien), aber Inhalt ist unterschiedlich.

Also war ich am Ende mit einer Linesprung-Situation konfrontiert, als die Länge (Lines) für einige Zeit gleich blieb ...

0
TooGeeky

Ich benutze dafür eine Funktion:

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