两个RichTextBox如何同步滚动位置?

16
在我的应用程序表单中,我有两个 RichTextBox 对象。它们始终都会拥有相同的文本行数。我希望“同步”这两个对象的垂直滚动条位置,以便当用户在其中一个对象上更改垂直滚动条位置时,另一个对象也能同步滚动相同的距离。我该如何实现这一功能?
6个回答

23

感谢Jay的回答; 在进一步搜索后,我还发现了这里描述的方法。我将为其他有兴趣的人概述它。


首先,声明以下枚举:

public enum ScrollBarType : uint {
   SbHorz = 0,
   SbVert = 1,
   SbCtl = 2,
   SbBoth = 3
 }

public enum Message : uint {
   WM_VSCROLL = 0x0115
}

public enum ScrollBarCommands : uint {
   SB_THUMBPOSITION = 4
}

接下来,将外部引用添加到GetScrollPosSendMessage

[DllImport( "User32.dll" )]
public extern static int GetScrollPos( IntPtr hWnd, int nBar );

[DllImport( "User32.dll" )]
public extern static int SendMessage( IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam );

最后,为适当的 RichTextBox 添加一个事件处理程序,处理 VScroll 事件:

private void myRichTextBox1_VScroll( object sender, EventArgs e )
{
   int nPos = GetScrollPos( richTextBox1.Handle, (int)ScrollBarType.SbVert );
   nPos <<= 16;
   uint wParam = (uint)ScrollBarCommands.SB_THUMBPOSITION | (uint)nPos;
   SendMessage( richTextBox2.Handle, (int)Message.WM_VSCROLL, new IntPtr( wParam ), new IntPtr( 0 ) );
}

在这种情况下,richTextBox2 的垂直滚动位置将与 richTextBox1 同步。


非常好的总结。省了我很多时间。谢谢! - Aditya Bokade

17

我曾经为一个小项目做过这个,以下是我发现的最简单的解决方案。

通过子类化RichTextBox来创建新的控件:

   public class SynchronizedScrollRichTextBox : System.Windows.Forms.RichTextBox
    {
        public event vScrollEventHandler vScroll;
        public delegate void vScrollEventHandler(System.Windows.Forms.Message message);

        public const int WM_VSCROLL = 0x115;

        protected override void WndProc(ref System.Windows.Forms.Message msg) {
            if (msg.Msg == WM_VSCROLL) {
                if (vScroll != null) {
                    vScroll(msg);
                }
            }
            base.WndProc(ref msg);
        }

        public void PubWndProc(ref System.Windows.Forms.Message msg) {
            base.WndProc(ref msg);
        }
    }     

将新的控件添加到表单中,对于每个控件,明确地通知其他实例该控件的垂直滚动位置已更改。类似这样:

private void scrollSyncTxtBox1_vScroll(Message msg) {
    msg.HWnd = scrollSyncTxtBox2.Handle;
    scrollSyncTxtBox2.PubWndProc(ref msg);
}

如果所有“linked”控件没有相同数量的可显示行,则我认为此代码存在问题。


1
这种方法在通过移动滚动条进行滚动时运行良好。但是当使用以下方式进行滚动时,滚动条不会同步:(1) 使用鼠标滚轮滚动 (2) 使用“向下翻页”或“向上翻页”键滚动 - 通过按箭头键进行滚动 - Tun
@Jay riggs 'System.Windows.Forms.RichTextBox' 不包含名为 'PubWndProc' 的定义,也没有接受类型为 'System.Windows.Forms.RichTextBox' 的第一个参数的扩展方法 'PubWndProc' 可用(是否缺少使用指令或程序集引用?) - Pomster

5

[Visual Studio C# 2010 Express, v10.0.30319在Windows 7 64位安装中]

我使用了上面Donut发布的解决方案,但是发现当滚动包含许多行的RichTextBoxes到末尾时会出现问题。

如果GetScrollPos()的结果为>0x7FFF,那么当移动nPos时,最高位将被设置。然后使用生成的wParam变量创建IntPtr将导致OverflowException异常。您可以使用以下代码轻松测试这一点(第二行将失败):

    IntPtr ip = new IntPtr(0x7FFF0000);
    IntPtr ip2 = new IntPtr(0x80000000);

使用UIntPtrSendMessage()版本似乎是一个解决方案,但我无法使其工作。因此,我使用了以下方法:

    [DllImport("User32.dll")]
    public extern static int SendMessage(IntPtr hWnd, uint msg, UInt32 wParam, UInt32 lParam);

这应该可以覆盖到0xffff,但在那之后会失败。我还没有从GetScrollPos()中遇到过>0xffff的结果,并且认为User32.dll不太可能有64位版本的SendCommand(),但对于该问题的任何解决方案将不胜感激。

2
const int WM_USER = 0x400;

const int EM_GETSCROLLPOS = WM_USER + 221;

const int EM_SETSCROLLPOS = WM_USER + 222;

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int msg, int wParam, ref Point lParam);

private void RichTextBox1_VScroll(object sender, EventArgs e)
{
    Point pt;

    SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);

    SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}


private void RichTextBox2_VScroll(object sender, EventArgs e)
{
    Point pt;

    SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);

    SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}

1

Jay的子类方法的变体可以在Joseph Kingry的答案中找到,链接如下:Synchronizing Multiline Textbox Positions in C#

Joseph的方法也使用了子类,但不需要_VScroll事件处理程序。我使用这种方法在3个框之间进行了三向绑定,并添加了WM_HSCROLL。


0

@Sudhakar MuthuKrishnan的答案需要一些修正,但是有效。谢谢!

首先获取引发事件的GetScrollPos,然后为其他位置设置滚动位置。

private void RichTextBox1_VScroll(object sender, EventArgs e)
{
    Point pt = new Point();

    SendMessage(RichTextBox1.Handle, EM_GETSCROLLPOS, 0, ref pt);

    SendMessage(RichTextBox2.Handle, EM_SETSCROLLPOS, 0, ref pt);
}


private void RichTextBox2_VScroll(object sender, EventArgs e)
{
    Point pt = new Point();

    SendMessage(RichTextBox2.Handle, EM_GETSCROLLPOS, 0, ref pt);

    SendMessage(RichTextBox1.Handle, EM_SETSCROLLPOS, 0, ref pt);
}

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