9

I have searched the internet far and wide and seen many questions like this, but I have not seen an actual answer.

I have a rich text box control with lots of text in it. It has some legal information in this control. By default the "Accept" button is disabled. I want to detect on the scroll event if the position of the v-scroll bar is at the bottom. If it is at the bottom, enable the button.

How would I detect the current v-scroll bar position?

Thank You!

EDIT I am using WinForms (.Net 4.0)

Landin Martens
  • 3,283
  • 12
  • 43
  • 61

3 Answers3

19

This should get you close to what you are looking for. This class inherits from the RichTextBox and uses some pinvoking to determine the scroll position. It adds an event ScrolledToBottom which gets fired if the user scrolls using the scrollbar or uses the keyboard.

public class RTFScrolledBottom : RichTextBox {
  public event EventHandler ScrolledToBottom;

  private const int WM_VSCROLL = 0x115;
  private const int WM_MOUSEWHEEL = 0x20A;
  private const int WM_USER = 0x400;
  private const int SB_VERT = 1;
  private const int EM_SETSCROLLPOS = WM_USER + 222;
  private const int EM_GETSCROLLPOS = WM_USER + 221;

  [DllImport("user32.dll")]
  private static extern bool GetScrollRange(IntPtr hWnd, int nBar, out int lpMinPos, out int lpMaxPos);

  [DllImport("user32.dll")]
  private static extern IntPtr SendMessage(IntPtr hWnd, Int32 wMsg, Int32 wParam, ref Point lParam);

  public bool IsAtMaxScroll() {
    int minScroll;
    int maxScroll;
    GetScrollRange(this.Handle, SB_VERT, out minScroll, out maxScroll);
    Point rtfPoint = Point.Empty;
    SendMessage(this.Handle, EM_GETSCROLLPOS, 0, ref rtfPoint);

    return (rtfPoint.Y + this.ClientSize.Height >= maxScroll);
  }

  protected virtual void OnScrolledToBottom(EventArgs e) {
    if (ScrolledToBottom != null)
      ScrolledToBottom(this, e);
  }

  protected override void OnKeyUp(KeyEventArgs e) {
    if (IsAtMaxScroll())
      OnScrolledToBottom(EventArgs.Empty);

    base.OnKeyUp(e);
  }

  protected override void WndProc(ref Message m) {
    if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL) {
      if (IsAtMaxScroll())
        OnScrolledToBottom(EventArgs.Empty);
    }

    base.WndProc(ref m);
  }

}

This is then how it can get used:

public Form1() {
  InitializeComponent();
  rtfScrolledBottom1.ScrolledToBottom += rtfScrolledBottom1_ScrolledToBottom;
}

private void rtfScrolledBottom1_ScrolledToBottom(object sender, EventArgs e) {
  acceptButton.Enabled = true;
}

Tweak as necessary.

LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • Note that the scroll position is not updated while the user is holding the scroll bar and moving it. Only after the mouse button is released. – Sellorio Jul 26 '13 at 01:59
  • Could you please explain why do we need to add `this.ClientSize.Height` to the scroll position? Why doesn't the scroll position equal `maxScroll` even if the scroll is at the very bottom? – Dmitrii Erokhin Sep 03 '13 at 05:45
  • @DmitryErokhin Good question. The minScroll and maxScroll gives the range of the scrolling distance, but it's offset by the size of the control, which for the vertical scrollbar is the control's height. Basically, rtfPoint.Y will never equal maxScroll when there is a scroll range in play because of that size offset. – LarsTech Sep 03 '13 at 13:19
6

The following works very well in one of my solutions:

Point P = new Point(rtbDocument.Width, rtbDocument.Height);
int CharIndex = rtbDocument.GetCharIndexFromPosition(P);

if (rtbDocument.TextLength - 1 == CharIndex)
{
   btnAccept.Enabled = true;
}
RooiWillie
  • 2,198
  • 1
  • 30
  • 36
5

The question How to get scroll position for RichTextBox? could be helpful, Check out this function

   richTextBox1.GetPositionFromCharIndex(0);
Community
  • 1
  • 1
major
  • 323
  • 1
  • 4
  • 11