禁用RichTextBox上的平滑滚动

5

我有一个标签,基于RichTextBox上的文本对行号进行标记。我已经钩住了Vscroll事件来处理标记。

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;
    }
}

除了RichTextBox使用平滑滚动功能外,一切似乎都正常工作。在许多情况下,用户没有滚动到下一行,导致我的行号计数出现问题。这会导致行号与RichTextBox上显示的实际文本不同步。
最终,我需要禁用平滑滚动功能才能完成此操作。有人告诉我可以覆盖RichTextBox的postMessage API以禁用该功能,但是在搜索了许多文档后,我找不到任何好的解决方案。
我希望能够提供尽可能详细的解决方案来禁用平滑滚动功能。谢谢。

不是针对你的问题的答案,但从长远来看,可能需要更高级的编辑控件... Scintilla 是一个可能的选择。 - Roman Starkov
您无法关闭此功能。 - Hans Passant
请查看此链接 - https://dev59.com/7VTTa4cB1Zd3GeqPpBay#4920372 和此链接 - http://www.codeproject.com/Articles/7830/Scrolling-Around-with-the-RichTextBox-Control - Angshuman Agarwal
3个回答

5
这里有一个来自微软的VB示例,建议你需要拦截WM_MOUSEWHEEL消息。
以下是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;
        }
    }
}

我看了一段时间的代码,由于我没有VB方面的专业知识,实在不明白这段代码是如何工作的。或许可以给我提供一份C#的代码解释吗? - TtT23
我已经添加了一个类似的C#示例 - 它还有一些缺陷,但应该能够让您开始。 - PhilMY
这段代码 if(((uint)m.WParam & 0xFF) == SB_THUMBTRACK) { Intercept(ref m); } 的副作用是在按下滚动条拇指时不刷新内容。而我也不确定它的目的是什么。(好吧,这是一个旧答案。) - Léon Pelletier

1

我知道这篇文章很老,但如果Dan Sporici的网站挂了,我想发布他的绝妙解决方案。这个方法简单易行,只需复制粘贴即可。

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);
    }
}

0

这将解决VB.NET和C#(使用代码转换器)。

  1. 只需将此代码粘贴到您自己的类之外(例如:Public Class Form1)。
  2. 运行代码一次,新控件将添加到工具箱中。
  3. 现在从IDE工具箱中将控件添加到您的表单中。

代码:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接