1

This is the first code I've written in c#, and my first question to Stackoverflow...apologies up front if I'm doing everything wrong! :-/

I've tried to implement the Public Class RTFScrolledToBottom written by LarsTech that was posted as answered the question here: Get current scroll position from rich text box control?

In the public Form1() code block, this line is generating a CS1061 error:

rtfScrolledBottom1.ScrolledToBottom += rtfScrolledBottom1_ScrolledToBottom;

object does not contain a definition for ScrolledToBottom and no accessible extension method ScrolledToBottom accepting a first argument of type object could be found (are you missing a using directive or an assembly reference?)

Thanks in advance for any assistance pointing me to what I'm screwing up!!

Cheers!

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private object rtfScrolledBottom1;

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

        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {

        }
    }



    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);
        }
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
Kent Smith
  • 11
  • 2
  • You have defined `rtfScrolledBottom1` as object. You need to define it as `RTFScrolledBottom`. You can drop an instance of the `RTFScrolledBottom` control from toolbox. – Reza Aghaei Sep 07 '18 at 19:35

1 Answers1

0

You have defined rtfScrolledBottom1 as object and object doesn't have any event. You need to define it as RTFScrolledBottom. You also can drop an instance of the RTFScrolledBottom control from toolbox and use it like any other control.

Alternative solution

As an alternative to the solution which you found in the linked post, here is another solution which Works with RichTextBox without creating a derived control, while you can put the logic in a derived control and make it more reusable, like what has done in the linked post.

You can handle VScroll event of the RichTextBox and get the scroll position by calling GetScrollInfo method. Then in the SCROLLINFO if nPage + nPos == nMax, it means the scroll is at bottom:

[StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO {
    public int cbSize;
    public ScrollInfoMask fMask;
    public int nMin;
    public int nMax;
    public uint nPage;
    public int nPos;
    public int nTrackPos;
}
public 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),
}
[DllImport("user32.dll")]
private static extern bool GetScrollInfo(IntPtr hwnd, SBOrientation fnBar, 
    ref SCROLLINFO lpsi);
public enum SBOrientation : int { SB_HORZ = 0x0, SB_VERT = 0x1 }

private void richTextBox1_VScroll(object sender, EventArgs e)
{
    var info = new SCROLLINFO() { 
        cbSize = (Marshal.SizeOf<SCROLLINFO>()),
        fMask = ScrollInfoMask.SIF_ALL 
    };
    GetScrollInfo(richTextBox1.Handle, SBOrientation.SB_VERT, ref info);
    if (info.nPage + info.nPos == info.nMax)
    {
        //VScroll is at bottom
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • Two things. First, when using `GetScrollInfo` you **need** to set the `cbSize` of `SCROLLINFO` struct before calling it. Second one, when user uses the `thumb` to scroll, you can not get the thumb position this way unless the user releases the mouse. So it is not an *Alternative solution*! – γηράσκω δ' αεί πολλά διδασκόμε Sep 07 '18 at 22:49
  • @γηράσκωδ'αείπολλάδιδασκόμε I've tried both solutions, I'd prefer what I shared here. For example in the linked solution, when the scroll is at bottom, the event will be raised even by changing the caret position, or it will be raised by the first scroll-up. Anyway, future readers can choose at their preference. – Reza Aghaei Sep 08 '18 at 05:47