一般的,我们做一个带行号的编辑控件,通常都使用RichTextBox。 网络上很多的都是 RichTextBox + Panel 来实现。 为 TextBox 实现带行号功能。在网上查了查,发现例子很少,通常都是考虑两个TextBox。好不容易找到一个,发现它实现的方法太不讲究... 为了让左边的TextBox显示行号,居然用循环内容行数来写行号... 有点郁闷的是,还专门写了两个方法: //根据行号确定光标索引 GetCurIndex(int curRow); //定位总行号 GetRNCount(String str) 难道不知TextBox也有 GetPositionFromCharIndex()、GetCharIndexFromPosition() 和Lines.Length 么... 程序一执行,少量行编辑还行,行数一多... 循环写行号,。。。 程序也没办法实现滚动条、鼠标滚动、鼠标选择内容上下移动等来重写行号。 考虑到仅是文本的TextBox 在编辑时不用考虑rtf格式问题,用TextBox实现的带行号控件还是有点可用性,我自己也写了一个 仅用TextBox 实现的带行号功能。当然需要调用Windows API来做。 大致思想是,用两个 TextBox 同步滚动,加上独立的ScrollBar来实现。 主要涉及 内容文本的 TextChanged事件、KeyDown(上下按键)、ValueChanged(Scroll滚动事件)、SizeChanged(文本框大小改变)等事件。 ![](http://image109.360doc.com/DownloadImg/2019/07/2021/166493970_1_20190720094741423.gif) 控件事件--------------- this.txtContent.MouseWheel += new MouseEventHandler(txtContect_MouseWheel); private int pageLine = 0; //当前文本框内容所能显示的行数 private bool isLeftDown = false; //鼠标左键是否点下 private void txtContect_TextChanged(object sender, EventArgs e) void txtContect_MouseWheel(object sender, MouseEventArgs e) private void txtContent_KeyDown(object sender, KeyEventArgs e) if (e.KeyData == System.Windows.Forms.Keys.Up || e.KeyData == System.Windows.Forms.Keys.Down) private void txtContent_KeyUp(object sender, KeyEventArgs e) if (e.KeyData == System.Windows.Forms.Keys.Up || e.KeyData == System.Windows.Forms.Keys.Down) private void vScrollBar1_ValueChanged(object sender, EventArgs e) int t = SetScrollPos(this.txtContent.Handle, 1, vScrollBar1.Value, true); SendMessage(this.txtContent.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * vScrollBar1.Value, 0); private void txtContent_MouseDown(object sender, MouseEventArgs e) if (e.Button == System.Windows.Forms.MouseButtons.Left) isLeftDown = true; private void txtContent_MouseMove(object sender, MouseEventArgs e) private void txtContent_MouseUp(object sender, MouseEventArgs e) private void txtContent_SizeChanged(object sender, EventArgs e) SCROLLINFO si = new SCROLLINFO(); si.cbSize = (uint)Marshal.SizeOf(si); int r = GetScrollInfo(this.txtContent.Handle, SB_VERT, ref si); pageLine = (int)si.nPage; private void txtRow_TextChanged(object sender, EventArgs e) if (this.txtRow.Lines.Length > 0) System.Drawing.SizeF s = this.txtRow.CreateGraphics().MeasureString(this.txtRow.Lines[this.txtRow.Lines.Length - 1], this.txtRow.Font); this.txtRow.Width = (int)s.Width; private void txtRow_SizeChanged(object sender, EventArgs e) this.txtContent.Location = new Point(this.txtRow.Width, this.txtContent.Location.Y); this.txtContent.Width = this.ClientSize.Width - this.txtRow.Width;
方法----------
private void ShowCursorLine() toolStripStatusLabel1.Text = "行: " + (this.txtContent.GetLineFromCharIndex(this.txtContent.SelectionStart) + 1); private void timer1_Tick(object sender, EventArgs e) private void SetScrollBar() SCROLLINFO si = new SCROLLINFO(); si.cbSize = (uint)Marshal.SizeOf(si); int r = GetScrollInfo(this.txtContent.Handle, SB_VERT, ref si); pageLine = (int)si.nPage; this.vScrollBar1.LargeChange = pageLine; this.vScrollBar1.Visible = true; this.vScrollBar1.Maximum = si.nMax; this.vScrollBar1.Value = si.nPos; this.vScrollBar1.Visible = false; int firstLine = txtContent.GetLineFromCharIndex(txtContent.GetCharIndexFromPosition(new Point(0, 2))); string[] lin = new string[pageLine]; for (int i = 0; i < pageLine; i++) lin[i] = (i + firstLine + 1).ToString();
调用 Windows API------------ public static uint SIF_RANGE = 0x0001; public static uint SIF_PAGE = 0x0002; public static uint SIF_POS = 0x0004; public static uint SIF_TRACKPOS = 0x0010; public static uint SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS); public int SB_THUMBPOSITION = 4; public int WM_VSCROLL = 0x0115; [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int GetScrollInfo(IntPtr hwnd, int bar, ref SCROLLINFO si); [DllImport("user32.dll")] private static extern int GetScrollPos(IntPtr hwnd, int nbar); [DllImport("user32.dll")] public static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool Rush); [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
重载 TextBox 的 KeyDown 事件------------------- protected override bool IsInputKey(System.Windows.Forms.Keys KeyData) if (KeyData == System.Windows.Forms.Keys.Up || KeyData == System.Windows.Forms.Keys.Down) return base.IsInputKey(KeyData);
基本上完整的实现了 > 绘制行号,包括点击滚动条、鼠标滚轮、上下按键、文本输入、鼠标选择内容上下移动、行显示宽度自适应。 但仍有个问题,就是TextBox 在GetCharIndexFromPosition() 时,只支持 65535字符.... 所以程序目前只支持最大文本65535字符 如果有更好的解决方法,望不吝赐教.
源代码下载 提取码: b1de598c-1c1b-4a5b-be7a-ab30ceb6cb4b
|