C#为RichTextBox控件添加行号

写过程序的人应该不会对RichTextBox感到陌生,与TextBox相比,它封装了更丰富的对象,使你的程序使用起来更加方便。但是它也有美中不足的地方。比如说没有行号,滚动条滚动进度无法控制等一系列的问题。

今天,就据我所熟悉的,向大家介绍介绍.希望达到抛砖引玉的效果,有不足的地方,还请大家积极指出来!

首先介绍如何为自己的RichTextBox增加行号。

新建一个用户控件类(UserControl),取名叫“AdaScrollRichTextBox ”继承自“System.Windows.Forms.RichTextBox ”。我们要给这个RichTextBox类加上行号,有一个比较巧妙的办法。那就是在这个类里再加上一个RichTextBox类。如下面这个方法:
         private RichTextBox   _LineId;
        private void InitializeLineId()
        {
            _LineId = new RichTextBox ();
            _LineId.Name = "LineId";
            //_LineId.Text = "1";
            _LineId.Width = 40;
            _LineId.Dock = DockStyle.Left;
            _LineId.Font = this.Font;
            _LineId.ForeColor = Color.Green;
            _LineId.ReadOnly = true;
            _LineId.BackColor = Color.White;
            _LineId.TabStop = false;
            _LineId.BorderStyle = BorderStyle.None;
            _LineId.ScrollBars = RichTextBoxScrollBars.None;
            _LineId.Cursor = Cursors.Arrow;
            this.SelectionIndent = 40;//由于左边行号占用了40象素,所以AdaScrollRichTextBox 需要向右边缩进40象素。
            this.Controls.Add(_LineId);//注意这里,将_LineId添加进AdaScrollRichTextBox Controls。
        }

在AdaScrollRichTextBox 的构造函数里调用这个方法,就可以实现行号了。

为了便于动态的绑定行号,最好是重写AdaScrollRichTextBox 的OnTextChanged方法。如下所示:
        protected override void OnTextChanged(EventArgs e)
        {
            int liLineCount = this.Lines.Length;
            for (int i = 1; I <= liLineCount; i++)
            {
                this._LineId.AppendText(i.ToString() + "\r\n");
            }
            base.OnTextChanged(e);
        }

这样,当你在为AdaScrollRichTextBox 赋值的时候,便会自动为你添加行号。值得注意的是,前面我们提了,RichTextBox的ScrollBar有问题,不能控制滚动条的滚动进度。所以,程序写到目前,在效果上还是有些问题的。细心的人就会发现,当拉动滚动条的时候,行号不能跟随着滚动条的滚动一起滚动,更加不要提鼠标的滚轮了!

我们新建一个类,他继承自RichTextBox,取名“adaLineIdTxt ”。
我们知道Windows事件处理都是消息机制,你触动键盘、拖动鼠标、拖动滚动条,都会触发消息。拖动滚动条?消息?对,拖动滚动条的消息就是“Message.Msg”里的“WM_HSCROLL = 0x0114”和“WM_VSCROLL = 0x0115”。只要我们捕获到这两个消息,就可以控制拖动滚动条的滚动了。

首先,我们定义一个委托
public delegate void SendMessage(Message poMsg);
光有委托不行,我们还得定义一个事件
public event SendMessage SendMessageEvent;

以上做好之后,我们还需要重载WndProc方法。
        protected override void WndProc(ref Message m)
        {
            if ( m.Msg == WM_VSCROLL)
            {
                if (SendMessageEvent != null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }

//如果拖动滚动条是垂直方向(因为这里只让行号滚动,所以只有垂直方向),就触发消息。
定义一个滚动方法。
        public void Scroll(Message poMsg)
        {
            if (this.Handle != null)
            {
                poMsg.HWnd = this.Handle;
                WndProc(ref poMsg);
            }
        }
        这样,我们的行号随滚动条滚动的功能就基本上实现了。下面是完整代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace AdaEniac
{
    public delegate void SendMessage(Message poMsg);
    public partial class adaLineIdTxt : RichTextBox
    {
        private int WM_HSCROLL = 0x0114;
        private int WM_VSCROLL = 0x0115;
        public event SendMessage SendMessageEvent;
        public adaLineIdTxt()
        {
            InitializeComponent();
        }
        protected override void WndProc(ref Message m)
        {
            if ( m.Msg == WM_VSCROLL)
            {
                if (SendMessageEvent != null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }
        public void Scroll(Message poMsg)
        {
            if (this.Handle != null)
            {
                poMsg.HWnd = this.Handle;
                WndProc(ref poMsg);
            }
        }
    }
}

需要说明的是,我前天写的“RichTextBox知识--为RichTextBox添加行号”需要做部分修改。将“_LineId”换成“adaLineIdTxt ” 类型。然后,还需要在“AdaScrollRichTextBox ”里增加一个方法
        public void LineIdScroll(Message poMsg)
        {
            if (poMsg.Msg == (int)wm.WM_VSCROLL)
            {
                _LineId.Scroll(poMsg);
            }
        }

在你使用该控件“AdaScrollRichTextBox ”的时候,需要调用其“SendMessageEvent”事件。在该事件里,可以让行号随滚动条滚动起来。

如例private void txtView_SendMessageEvent(Message poMsg)
        {
            this.txtView.LineIdScroll(poMsg);
        }

至此,我们为RichTextBox增加的行号已经可以随滚动条的滚动而滚动了。但是还是存在些问题,什么问题呢?

那就是滚轮滑动时,行号没滚动。

在  RichTextBox知识(二)里,我们为RichTextBox添加了随滚动条滚动的行号,但是滚轮滚动的时候,行号却没有跟随滚动!今天我们再让行号随滚轮滚动起来.具体代码见下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace AdaWinControls
{
    public partial class adScrollRichTextBox : System.Windows.Forms.RichTextBox
    {
        public event SendMessage SendMessageEvent;
        private bool _AutoLineId = true;
        public bool AutoLineId
        {
            set
            {
                _AutoLineId = value;
            }
            get
            {
                return _AutoLineId;
            }
        }
        private adLineIdTxt _LineId;
        public adLineIdTxt LineId
        {
            get
            {
                return _LineId;
            }
        }
        private bool _IsChecked = false;
        public bool IsChecked
        {
            get
            {
                return _IsChecked;
            }
            set
            {
                _IsChecked = value;
            }
        }
        public adScrollRichTextBox()
        {
            InitializeComponent();
            InitializeLineId();
        }
        private void InitializeLineId()
        {
            _LineId = new adLineIdTxt();
            _LineId.Name = "LineId";
            _LineId.Text = "1";
            _LineId.Width = 40;
            _LineId.Dock = DockStyle.Left;
            _LineId.Font = this.Font;
            _LineId.ForeColor = Color.Green;
            _LineId.ReadOnly = true;
            _LineId.BackColor = Color.White;
            _LineId.TabStop = false;
            _LineId.BorderStyle = BorderStyle.None;
            _LineId.ScrollBars = RichTextBoxScrollBars.None;
            _LineId.Cursor = Cursors.Arrow;
            this.SelectionIndent = 40;
            this.Controls.Add(_LineId);
        }
        protected override void WndProc(ref Message m)
        {
            if (m.Msg == (int)ApiVariable.WM_HSCROLL || m.Msg == (int)ApiVariable.WM_VSCROLL
                || m.Msg == (int)ApiVariable.WM_MOUSEWHEEL || m.Msg == (int)ApiVariable.EM_SCROLL
                || m.Msg == (int)ApiVariable.BM_CLICK)
            {
                if (SendMessageEvent != null)
                {
                    SendMessageEvent(m);
                }
            }
            base.WndProc(ref m);
        }
        public void Scroll(Message poMsg)
        {
            if (this.Handle != null)
            {
                poMsg.HWnd = this.Handle;
                WndProc(ref poMsg);
            }
        }
        public void LineIdScroll(Message poMsg)
        {
            if (poMsg.Msg == (int)ApiVariable.WM_VSCROLL)
            {
                _LineId.Scroll(poMsg);
            }
        }
        public void NewLine(string pcLineNo, string pcLine, Color poLineColor)
        {
            NewLine(pcLineNo, pcLine, poLineColor, Color.White);
        }
        public void NewLine(string pcLineNo, string pcLine, Color poLineColor, Color poBackColor)
        {
            this.SelectionIndent = 40;
            this.SelectionColor = poLineColor;
            this.SelectionBackColor = poBackColor;
            this.AppendText(pcLine + "\r\n");
            this._LineId.NewLineNo(pcLineNo);
        }
        public void SetValue(string pcAllLine)
        {
            this.SelectionIndent = 40;
            this.Text = pcAllLine;
            this._LineId.Clear();
            for (int i = 1; i <= this.Lines.Length; i++)
            {
                this._LineId.NewLineNo(i.ToString());
            }
        }
        protected override void OnSizeChanged(EventArgs e)
        {
            _LineId.Top--;
            base.OnSizeChanged(e);
        }
        protected override void OnTextChanged(EventArgs e)
        {
            this.SelectionIndent = 40;
            this.SelectionHangingIndent = 40;
            if (_AutoLineId)
            {
                this._LineId.Clear();
                for (int i = 0; i < this.Lines.Length; i++)
                {
                    this._LineId.NewLineNo(i.ToString());
                }
            }
            base.OnTextChanged(e);
        }
        #region RichTextBox_ScrollVariable
        private int _VPrePosition = 0;
        private int _HPrePosition = 0;
        // =================================================================== 
        // for NativeWindow and PostMessageA
        private const int WM_HSCROLL = 0x114;
        private const int WM_VSCROLL = 0x115;
        private const int WM_MOUSEWHEEL = 0x20A;
        private const int WM_COMMAND = 0x111;
        // =================================================================== 
        // for GetScroll and PostMessageA
        private const int WM_USER = 0x400;
        private const int SBS_HORZ = 0;
        private const int SBS_VERT = 1;
        // =================================================================== 
        // for SubClassing
        private const int SB_THUMBPOSITION = 4;
        // =================================================================== 
        // API Function: GetScrollPos()
        [DllImport("user32.dll")]
        private static extern int GetScrollPos(IntPtr hWnd, int nBar);
        // =================================================================== 
        // API Function: PostMessageA()
        [DllImport("user32.dll")]
        private static extern bool PostMessageA(IntPtr hwnd, int wMsg, int wParam, int lParam);
        #endregion
        protected override void OnVScroll(EventArgs e)
        {
            VerticalScrollNew(this, this._LineId);
            base.OnVScroll(e);
        }
        private void VerticalScrollNew(RichTextBox sender, RichTextBox receiver)
        {
            int liPosition = GetScrollPos(sender.Handle, SBS_VERT);
            if (liPosition != _VPrePosition)
            {
                this._VPrePosition = liPosition;
                PostMessageA(receiver.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * liPosition, 0);
            }
        }
    }
}

我们在保持前一篇截取滚动条消息的同时,引用Windows API.用它来控制行号跟随滚轮滚动.

这里需要重写"adScrollRichTextBox" 的 OnVScroll(EventArgs e)方法.有人问,为什么不连带重写OnHScroll方法呢?原因很简单,因为我们的行号只需要操作上下滚动,左右滚动就没必要了.除非你想玩BT点,让你的行好忽隐忽现.

至此,我们已经顺利的为RichTextBox添加上行号了!

我上传了资源,见地址:http://download.csdn.net/source/1078930

猜你喜欢

转载自blog.csdn.net/haojingus/article/details/11801101