[导入]两个RichTextBox同步滚动的实现

最近想做一个比较两段文本的东西,将文本放入RichTextBox,想拖动其中一个的滚动条,然后另外一个也就可以滚动的效果。
遵循我的一贯原则,先从RichTextBox本身的特性上找,从滚动事件上看,事件处理参数中居然没有滚动位置参数,RichTextBox也没有当前行的说法;下一步从属性上找,没有合适的;下一步找方法,找到一个有点象的: ScrollToCaret(),MSDN的说法是:将控件的内容滚动到当前插入符号位置,可是我根本就没有当前插入符号的位置,也没法得到这一点;最后找受保护的方法,还好有一个WndProc,这个方法的解释为:“处理 Windows 消息”,天哪,这回掉到大坑中了,整个windows体系就是消息来消息去的,这回我们可要从中间拦截一道了。
先找到滚动消息的消息类型编号,懒一点,自己做个类,继承自RichTextBox,重写WndProc方法,只不过多写一句话而已,Console.WriteLine(e.ToString()),当然了,这个类要在测试工程中用一下,避免太多的消息干扰,在界面上做一个按钮一个普通TextBox,按钮的功能为将TextBox中的内容添加到自定义的RichTextBox中,每个动作都会激发WndProc方法,从一大堆的结果中找到自己关心的内容。
以下为自定义RichTextBox
    public class MyRichTextBox:RichTextBox
    {
        protected override void WndProc(ref Message m)
        {
            Console.WriteLine(m.ToString());
            base.WndProc(ref m);
        }
    }
以下为测试代码:

        MyRichTextBox rtb = new MyRichTextBox();
        private void frmMain_Load(object sender, EventArgs e)
        {
            this.Controls.Add(rtb);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            this.rtb.Text = this.textBox1.Text;
        }

    测试后的打印的内容:

msg=0x115 (WM_VSCROLL) hwnd=0x10b34 wparam=0x1 lparam=0x0 result=0x0
msg=0x204e (WM_REFLECT + WM_NOTIFY) hwnd=0x10b34 wparam=0x10b34 lparam=0x517bf48 result=0x0
msg=0x2111 (WM_REFLECT + WM_COMMAND) hwnd=0x10b34 wparam=0x6020b34 lparam=0x10b34 result=0x0
msg=0xf (WM_PAINT) hwnd=0x10b34 wparam=0x0 lparam=0x0 result=0x0
msg=0x2111 (WM_REFLECT + WM_COMMAND) hwnd=0x10b34 wparam=0x4000b34 lparam=0x10b34 result=0x0
msg=0xb8 hwnd=0x10b34 wparam=0x0 lparam=0x0 result=0x0
msg=0x215 (WM_CAPTURECHANGED) hwnd=0x10b34 wparam=0x0 lparam=0x0 result=0x0
msg=0x115 (WM_VSCROLL) hwnd=0x10b34 wparam=0x8 lparam=0x0 result=0x0
msg=0x204e (WM_REFLECT + WM_NOTIFY) hwnd=0x10b34 wparam=0x10b34 lparam=0x517bf48 result=0x0
msg=0x84 (WM_NCHITTEST) hwnd=0x10b34 wparam=0x0 lparam=0xc600b7 result=0x0
msg=0x84 (WM_NCHITTEST) hwnd=0x10b34 wparam=0x0 lparam=0xc600b7 result=0x0
msg=0x20 (WM_SETCURSOR) hwnd=0x10b34 wparam=0x10b34 lparam=0x2000007 result=0x0
msg=0xa0 (WM_NCMOUSEMOVE) hwnd=0x10b34 wparam=0x7 lparam=0xc600b7 result=0x0
msg=0x84 (WM_NCHITTEST) hwnd=0x10b34 wparam=0x0 lparam=0xc600b8 result=0x0
msg=0x84 (WM_NCHITTEST) hwnd=0x10b34 wparam=0x0 lparam=0xc600b8 result=0x0

 

    从一大堆结果中找自己感兴趣的,啊哈,有了,

msg=0x115 (WM_VSCROLL) hwnd=0x10b34 wparam=0x8 lparam=0x0 result=0x0

这就是我想要的内容,大力水手与你猜猜猜开始了,

    什么是msg=0x115,每行的msg都不一样,当然了windows消息不一样,那么msg就不一样。

    那么hwnd是什么?windows程序接触多了,是个傻子也知道这是句柄,也可以认为是指针,谁的指针,当然是你操作的对象的指针,那么是不是用这个家伙就可以代表着那个对象,啊哈,答对了。

    wparam呢?lparam呢?不好猜了吧,我也不知道,w和l不知道并不意味这咱们啥都不知道,param这知道,参数嘛。

    下面我们来解决这个问题:

    从面向对象的角度上看,如果我们将对象的地址或指针换了,那么他所操作的应该是其他的对象。

   着手的地方应该是WndProc方法,这个方法有一个强制引用参数Message,既然是强制引用,那么这小子很有可能是个值类型的玩意,但对我们没什么影响。我们的目标是RichText1滚到哪里,RichTextBox2也滚到哪里,这样,我们就只换我们操作的对象就可以了。

    现在要考虑的是将Message怎么传递给另外的RichTextBox,并且能够实现功能。在windows体系本身来说用的就是事件消息机制,那我们为什么不能再画一个瓢呢?啊哈,.Net本身就有一个委托机制。

    先做一个委托吧。

    public delegate void SendMessage(Message msg);

    下来在MyRichTextBox中做一个事件SendMessageEvent。

    public event SendMessage SendMessageEvent;

    当WndProc执行的时候激发这个事件。

        public event SendMessage SendMessageEvent;
        protected override void WndProc(ref Message m)
        {
            SendMessageEvent(m);
            base.WndProc(ref m);
        }

    但是这个消息要能让另一个RichTextBox滚动也需要执行WndProc,但是WndProc为受保护方法,没法子,只好做了一个公开的方法,

        public void Scroll(Message m)
        {
            m.HWnd = this.Handle;
            WndProc(ref m);
        }

    在使用的时候,用以下方式:

        MyRichTextBox c1 = new MyRichTextBox();
        MyRichTextBox c2 = new MyRichTextBox();
        private void Form1_Load(object sender, EventArgs e)
        {
            c1.SendMessageEvent += new SendMessage(c1_SendMessageEvent);
            this.Controls.Add(c1);
            this.Controls.Add(c2);
        }

        void c1_SendMessageEvent(Message msg)
        {
            c2.Scroll(msg);
        }

    c1为源RichTextBox,c2为被动RichTextBox,c1滚动,c2也跟着滚动。

    其实大家看到这儿,怎么回事,c2的滚动还是要调用c2的Scroll方法,这算那门子事啊,大力水手曰:世道艰难,求人不如求己,但是c2的发财,是由于c1的消息,所以大力水手曰:信息是第一生产力。

    是否就此天下太平了?四海欢腾了?九州高歌了?

    回顾我们的目标,我们要两个RichTextBox同步滚动,的确实现了,但是如果去掉:c1.SendMessageEvent += new SendMessage(c1_SendMessageEvent);将会引发NullReferenceException?

    原因是这个事件没有被一个合适的方法所订阅,你就想调用这个方法,解决之道也简单,加一个简单的判空即可:

                    if (SendMessageEvent!=null)
                {
                    SendMessageEvent(m);
                }

    够了吗,够了,天下太平了?四海欢腾了?九州高歌了?

    没有,这回是多了,怎么回事?无论在c1上做什么操作,在c2上就有什么结果,我只想要同步滚动而已,太多了,应该只拦截那些滚动的方法。

            if (m.Msg==0x115)
            {
                if (SendMessageEvent!=null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);

    0x115为滚动,但是...,但是...,嗯,总有点不太对劲的地方,做一下全面测试,啊,找到了,刚才看到Scroll就以为找到了滚动,将谁忽略了?!哪有啊,肯定有!哦,明白了那个字母“V”,我以为那是一个修饰符而已,看来还是很有用处的,什么用处啊?到底是啥啊,你想急死我啊,嘿,还就是,谁让你不好好学习了。那是vertical,垂直啊,我说嘛,加上了这个横向滚动咋就不行了,看来还得再来一个咯,还得把横向滚动的消息放过,那么找吧,又找到了一个HScroll就是它了。

    再改改if (m.Msg==0x115 || m.Msg==0x114)。

    还不太美,毕竟不能太违反八荣八耻:将0x114,0x115改为两个常数。

    private const int WM_HSCROLL = 0x0114;

    private const int WM_VSCROLL = 0x0115;

    天下太平了?四海欢腾了?九州高歌了?

    因为这两个常量是同一类型,处于同一个地位,因而,最好将他们做为一个常量组,.Net体系中有常量组吗?有啊,改名了,叫枚举。

    public enum WindowsMessage
    {
        WM_HSCROLL = 0x0114,
        WM_VSCROLL = 0x0115
    }

    因而final version应该是:

    public enum WindowsMessage
    {
        WM_HSCROLL = 0x0114,
        WM_VSCROLL = 0x0115
    }
    public delegate void SendMessage(Message msg);
    public class MyRichTextBox:System.Windows.Forms.RichTextBox
    {
        public event SendMessage SendMessageEvent;
        protected override void WndProc(ref Message m)
        {
            if (m.Msg==WindowsMessage.WM_HSCROLL || m.Msg==WindowsMessage.WM_VSCROLL)
            {
                if (SendMessageEvent!=null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }
        public void Scroll(Message m)
        {
            m.HWnd = this.Handle;
            WndProc(ref m);
        }
    }

    这样就功德圆满了,收工。


文章来源: http://blog.sina.com.cn/s/blog_49458c2701007woh.html

转载于:https://www.cnblogs.com/dalishuishou/archive/2008/05/01/1178414.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值