C#:同步两个RichTextBox的滚动位置?

时间:2009-12-01 16:29:33

标签: c# .net winforms scroll richtextbox

在我的应用程序表单中,我有两个RichTextBox个对象。它们将始终具有相同数量的文本行。我想“同步”这两者之间的垂直滚动,这样当用户在一个上改变垂直滚动位置时,另一个滚动相同的量。我该怎么做呢?

6 个答案:

答案 0 :(得分:20)

感谢Jay的回答;经过一些搜索后,我还发现了here所描述的方法。对于其他感兴趣的人,我将在下面概述。


首先,声明以下枚举:

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

最后,为相应VScrollRichTextBox事件添加事件处理程序:

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同步。

答案 1 :(得分:15)

我之前为一个小项目做过这个,这是我发现的简单解决方案。

通过继承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);
        }
    }     

将新控件添加到表单中,并为每个控件显式通知控件的其他实例其vScroll位置已更改。像这样的事情:

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

我认为如果所有“链接”控件没有相同数量的可显示行,则此代码会出现问题。

答案 2 :(得分:4)

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

我使用过上面发布的Donut的解决方案,但在滚动到包含许多行的RichTextBox的末尾时发现了一个问题。

如果GetScrollPos()的结果为>0x7FFF,那么当移位nPos时,会设置最高位。然后,使用IntPtr变量创建wParam将失败并显示OverflowException。您可以使用以下内容轻松测试(第二行将失败):

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

使用SendMessage()的{​​{1}}版本似乎是一种解决方案,但我无法让它发挥作用。所以,我使用以下内容:

UIntPtr

这应该是 [DllImport("User32.dll")] public extern static int SendMessage(IntPtr hWnd, uint msg, UInt32 wParam, UInt32 lParam); ,但在此之后会失败。我还没有遇到来自0xffff的{​​{1}}结果,并且假设User32.dll不太可能有>0xffff的64位版本,但是对此问题的任何解决方案都将非常感激

答案 3 :(得分:1)

Jay的子类方法的变体可以在Joseph Kingry的帖子中找到:Synchronizing Multiline Textbox Positions in C#。 Joseph的方法也是子类,但不需要_VScroll事件处理程序。我使用这种方法在3个盒子之间进行3向绑定并添加了WM_HSCROLL。

答案 4 :(得分:1)

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

答案 5 :(得分: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);
}