5

I have a label which labels the line numbers based on the text on RichTextBox. I have hooked the event of Vscroll to handle the labeling.

private void rtbLogicCode_VScroll(object sender, EventArgs e)
{
    Point pt = new Point(0, 1);
    int firstIndex = rtbLogicCode.GetCharIndexFromPosition(pt);
    int firstLine = rtbLogicCode.GetLineFromCharIndex(firstIndex);

    pt.X = ClientRectangle.Width;
    pt.Y = ClientRectangle.Height;
    int lastIndex = rtbLogicCode.GetCharIndexFromPosition(pt);
    int lastLine = rtbLogicCode.GetLineFromCharIndex(lastIndex);

    // Small correction
    if (rtbLogicCode.Text.EndsWith("\n"))
        lastLine++;

    labelLogicCode.ResetText();
    LabelLineNum(firstLine+1,lastLine);
}
#endregion

private void LabelLineNum(int startNum, int lastNum)
{
    labelLogicCode.Font = UIConstant.DDCLogicCodeFont;
    for (int i = startNum; i < lastNum; i++)
    {
        labelLogicCode.Text += i + Environment.NewLine;
    }
}

Everything seems to work properly except RichTextBox uses Smooth Scrolling feature, which screws up my line numbering in many cases where the user has not scrolled all the way to the next line. This causes the line numbers to be not synchronized with the actual text shown on the RichTextBox.

In the end, I need to disable smoothscrolling feature to accomplish this. I was told that you can override the postMessage API of RichTextBox to disable the mentioned feature but after searching through many documents, I couldn't find any good ones.

I would appreciate a solution that is as detailed as possible on how to disable smoothscrolling feature. Thanks.

PhilMY
  • 2,621
  • 21
  • 29
TtT23
  • 6,876
  • 34
  • 103
  • 174

3 Answers3

5

Here's a VB example from Microsoft, suggesting you need to intercept WM_MOUSEWHEEL messages.

Here's a quick prototype in C#:

class MyRichTextBox : RichTextBox {

    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(
          IntPtr hWnd,      // handle to destination window
          uint Msg,       // message
          IntPtr wParam,  // first message parameter
          IntPtr lParam   // second message parameter
    );

    const uint WM_MOUSEWHEEL = 0x20A;
    const uint WM_VSCROLL = 0x115;
    const uint SB_LINEUP = 0;
    const uint SB_LINEDOWN = 1;
    const uint SB_THUMBTRACK = 5;

    private void Intercept(ref Message m) {
        int delta = (int)m.WParam >> 16 & 0xFF;
        if((delta >> 7) == 1) {
            SendMessage(m.HWnd, WM_VSCROLL, (IntPtr)SB_LINEDOWN, (IntPtr)0);
        } else {
            SendMessage(m.HWnd, WM_VSCROLL, (IntPtr)SB_LINEUP, (IntPtr)0);
        }
    }

    protected override void WndProc(ref Message m) {
        switch((uint)m.Msg) {
            case WM_MOUSEWHEEL:
                Intercept(ref m);
                break;
            case WM_VSCROLL:
                if(((uint)m.WParam & 0xFF) == SB_THUMBTRACK) {
                    Intercept(ref m);
                } else {
                    base.WndProc(ref m);
                }
                break;
            default:
                base.WndProc(ref m);
                break;
        }
    }
}
Test User
  • 3
  • 2
PhilMY
  • 2,621
  • 21
  • 29
  • I looked at the code for a while and honestly don't understand how that code works as I have no expertise in VB. Could I get an explanation of the code in C# perhaps? – TtT23 Jun 20 '12 at 07:57
  • I've added a similar example in C# - it's a bit rough around the edges but should get you started. – PhilMY Jun 20 '12 at 08:59
  • This part `if(((uint)m.WParam & 0xFF) == SB_THUMBTRACK) { Intercept(ref m); }` has the side effect of not refreshing the content when pressing down the scroll thumb. And I'm not sure about the purpose of it. (Ok, that's an old answer.) – Léon Pelletier Dec 04 '17 at 03:01
1

I know this is old, but if Dan Sporici's site ever goes down I thought I post his fantastic solution. It's simple and works easily with just a copy & paste.

class editedRichTextBox : RichTextBox
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

    //this message is sent to the control when we scroll using the mouse
    private const int WM_MOUSEWHEEL = 0x20A;

    //and this one issues the control to perform scrolling
    private const int WM_VSCROLL = 0x115;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MOUSEWHEEL)
        {
            int scrollLines = SystemInformation.MouseWheelScrollLines;
            for (int i = 0; i < scrollLines; i++)
            {
                if ((int)m.WParam > 0) // when wParam is greater than 0
                    SendMessage(this.Handle, WM_VSCROLL, (IntPtr)0, IntPtr.Zero); // scroll up 
                else  
                    SendMessage(this.Handle, WM_VSCROLL, (IntPtr)1, IntPtr.Zero); // else scroll down
            }
            return;
        }
        base.WndProc(ref m);
    }
}
Ryan Naccarato
  • 674
  • 8
  • 12
0

This will solve for VB.NET as well as C# (use code converter).

  1. Just paste this code outside your own class (Eg: Public Class Form1).
  2. Run the code once and new control will be added to the toolbox.
  3. Now add the control to your FORM from the toolbox in IDE.

Code:

Public Class MyRichTextBox : Inherits RichTextBox
<System.Runtime.InteropServices.DllImport("user32.dll")> Public Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
End Function
Protected Overrides Sub WndProc(ByRef m As Message)
    If m.Msg = &H20A Then : Dim ud As IntPtr
        If m.WParam.ToInt32 > 0 Then ud = 0 Else ud = 1
        For i = 1 To 3 : SendMessage(Me.Handle, &H115, ud, 0) : Next
    Else : MyBase.WndProc(m) : End If
End Sub
End Class