分享

C# TextBox 同步滚动 实现行号

 羊玉wngbx 2019-07-20
    一般的,我们做一个带行号的编辑控件,通常都使用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(文本框大小改变)等事件。

 

控件事件---------------

  1. public Example()
  2. {
  3. InitializeComponent();
  4. this.txtContent.MouseWheel += new MouseEventHandler(txtContect_MouseWheel);
  5. }
  6. private int pageLine = 0; //当前文本框内容所能显示的行数
  7. private bool isLeftDown = false; //鼠标左键是否点下
  8. private void txtContect_TextChanged(object sender, EventArgs e)
  9. {
  10. //调用顺序不可变
  11. SetScrollBar();
  12. ShowRow();
  13. ShowCursorLine();
  14. }
  15. //鼠标滚动
  16. void txtContect_MouseWheel(object sender, MouseEventArgs e)
  17. {
  18. timer1.Enabled = true;
  19. }
  20. // 上、下键
  21. private void txtContent_KeyDown(object sender, KeyEventArgs e)
  22. {
  23. if (e.KeyData == System.Windows.Forms.Keys.Up || e.KeyData == System.Windows.Forms.Keys.Down)
  24. SetScrollBar();
  25. }
  26. private void txtContent_KeyUp(object sender, KeyEventArgs e)
  27. {
  28. if (e.KeyData == System.Windows.Forms.Keys.Up || e.KeyData == System.Windows.Forms.Keys.Down)
  29. ShowCursorLine();
  30. }
  31. //点击滚动条
  32. private void vScrollBar1_ValueChanged(object sender, EventArgs e)
  33. {
  34. int t = SetScrollPos(this.txtContent.Handle, 1, vScrollBar1.Value, true);
  35. SendMessage(this.txtContent.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * vScrollBar1.Value, 0);
  36. ShowRow();
  37. }
  38. //显示光标行
  39. private void txtContent_MouseDown(object sender, MouseEventArgs e)
  40. {
  41. if (e.Button == System.Windows.Forms.MouseButtons.Left) isLeftDown = true;
  42. ShowCursorLine();
  43. }
  44. //鼠标选择内容上下移动
  45. private void txtContent_MouseMove(object sender, MouseEventArgs e)
  46. {
  47. SetScrollBar();
  48. }
  49. private void txtContent_MouseUp(object sender, MouseEventArgs e)
  50. {
  51. isLeftDown = false;
  52. }
  53. //文本框大小改变
  54. private void txtContent_SizeChanged(object sender, EventArgs e)
  55. {
  56. SCROLLINFO si = new SCROLLINFO();
  57. si.cbSize = (uint)Marshal.SizeOf(si);
  58. si.fMask = SIF_ALL;
  59. int r = GetScrollInfo(this.txtContent.Handle, SB_VERT, ref si);
  60. pageLine = (int)si.nPage;
  61. timer1.Enabled = true;
  62. ShowRow();
  63. }
  64. //行显示栏宽度自适应
  65. private void txtRow_TextChanged(object sender, EventArgs e)
  66. {
  67. if (this.txtRow.Lines.Length > 0)
  68. {
  69. System.Drawing.SizeF s = this.txtRow.CreateGraphics().MeasureString(this.txtRow.Lines[this.txtRow.Lines.Length - 1], this.txtRow.Font);
  70. this.txtRow.Width = (int)s.Width;
  71. }
  72. }
  73. private void txtRow_SizeChanged(object sender, EventArgs e)
  74. {
  75. this.txtContent.Location = new Point(this.txtRow.Width, this.txtContent.Location.Y);
  76. this.txtContent.Width = this.ClientSize.Width - this.txtRow.Width;
  77. }
  78. //


 
方法---------- 

  1. #region Method
  2. private void ShowCursorLine()
  3. {
  4. toolStripStatusLabel1.Text = "行: " + (this.txtContent.GetLineFromCharIndex(this.txtContent.SelectionStart) + 1);
  5. }
  6. private void timer1_Tick(object sender, EventArgs e)
  7. {
  8. SetScrollBar();
  9. timer1.Enabled = false;
  10. }
  11. private void SetScrollBar()
  12. {
  13. SCROLLINFO si = new SCROLLINFO();
  14. si.cbSize = (uint)Marshal.SizeOf(si);
  15. si.fMask = SIF_ALL;
  16. int r = GetScrollInfo(this.txtContent.Handle, SB_VERT, ref si);
  17. pageLine = (int)si.nPage;
  18. this.vScrollBar1.LargeChange = pageLine;
  19. if (si.nMax >= si.nPage)
  20. {
  21. this.vScrollBar1.Visible = true;
  22. this.vScrollBar1.Maximum = si.nMax;
  23. this.vScrollBar1.Value = si.nPos;
  24. }
  25. else
  26. this.vScrollBar1.Visible = false;
  27. }
  28. private void ShowRow()
  29. {
  30. int firstLine = txtContent.GetLineFromCharIndex(txtContent.GetCharIndexFromPosition(new Point(0, 2)));
  31. string[] lin = new string[pageLine];
  32. for (int i = 0; i < pageLine; i++)
  33. {
  34. lin[i] = (i + firstLine + 1).ToString();
  35. }
  36. txtRow.Lines = lin;
  37. }
  38. #endregion

调用 Windows API------------ 

  1. #region 调用 API
  2. public static uint SIF_RANGE = 0x0001;
  3. public static uint SIF_PAGE = 0x0002;
  4. public static uint SIF_POS = 0x0004;
  5. public static uint SIF_TRACKPOS = 0x0010;
  6. public static uint SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS);
  7. public int SB_THUMBPOSITION = 4;
  8. public int SB_VERT = 1;
  9. public int WM_VSCROLL = 0x0115;
  10. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  11. public struct SCROLLINFO
  12. {
  13. public uint cbSize;
  14. public uint fMask;
  15. public int nMin;
  16. public int nMax;
  17. public uint nPage;
  18. public int nPos;
  19. public int nTrackPos;
  20. }
  21. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  22. public static extern int GetScrollInfo(IntPtr hwnd, int bar, ref SCROLLINFO si);
  23. [DllImport("user32.dll")]
  24. private static extern int GetScrollPos(IntPtr hwnd, int nbar);
  25. [DllImport("user32.dll")]
  26. public static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool Rush);
  27. [DllImport("user32.dll", CharSet = CharSet.Auto)]
  28. public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
  29. #endregion

重载 TextBox 的 KeyDown 事件-------------------

  1. protected override bool IsInputKey(System.Windows.Forms.Keys KeyData)
  2. {
  3. if (KeyData == System.Windows.Forms.Keys.Up || KeyData == System.Windows.Forms.Keys.Down)
  4. return true;
  5. return base.IsInputKey(KeyData);
  6. }
  7. //

基本上完整的实现了 > 绘制行号,包括点击滚动条、鼠标滚轮、上下按键、文本输入、鼠标选择内容上下移动、行显示宽度自适应

但仍有个问题,就是TextBoxGetCharIndexFromPosition() 时,只支持 65535字符....  所以程序目前只支持最大文本65535字符

如果有更好的解决方法,望不吝赐教.

源代码下载
提取码: b1de598c-1c1b-4a5b-be7a-ab30ceb6cb4b

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多