自定义Windows窗体控件--CodeRichText
该控件可以作为一个简易的代码编辑器,可以实现代码的高亮显示,代码行号等。
CodeRichText.cs
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;
using HWND = System.IntPtr;
namespace CodeRichText
{
public partial class CodeRichText : UserControl
{
private string[] keywords ={ };
private string[] dividers ={ };
public CodeRichText()
{
InitializeComponent();
UpdateLineNo();
}
[DllImport("user32")]
private static extern int SendMessage(HWND hwnd, int wMsg, int wParam, IntPtr lParam);
private const int WM_SETREDRAW = 0xB;
public string[] KeyWords
{
get { return keywords; }
set
{
keywords = value;
ColorAllText();
}
}
public string[] Dividers
{
get { return dividers; }
set
{
dividers = value;
ColorAllText();
}
}
public string CodeText
{
get { return this.richTextBoxSourceCode.Text; }
set { this.richTextBoxSourceCode.Text = value; }
}
public Font CodeFont
{
get { return this.richTextBoxSourceCode.Font; }
set { this.richTextBoxSourceCode.Font = value; }
}
public void LoadFile(string path)
{
richTextBoxSourceCode.LoadFile(path,RichTextBoxStreamType.PlainText);
}
public void SaveFile(string path)
{
richTextBoxSourceCode.SaveFile(path,RichTextBoxStreamType.PlainText);
}
private void ColorAllText()
{
if (richTextBoxSourceCode.Text == string.Empty) return;
for (int i = 0; i < richTextBoxSourceCode.Lines.Length;i++ )
ColorCurrentText(i);
}
private void ColorCurrentText(int lineNum)
{
if (richTextBoxSourceCode.Text == string.Empty) return;
string lineStr = richTextBoxSourceCode.Lines[lineNum];
int selectStart = richTextBoxSourceCode.SelectionStart;
int lineStart=0;
for(int i=0;i<lineNum;i++)
lineStart+=richTextBoxSourceCode.Lines[i].Length+1;
SendMessage(richTextBoxSourceCode.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
richTextBoxSourceCode.SelectionStart = lineStart;
richTextBoxSourceCode.SelectionLength = lineStr.Length;
richTextBoxSourceCode.SelectionColor = Color.Black;
richTextBoxSourceCode.SelectionStart = selectStart;
richTextBoxSourceCode.SelectionLength = 0;
richTextBoxSourceCode.SelectionColor = Color.Black;
string[] words = lineStr.Split(dividers,StringSplitOptions.RemoveEmptyEntries);
int lineIndex = 0;
foreach (string word in words)
{
if (IsKeyWord(word))
{
lineIndex = lineStr.IndexOf(word, lineIndex);
richTextBoxSourceCode.SelectionStart = lineStart + lineIndex;
richTextBoxSourceCode.SelectionLength = word.Length;
richTextBoxSourceCode.SelectionColor = Color.Blue;
richTextBoxSourceCode.SelectionStart = selectStart;
richTextBoxSourceCode.SelectionLength = 0;
richTextBoxSourceCode.SelectionColor = Color.Black;
lineIndex += word.Length + 1;
}
else if (IsNumber(word))
{
lineIndex = lineStr.IndexOf(word, lineIndex);
richTextBoxSourceCode.SelectionStart = lineStart + lineIndex;
richTextBoxSourceCode.SelectionLength = word.Length;
richTextBoxSourceCode.SelectionColor = Color.Tomato;
richTextBoxSourceCode.SelectionStart = selectStart;
richTextBoxSourceCode.SelectionLength = 0;
richTextBoxSourceCode.SelectionColor = Color.Black;
lineIndex += word.Length + 1;
}
}
SendMessage(richTextBoxSourceCode.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
richTextBoxSourceCode.Refresh();
}
private bool IsKeyWord(string word)
{
foreach (string s in keywords)
{
if (string.Compare(word, s) == 0)
return true;
}
return false;
}
private bool IsNumber(string word)
{
foreach (char ch in word)
{
if (!(ch >= '0' && ch <= '9'))
return false;
}
return true;
}
private void richTextBoxSourceCode_TextChanged(object sender, EventArgs e)
{
if (richTextBoxSourceCode.Text == string.Empty) return;
int lineNum = richTextBoxSourceCode.GetLineFromCharIndex(richTextBoxSourceCode.SelectionStart);
UpdateLineNo();
ColorCurrentText(lineNum);
}
private void UpdateLineNo()
{
SendMessage(richTextBoxLineNo.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
//get index of first visible char and number of first visible line
Point pos = new Point(0, 0);
int firstIndex = richTextBoxSourceCode.GetCharIndexFromPosition(pos);
int firstLine = richTextBoxSourceCode.GetLineFromCharIndex(firstIndex);
//get index of last visible char and number of last visible line
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = richTextBoxSourceCode.GetCharIndexFromPosition(pos);
int lastLine = richTextBoxSourceCode.GetLineFromCharIndex(lastIndex);
//this is the point position of last visible char, use its Y value for calculating numberLabel size
pos = richTextBoxSourceCode.GetPositionFromCharIndex(lastIndex);
//finally, update line number
StringBuilder lineNo = new StringBuilder();
for (int i = firstLine; i <= lastLine + 1; i++)
{
lineNo.Append((i + 1).ToString() + "\n");
}
richTextBoxLineNo.Text = lineNo.ToString();
SendMessage(richTextBoxLineNo.Handle, WM_SETREDRAW, 1, IntPtr.Zero);
richTextBoxLineNo.Refresh();
}
private void CodeRichText_Scroll(object sender, ScrollEventArgs e)
{
}
private void richTextBoxSourceCode_VScroll(object sender, EventArgs e)
{
//move location of line number for amount of pixels caused by scrollbar
int d = richTextBoxSourceCode.GetPositionFromCharIndex(0).Y % (richTextBoxSourceCode.Font.Height + 1);
richTextBoxLineNo.Location = new Point(0, d);
UpdateLineNo();
}
private void richTextBoxLineNo_Enter(object sender, EventArgs e)
{
richTextBoxSourceCode.Focus();
}
private void richTextBoxSourceCode_FontChanged(object sender, EventArgs e)
{
richTextBoxLineNo.Font = new Font(richTextBoxSourceCode.Font, FontStyle.Regular);
}
}
}
CodeRichText.Designer.cs
namespace
CodeRichText
{
partial class CodeRichText
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null ;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing"> 如果应释放托管资源,为 true;否则为 false。 </param>
protected override void Dispose( bool disposing)
{
if (disposing && (components != null ))
{
components.Dispose();
}
base .Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this .richTextBoxLineNo = new System.Windows.Forms.RichTextBox();
this .richTextBoxSourceCode = new System.Windows.Forms.RichTextBox();
this .SuspendLayout();
//
// richTextBoxLineNo
//
this .richTextBoxLineNo.BackColor = System.Drawing.SystemColors.ControlLight;
this .richTextBoxLineNo.BorderStyle = System.Windows.Forms.BorderStyle.None;
this .richTextBoxLineNo.Cursor = System.Windows.Forms.Cursors.Arrow;
this .richTextBoxLineNo.Dock = System.Windows.Forms.DockStyle.Left;
this .richTextBoxLineNo.Font = new System.Drawing.Font( " Courier New " , 10F);
this .richTextBoxLineNo.ForeColor = System.Drawing.SystemColors.GradientActiveCaption;
this .richTextBoxLineNo.Location = new System.Drawing.Point( 0 , 0 );
this .richTextBoxLineNo.Name = " richTextBoxLineNo " ;
this .richTextBoxLineNo.ReadOnly = true ;
this .richTextBoxLineNo.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this .richTextBoxLineNo.Size = new System.Drawing.Size( 41 , 408 );
this .richTextBoxLineNo.TabIndex = 1 ;
this .richTextBoxLineNo.Text = "" ;
this .richTextBoxLineNo.Enter += new System.EventHandler( this .richTextBoxLineNo_Enter);
//
// richTextBoxSourceCode
//
this .richTextBoxSourceCode.BorderStyle = System.Windows.Forms.BorderStyle.None;
this .richTextBoxSourceCode.Dock = System.Windows.Forms.DockStyle.Fill;
this .richTextBoxSourceCode.Font = new System.Drawing.Font( " Courier New " , 10F);
this .richTextBoxSourceCode.Location = new System.Drawing.Point( 41 , 0 );
this .richTextBoxSourceCode.Name = " richTextBoxSourceCode " ;
this .richTextBoxSourceCode.Size = new System.Drawing.Size( 639 , 408 );
this .richTextBoxSourceCode.TabIndex = 0 ;
this .richTextBoxSourceCode.Text = "" ;
this .richTextBoxSourceCode.VScroll += new System.EventHandler( this .richTextBoxSourceCode_VScroll);
this .richTextBoxSourceCode.TextChanged += new System.EventHandler( this .richTextBoxSourceCode_TextChanged);
//
// CodeRichText
//
this .AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this .AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this .Controls.Add( this .richTextBoxSourceCode);
this .Controls.Add( this .richTextBoxLineNo);
this .Name = " CodeRichText " ;
this .Size = new System.Drawing.Size( 680 , 408 );
this .Scroll += new System.Windows.Forms.ScrollEventHandler( this .CodeRichText_Scroll);
this .ResumeLayout( false );
}
#endregion
private System.Windows.Forms.RichTextBox richTextBoxLineNo;
private System.Windows.Forms.RichTextBox richTextBoxSourceCode;
}
}
{
partial class CodeRichText
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null ;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing"> 如果应释放托管资源,为 true;否则为 false。 </param>
protected override void Dispose( bool disposing)
{
if (disposing && (components != null ))
{
components.Dispose();
}
base .Dispose(disposing);
}
#region 组件设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this .richTextBoxLineNo = new System.Windows.Forms.RichTextBox();
this .richTextBoxSourceCode = new System.Windows.Forms.RichTextBox();
this .SuspendLayout();
//
// richTextBoxLineNo
//
this .richTextBoxLineNo.BackColor = System.Drawing.SystemColors.ControlLight;
this .richTextBoxLineNo.BorderStyle = System.Windows.Forms.BorderStyle.None;
this .richTextBoxLineNo.Cursor = System.Windows.Forms.Cursors.Arrow;
this .richTextBoxLineNo.Dock = System.Windows.Forms.DockStyle.Left;
this .richTextBoxLineNo.Font = new System.Drawing.Font( " Courier New " , 10F);
this .richTextBoxLineNo.ForeColor = System.Drawing.SystemColors.GradientActiveCaption;
this .richTextBoxLineNo.Location = new System.Drawing.Point( 0 , 0 );
this .richTextBoxLineNo.Name = " richTextBoxLineNo " ;
this .richTextBoxLineNo.ReadOnly = true ;
this .richTextBoxLineNo.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None;
this .richTextBoxLineNo.Size = new System.Drawing.Size( 41 , 408 );
this .richTextBoxLineNo.TabIndex = 1 ;
this .richTextBoxLineNo.Text = "" ;
this .richTextBoxLineNo.Enter += new System.EventHandler( this .richTextBoxLineNo_Enter);
//
// richTextBoxSourceCode
//
this .richTextBoxSourceCode.BorderStyle = System.Windows.Forms.BorderStyle.None;
this .richTextBoxSourceCode.Dock = System.Windows.Forms.DockStyle.Fill;
this .richTextBoxSourceCode.Font = new System.Drawing.Font( " Courier New " , 10F);
this .richTextBoxSourceCode.Location = new System.Drawing.Point( 41 , 0 );
this .richTextBoxSourceCode.Name = " richTextBoxSourceCode " ;
this .richTextBoxSourceCode.Size = new System.Drawing.Size( 639 , 408 );
this .richTextBoxSourceCode.TabIndex = 0 ;
this .richTextBoxSourceCode.Text = "" ;
this .richTextBoxSourceCode.VScroll += new System.EventHandler( this .richTextBoxSourceCode_VScroll);
this .richTextBoxSourceCode.TextChanged += new System.EventHandler( this .richTextBoxSourceCode_TextChanged);
//
// CodeRichText
//
this .AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this .AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this .Controls.Add( this .richTextBoxSourceCode);
this .Controls.Add( this .richTextBoxLineNo);
this .Name = " CodeRichText " ;
this .Size = new System.Drawing.Size( 680 , 408 );
this .Scroll += new System.Windows.Forms.ScrollEventHandler( this .CodeRichText_Scroll);
this .ResumeLayout( false );
}
#endregion
private System.Windows.Forms.RichTextBox richTextBoxLineNo;
private System.Windows.Forms.RichTextBox richTextBoxSourceCode;
}
}
现在程序还有bug: 假设“wang”是关键字,某一行的内容为:hellowang wang,则本应该在第二个“wnag”上高亮显示,但是结果在“hellowang”中的“wang”上高亮显示了。
运行后界面如下: