摘要:利用 Regular Expression 為 RichTextBox 加上 syn利用 Regular Expression 為 RichTextBox 加上 syntax highlighting 功能tax highlighting 功能
最近在設計的應用程式,需要一個有 syntax highlighting 功能的 RichTextBox。後來在 Code Project 上面找到一個陽春的 RichTextBox 元件,這個元件可以針對字串、整數...等各種語法文字顯示自訂的顏色。不過我的需求更陽春,我只需要把類似 XML 的標籤用特殊顏色顯示就行了,像這樣:
因 此自己動手修改了一下,把程式碼簡化,並將類別名稱改為 RichTextBoxHL,再加個小圖示,以便安裝到 VS2005 的 Toolbox 時有個比較像樣的圖案。原始程式碼附在最後。這當中為了尋找成對的起始標籤和結束標籤,又 Goggle 了一下,看看有沒有現成的 regular expression,結果不只找到了:"<(?<tag>\\w*)>(?<text>.*)</\\k<tag>>",還發現另一個好用的 Regular Expression 的編輯與測試工具:Expresso。雖然早知道 regular expression 強大好用,但怎麼也耐不住性子去學那一大串的語法符號。有了這個工具,倒是增加不少學習和使用上的方便。另外,RegExpLib.com 也是可以去尋寶的地方。
RichTextBoxHL 原始程式碼:
using System; using System.ComponentModel; using System.Collections.Generic; using System.IO; using System.Text; using System.Drawing; using System.Windows.Forms; using System.Text.RegularExpressions; using Huanlin.Helpers;
namespace Huanlin.WinForms
{
/// <summary>
/// 能夠將標籤以特殊顏色顯示的 RichTextBox。
/// 此元件參考自 Patrik Svensson 的文章:Enabling syntax highlighting in a RichTextBox。
/// URL: http://www.codeproject.com/cs/miscctrl/SyntaxRichTextBox.asp。
/// </summary>
[ToolboxBitmap(typeof(RichTextBoxHL), "RichTextBoxHL.bmp")]
public class RichTextBoxHL : RichTextBox
{
private bool m_SkipTextChanged;
private bool m_EnableUpdate;
private string m_Line;
private int m_ContentLength;
private int m_LineStartIndex;
private int m_LineEndIndex;
private int m_LineLength;
private int m_CurrentSelection;private bool m_EnableTagColor; private Color m_TagColor;
public RichTextBoxHL()
: base()
{
m_EnableUpdate = true;
m_SkipTextChanged = false;m_EnableTagColor = true; m_TagColor = Color.Maroon; }
public new void LoadFile(string path)
{
m_SkipTextChanged = true;
try
{
base.LoadFile(path);
ProcessAllLines();
}
finally
{
m_SkipTextChanged = false;
}
}
public new void LoadFile(string path, RichTextBoxStreamType fileType)
{
m_SkipTextChanged = true;
try
{
base.LoadFile(path, fileType);
ProcessAllLines();
}
finally
{
m_SkipTextChanged = false;
}
}
public new void LoadFile(Stream data, RichTextBoxStreamType fileType)
{
m_SkipTextChanged = true;
try
{
base.LoadFile(data, fileType);
ProcessAllLines();
}
finally
{
m_SkipTextChanged = false;
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x00f)
{
if (m_EnableUpdate)
base.WndProc(ref m);
else
m.Result = IntPtr.Zero;
}
else
base.WndProc(ref m);
}
protected override void OnTextChanged(EventArgs e)
{
if (m_SkipTextChanged)
return;
m_EnableUpdate = false;
try
{
// Calculate sh*t here.
m_ContentLength = this.TextLength;int currSelectionStart = SelectionStart; int currSelectionLength = SelectionLength;
// Find the start of the current line. m_LineStartIndex = currSelectionStart; while ((m_LineStartIndex > 0) && (Text[m_LineStartIndex - 1] != '\n')) m_LineStartIndex--;
// Find the end of the current line. m_LineEndIndex = currSelectionStart; while ((m_LineEndIndex < Text.Length) && (Text[m_LineEndIndex] != '\n')) m_LineEndIndex++;
// Calculate the length of the line. m_LineLength = m_LineEndIndex - m_LineStartIndex;
// Get the current line. m_Line = Text.Substring(m_LineStartIndex, m_LineLength);
ProcessLine();
}
finally
{
m_EnableUpdate = true;
}
}
private void ProcessLine()
{
// Save the position and make the whole line black
int pos = base.SelectionStart;
base.SelectionStart = m_LineStartIndex;
base.SelectionLength = m_Line.Length;
base.SelectionColor = Color.Black;ProcessTagColor();
base.SelectionStart = pos; base.SelectionLength = 0; base.SelectionColor = Color.Black;
m_CurrentSelection = pos; }
private void ProcessTagColor()
{
if (!m_EnableTagColor)
return;MatchCollection matches = StrHelper.FindTagPairs(m_Line);
int start; int end;
foreach (Match match in matches)
{
// 每個找到的 Match 物件都包含一對標籤: <xxx>abc</xxx>
// 起始標籤
start = match.Index;
end = m_Line.IndexOf('>', start + 1);
base.SelectionStart = m_LineStartIndex + start;
base.SelectionLength = end - start + 1;
base.SelectionColor = m_TagColor;
// 結束標籤
start = m_Line.IndexOf('<', end + 1);
end = m_Line.IndexOf('>', start + 1);
base.SelectionStart = m_LineStartIndex + start;
base.SelectionLength = end - start + 1;
base.SelectionColor = m_TagColor;
}
}
private void ProcessAllLines()
{
m_Line = base.Text;
m_LineStartIndex = 0;
m_LineEndIndex = m_LineStartIndex + m_Line.Length;ProcessLine(); }
#region 屬性
public new string Text
{
get
{
return base.Text;
}
set
{
m_SkipTextChanged = true;
try
{
base.Text = value;
ProcessAllLines();
}
finally
{
m_SkipTextChanged = false;
}
}
}
public new string[] Lines
{
get
{
return base.Lines;
}
set
{
m_SkipTextChanged = true;
try
{
base.Lines = value;
ProcessAllLines();
}
finally
{
m_SkipTextChanged = false;
}
}
}
[Browsable(true), Description("是否將標籤顯示成特殊顏色。")]
public bool EnableTagColor
{
get
{
return m_EnableTagColor;
}
set
{
if (m_EnableTagColor != value)
{
m_EnableTagColor = value;
ProcessAllLines();
}
}
}
[Browsable(true), Description("標籤顏色。")]
public Color TagColor
{
get
{
return m_TagColor;
}
set
{
if (m_TagColor != value)
{
m_TagColor = value;
ProcessAllLines();
}
}
}#endregion } }
其中呼叫到的 StrHelper.FindTagPairs() 程式碼為:
public static MatchCollection FindTagPairs(string s)
{
string pattern = "<(?<tag>\\w*)>(?<text>.*)</\\k<tag>>";
return Regex.Matches(s, pattern);
}
如果需要為讓關鍵字或語法顯示特殊顏色,只要修改 ProcessLine(),利用 regular expression 把找到的字串"上色"就行了(就像上面的 ProcessTagColor 方法一樣)。