3

my question is as is:How to detect if a scrollbar is at or not at the end of a richtextbox?

edit: when I say at the end I mean completely scrolled to the bottom, not anywhere else.

fbernier
  • 12,278
  • 2
  • 24
  • 30

4 Answers4

2

I used this code to get the current and maximum positions correctly:

    const int SB_HORZ          = 0x0000;
    const int SB_VERT          = 0x0001;
    const int WM_HSCROLL       = 0x0114;
    const int WM_VSCROLL       = 0x0115;
    const int SB_THUMBPOSITION = 4;

    private enum ScrollInfoMask : uint
    {
        SIF_RANGE           = 0x1,
        SIF_PAGE            = 0x2,
        SIF_POS             = 0x4,
        SIF_DISABLENOSCROLL = 0x8,
        SIF_TRACKPOS        = 0x10,
        SIF_ALL             = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS)
    }

    [StructLayout(LayoutKind.Sequential)]
    struct SCROLLINFO
    {
        public uint cbSize;
        public uint fMask;
        public int  nMin;
        public int  nMax;
        public uint nPage;
        public int  nPos;
        public int  nTrackPos;
    }

    public int HScrollPosition
    {
        get
        {
            return GetScrollPos(Handle, SB_HORZ);
        }
        set
        {
            SetScrollPos((IntPtr)Handle, SB_HORZ, value, true);
            PostMessageA((IntPtr)Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
        }
    }

    public int VScrollPosition
    {
        get
        {
            return GetScrollPos(Handle, SB_VERT);
        }
        set
        {
            SetScrollPos((IntPtr)Handle, SB_VERT, value, true);
            PostMessageA((IntPtr)Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
        }
    }

    public int HScrollPositionMax
    {
        get
        {
            SCROLLINFO scrollInfo = new SCROLLINFO();
            scrollInfo.fMask = (uint)ScrollInfoMask.SIF_ALL;
            scrollInfo.cbSize = (uint)Marshal.SizeOf(scrollInfo);
            GetScrollInfo(Handle, SB_HORZ, ref scrollInfo);

            return scrollInfo.nMax - (int)scrollInfo.nPage;
        }
    }

    public int VScrollPositionMax
    {
        get
        {
            SCROLLINFO scrollInfo = new SCROLLINFO();
            scrollInfo.fMask = (uint)ScrollInfoMask.SIF_ALL;
            scrollInfo.cbSize = (uint)Marshal.SizeOf(scrollInfo);
            GetScrollInfo(Handle, SB_VERT, ref scrollInfo);

            return scrollInfo.nMax - (int)scrollInfo.nPage;
        }
    }

It's tedious to get the "correct" maximum value. You have to substract the nPage (large change) value of the max value GetScrollRange returns, and then you can compare it with the HScrollPosition property correctly, like:

if (_rtb.VScrollPosition == _rtb.VScrollPositionMax)
{
    Debug.WriteLine("Scroll is at the bottom most edge");
}
Ray
  • 7,940
  • 7
  • 58
  • 90
  • I have noticed that when a RichTextBox has a "display" area height that is not a multiple of the font height, sometimes the scroll position ( eg. nPos ) is not quite equal to the max ( eg. nMax - nPage ), because there is a "partial line" of text below the last actual line of text, but the scroll position is quantized to the nearest line, making nMax a bit higher than expected. I suppose one could also subtract from nMax the height of a line of text less one -- this way, a scroll position that is "close enough" counts as being "at max". Will probably try it; might report back. – bernz Jul 20 '17 at 12:48
2

Check out the GetScrollRange and GetScrollPos API...

Private Const SBS_HORZ = 0
Private Const SBS_VERT = 1

<DllImport("user32.dll")> _
Public Function GetScrollRange(ByVal hWnd As IntPtr, ByVal nBar As Integer, _
                               ByRef lpMinPos As Integer, _
                               ByRef lpMaxPos As Integer) As Boolean
End Function

<DllImport("user32.dll")> _
Public Function GetScrollPos(ByVal hWnd As Integer, _
                             ByVal nBar As Integer) As Integer
End Function

// ...

Dim scrollMin as Integer = 0
Dim scrollMax as Integer = 0

If(GetScrollRange(rtb.Handle, SBS_VERT, scrollMin, scrollMax) Then
   Dim pos as Integer = GetScrollPos(rtb.Handle, SBS_VERT)

   // Detect if they're at the bottom
EndIf

Notes:

To determine if the scrollbar is visible, call GetWindowLong and check for WS_VSCROLL

To determine the max value the slider can get to, call GetScrollInfo; I think the maximum value is

scrollMax - largeChange + 1
Daniel LeCheminant
  • 50,583
  • 16
  • 120
  • 115
  • ok works great, but two more things: how can I know if the scrollbar is visible, and how can I get the last possible position of the scrollbar? – fbernier Mar 18 '09 at 03:43
  • I am able to make a call to getScrollInfo but I just don't know how to build the function which will return the last position. could you help me with that? – fbernier Mar 18 '09 at 14:43
1
        if (richTextBox1.Size.Width - richTextBox1.ClientSize.Width > 10) 

Sorry, you'll have to convert that to VB.NET yourself. Shouldn't be too hard :)

Orwellophile
  • 13,235
  • 3
  • 69
  • 45
0

One method is to use the GetScrollPos function. Here's kind of a long winded example of it using VB.NET.

http://www.codeproject.com/KB/vb/VbNetScrolling.aspx?print=true

BobbyShaftoe
  • 28,337
  • 7
  • 52
  • 74