Question How do I save and restore VScroll position of a RichTextBox

robertb_NZ

Well-known member
Joined
May 11, 2010
Messages
146
Location
Auckland, New Zealand
Programming Experience
10+
I have a form containing a RichTextBox called ProgramArea; if the text in ProgramArea exceeds the space available then scroll bars appear automatically. When users click the [CHECK] button their program is checked and the text changed (colored, expanded, error messages, etc), and the RTB is re-displayed, but it is always re-displayed starting from the top of the text. I would like it to be re-displayed with the same scrolling as before.

Internally the logic of [Check] is: -
Private Sub btnCheck_Click(sender As System.Object, e As System.EventArgs) Handles btnCheck.Click
JazzProcessTo(0, 9999) ' From start to finish​
End Sub​
so that I'd like to surround the JazzProcessTo reference with something like this: -
Dim SaveVScroll = ProgramArea.VScroll.Value
JazzProcessTo(0, 9999)
ProgramArea.VScroll.Value = SaveVScroll​

However I can't write this, and I can't find any way of referring to the properties of the automatic ScrollBars associated with the RTB. So how do I prevent the program from re-displaying the RTB from the top?

If you want to see some context, have a look at http://www.jazzsoftware.co.nz/Videos/demo1intro/demo1intro.html about 30 seconds from start.
 
It appears there exist native functions GetScrollPos/SetScrollPos. As alternative you could get/set SelectionStart and use ScrollToCaret if scrolling often is a result of caret position.
 
JohnH, thank you for your response.

RichTextBox doesn't support GetScrollPos/SetScrollPos.

I tried using get/set SelectionStart and ScrollToCaret with
Private Sub btnCheck_Click(sender As System.Object, e As System.EventArgs) Handles btnCheck.Click
Dim P = ProgramArea.SelectionStart
JazzProcessTo(0, 9999) ' From start to finish
ProgramArea.SelectionStart = P
ProgramArea.ScrollToCaret()​
End Sub​

This partly worked, but puts the selection point at the top or bottom of the RTB, which is not the same as putting the selection point at the same relative position. This is no improvement on the default behavior; and besides it depends on the user having made changes within the visible part of the RTB, which is not always true. Using GetScrollPos/SetScrollPos (or GetScrollInfo etc) seems the correct approach.

I found this URL https://stackoverflow.com/questions/1619783/get-vertical-scroll-bar-position-as-integer-in-vb-net. It suggests that I should create my own RTB class derived from RichTextBox so that I can add ScrollPos properties. I've been trying to make this work, I've got as far as compiling successfully, but so far no further. I'll keep trying. I've also searched for other references, nothing seems useful.

Are there any other documents that I should read, particularly one with an example that shows me how to do what I want?

Thank you, Robert.
 
I have made progress: -Now when I invoke btnCheck_Click: -
Private Sub btnCheck_Click(sender As System.Object, e As System.EventArgs) Handles btnCheck.Click
Dim VScrollpos = ProgramArea.VScrollPos
JazzProcessTo(0, 9999) ' From start to finish
ProgramArea.VScrollPos = VScrollpos​
End Sub​
the form is returned with the scroll bar correctly positioned. However the text is shown from the beginning, not scrolled.

To position the text correctly I need to click on the scroll bar. How do I do this by program? I can't seem to find a way of referring to the Vertical Scroll Bar of ProgramArea.

Thank you, Robert.
 
Sorted! I found another paper
http://codebetter.com/patricksmacchia/2008/07/07/some-richtextbox-tricks/

and from this I found a method to post a message to the control. I converted the code given from C# to VB with the Telerik code converter (http://converter.telerik.com/), and extracted from this the bits that I wanted. First the PostMessage function: -
VB.NET:
    <DllImport("user32.dll")> _
    Private Shared Function PostMessageA(ByVal hWnd As IntPtr, ByVal nBar As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
    End Function

Then a couple of constants: -
VB.NET:
    Private Const WM_VSCROLL As Integer = 277
    Private Const SB_THUMBPOSITION As Integer = 4

and then added this into the VScrollPos Set: -
VB.NET:
        Set(ByVal value As Integer)
            SetScrollPos(Me.Handle, SB_VERT, value, True)
            PostMessageA(Me.Handle, WM_VSCROLL, SB_THUMBPOSITION + 65536 * value, 0)
        End Set

This all works perfectly. Note that at present I have set the property ScrollBars to ForceVertical, so that it always appears because the StackOverflow paper warns me that the code won't work if the scrollbar is not visible. It should be relatively easy (test VScroll property) to remove this requirement.

Here is my myRTB code so far: -
VB.NET:
Imports System.Runtime.InteropServices
Public Class MyRTB
    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
    Public Shared Function GetScrollPos(ByVal hWnd As IntPtr, ByVal nBar As Integer) As Integer
    End Function
    <DllImport("user32.dll")> _
    Private Shared Function SetScrollPos(ByVal hWnd As IntPtr, ByVal nBar As Integer, ByVal nPos As Integer, ByVal bRedraw As Boolean) As Integer
    End Function
    <DllImport("user32.dll")> _
    Private Shared Function PostMessageA(ByVal hWnd As IntPtr, ByVal nBar As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
    End Function

    Private Const SB_VERT As Integer = &H1
    Private Const WM_VSCROLL As Integer = 277
    Private Const SB_THUMBPOSITION As Integer = 4

    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        'Add your custom paint code here
    End Sub
    Public Property VScrollPos() As Integer
        Get
            Return GetScrollPos(Me.Handle, SB_VERT)
        End Get
        Set(ByVal value As Integer)
            SetScrollPos(Me.Handle, SB_VERT, value, True)
            PostMessageA(Me.Handle, WM_VSCROLL, SB_THUMBPOSITION + 65536 * value, 0)
        End Set
    End Property
End Class
 
Back
Top