Scroll Position von TextBox abfragen und restaurieren

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    Scroll Position von TextBox abfragen und restaurieren

    Hi,

    Ich habe eine multiline Textbox, die sehr groß werden kann. Im dümmsten Fall enthält sie ein paar tausend Zeilen.

    Den Inhalt der Textbox überarbeite ich per Programm und weise diesen neuen Inhalt der Textbox zu. Dabei wird der "Scroll Regler" immer auf den Anfang der Text Box gestellt.

    Wie man sich vorstellen kann, ist das gelegentlich ganz schön lästig.

    Wie kann ich also die Position vor allem des vertical Scroll Reglers beibehalten ... etwa vor der Änderung auslesen und nach der Änderung restaurieren.

    Ich habe natürlich im Netz nach einer Lösung gesucht. Aber da habe ich nur sehr komplexe Lösungen gefunden. Das müsste doch einfacher gehen ... etwa so wie bei einer DataGrid View ....

    Wenn nicht, dann muss ich halt in den sauren Apfel beißen ... aber vorher würde ich schon gern eure geschätzte Meinung einholen ... :)

    LG
    Peter
    @Peter329 Die ScrollPosition selbst zu setzen ist etwas blöd.
    Wenn es genügt, dass der Eingabecursor wieder an derselben TextPosition steht AndAlso dass der Eingabecursor im Anzeigebereich liegt, dann so:

    VB.NET-Quellcode

    1. Dim pos = TextBox1.SelectionStart
    2. TextBox1.Text = DEIN_TEXT
    3. TextBox1.SelectionStart = Math.Min(pos, TextBox1.TextLength)
    4. TextBox1.ScrollToCaret()
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Oder eben die aktuelle Zeile merken und wiederverwenden:

    VB.NET-Quellcode

    1. Dim CurrentLineIndex = TextBox1.GetLineFromCharIndex(TextBox1.SelectionStart)
    2. '[…]
    3. TextBox1.SelectionStart = Math.Min(TextBox1.GetFirstCharIndexFromLine(CurrentLineIndex), TextBox1.Text.Length)
    4. TextBox1.ScrollToCaret()

    Besonders dann, wenn sich Anfangs- und bearbeiteter Text erheblich unterscheiden.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Hey,

    hab mich mal ein bisschen schlau gemacht und hab Dir was zusammengebastelt....

    VB.NET-Quellcode

    1. Public Class ControlScroll
    2. <DllImport("user32")>
    3. Private Shared Function GetScrollPos(handle As IntPtr, scrollBar As Integer) As Integer
    4. End Function
    5. <DllImport("user32")>
    6. Private Shared Function SetScrollPos(handle As IntPtr, scrollBar As Integer, position As Integer, redraw As Boolean) As Integer
    7. End Function
    8. <DllImport("user32")>
    9. Private Shared Function SendMessage(handle As IntPtr, message As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
    10. End Function
    11. Public Shared Function GetScrollPosition(control As Control) As Integer
    12. Return GetScrollPos(control.Handle, 1)
    13. End Function
    14. Public Shared Sub SetScrollPosition(control As Control, position As Integer)
    15. SetScrollPos(control.Handle, 1, position, True)
    16. SendMessage(control.Handle, &H115, New IntPtr((position << 16) + 4), IntPtr.Zero)
    17. End Sub
    18. End Class


    Das Ganze geht recht leicht über WindowsAPI-Aufrufe. GetScrollPos liefert die Position der vertiaklen Scrollbar, SetScrollPos erlaubt es Dir, die ScrollBar auf einen bestimmten Wert zu setzen und SendMessage übernimmt das Scrollen des Textes in der TextBox....

    Als Argumente übernehmen diese API-Aufrufe jeweils das Handle des Controls. GetScrollPos und SetScrollPos müssen wissen, welche ScrollBar gemeint ist => 0 = horizontal, 1 = vertical. SetScrollPos übernimmt zusätzlich noch die Position, auf die die ScrollBar gesetzt werden soll und ein Flag, ob neu gezeichnet werden soll.

    SendMessage braucht als Argument eine Nachricht (Message), welche in diesem Fall lautet &H115 (WM_SCROLL) und 2 Parameter wParam und lParam. wParam ist in diesem Fall die Position, auf die der Text gesetzt werden soll. lParam ist hier nicht erforderlich.

    Ich würd Dir empfehlen eine Ableitung der TextBox-Klasse zu erstellen, die diese Funktionalität eingebaut hat. Etwa so:

    VB.NET-Quellcode

    1. Public Class TextBoxX : Inherits TextBox
    2. Private _vScrollPosition As Integer
    3. Public Sub SaveVScrollPosition()
    4. _vScrollPosition = ControlScroll.GetScrollPosition(Me)
    5. End Sub
    6. Public Sub RestoreVScrollPosition()
    7. ControlScroll.SetScrollPosition(Me, _vScrollPosition)
    8. End Sub
    9. End Class


    Zu benutzen ist das ganze dann so....

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public Sub New()
    3. InitializeComponent()
    4. End Sub
    5. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    6. TextBoxX1.SaveVScrollPosition()
    7. End Sub
    8. Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    9. TextBoxX1.RestoreVScrollPosition()
    10. End Sub
    11. Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
    12. TextBoxX1.Clear()
    13. End Sub
    14. Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
    15. For i = 0 To 1000
    16. TextBoxX1.AppendText($"hello{i}{Environment.NewLine}")
    17. Next
    18. End Sub
    19. End Class


    Im Anhang noch das Project (VS2019)..... Bei Fragen einfach melden....
    Dateien
    • WindowsApp14.zip

      (18,28 kB, 94 mal heruntergeladen, zuletzt: )
    Die Unendlichkeit ist weit. Vor allem gegen Ende. ?(
    Manche Menschen sind gar nicht dumm. Sie haben nur Pech beim Denken. 8o
    Ich komme erst jetzt dazu die Vorschläge umzusetzen.

    Die Lösung mit

    VB.NET-Quellcode

    1. TextBox1.ScrollToCaret()
    ist leider nicht für meine Zwecke zufriedenstellend. Deshalb habe ich jetzt versucht die Scroll Bar Position zu restaurieren.

    Da scheitere ich aber schon beim Generieren des TextboxX Controls ... Ich habe einfach den Code von SpaceyX kopiert und eingefügt:

    VB.NET-Quellcode

    1. Public Class ControlScroll
    2. <DllImport("user32")>
    3. Private Shared Function GetScrollPos(handle As IntPtr, scrollBar As Integer) As Integer
    4. End Function
    5. <DllImport("user32")>
    6. Private Shared Function SetScrollPos(handle As IntPtr, scrollBar As Integer, position As Integer, redraw As Boolean) As Integer
    7. End Function
    8. <DllImport("user32")>
    9. Private Shared Function SendMessage(handle As IntPtr, message As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
    10. End Function
    11. Public Shared Function GetScrollPosition(control As Control) As Integer
    12. Return GetScrollPos(control.Handle, 1)
    13. End Function
    14. Public Shared Sub SetScrollPosition(control As Control, position As Integer)
    15. SetScrollPos(control.Handle, 1, position, True)
    16. SendMessage(control.Handle, &H115, New IntPtr((position << 16) + 4), IntPtr.Zero)
    17. End Sub
    18. End Class
    19. Public Class TextBoxX : Inherits TextBox
    20. Private _vScrollPosition As Integer
    21. Public Sub SaveVScrollPosition()
    22. _vScrollPosition = ControlScroll.GetScrollPosition(Me)
    23. End Sub
    24. Public Sub RestoreVScrollPosition()
    25. ControlScroll.SetScrollPosition(Me, _vScrollPosition)
    26. End Sub
    27. End Class


    Syntaxfehler gibt es keine ... aber wenn ich die TextboxX zeichne, dann erhalte ich eine Fehlermeldung (s. Anhang).

    Die Meldung ist leider wenig aussagekräftig. Was mache ich denn falsch ?

    LG
    Peter
    Bilder
    • s 2020-04-26 09-37-080.jpg

      71,1 kB, 1.225×546, 85 mal angesehen
    @SpaceyX Ein neues Projekt bringt es nicht.
    @Peter329 Ich konnte den Fehler reproduzieren.
    Danach hab ich eine leere TextBoxY mit ohne Inhalt gemacht, das funktioniert sofort.
    Also:
    Mach aus Deinen beiden Klasse eine Klasse und mach die Shared Prozeduren Private und die Nachbarin redet wieder mit Dir.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Public Class TextBoxX : Inherits TextBox
    3. <DllImport("user32")>
    4. Private Shared Function GetScrollPos(handle As IntPtr, scrollBar As Integer) As Integer
    5. End Function
    6. <DllImport("user32")>
    7. Private Shared Function SetScrollPos(handle As IntPtr, scrollBar As Integer, position As Integer, redraw As Boolean) As Integer
    8. End Function
    9. <DllImport("user32")>
    10. Private Shared Function SendMessage(handle As IntPtr, message As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
    11. End Function
    12. Private Shared Function GetScrollPosition(control As Control) As Integer
    13. Return GetScrollPos(control.Handle, 1)
    14. End Function
    15. Private Shared Sub SetScrollPosition(control As Control, position As Integer)
    16. SetScrollPos(control.Handle, 1, position, True)
    17. SendMessage(control.Handle, &H115, New IntPtr((position << 16) + 4), IntPtr.Zero)
    18. End Sub
    19. Private _vScrollPosition As Integer
    20. Public Sub SaveVScrollPosition()
    21. Me._vScrollPosition = TextBoxX.GetScrollPosition(Me)
    22. End Sub
    23. Public Sub RestoreVScrollPosition()
    24. TextBoxX.SetScrollPosition(Me, Me._vScrollPosition)
    25. End Sub
    26. End Class
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Danke ihr beiden ... jetzt funzt das !

    Ich bin begeistert ... SpaceyX hat das komplette Framework eingestellt, um die Scroll Position einer Textbox zu speichern und zu restaurieren ... und RFG hat dann noch die Hakeligkeiten des .NET Frameworks aufgeklärt. Ich bin begeistert.

    Also der "Hilfreich" Button ist da eigentlich nicht genug, um meine Wertschätzung auszudrücken. Ohne euch hätte ich das so nicht hinbekommen ! DANKE an alle Ratgeber !

    Das musste jetzt mal gesagt werden ... :)

    LG
    Peter

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()