前言
很多IDE或者开发工具中都有语法着色的功能,这是如何实现的呢?笔者试着用C#做了一个Sample,基本上实现此功能。
本文一半是原创,一半是参考国外的论坛
思路
语法着色器,实际上只做了两件事情:“接受用户输入”以及“改变关键字字体属性”。
1)首先看第一个:接受用户输入。
C#自带的控件中能够满足需求的首推“RichTextBox”,笔者在网上经常看到很多同行都以此为基础,制作出类似MS-Word的种种效果。既然,复杂的Word效果都能够实现,那么语法着色自然也就不在话下。
2)其次,最重要的“改变关键字字体属性”。
这一点还可分成三个更小的工作:关键字列表、判断关键字、定义字体属性。
关键字列表很简单,一个List即可满足需求。
这里以基本SQL语句为例。
private IList<String> _KeyWords = new List<String>()
{
"SELECT", "UPDATE", "DELETE", "INSERT", "DROP", "FROM", "WHERE", "ALERT", "CREATE", "TABLE",
"VALUES", "NULL",
"AND", "OR", "NOT", "IN", "INTO", "UNION", "AS", "IS",
"COUNT", "MAX", "MIN", "AVG",
"WAIT", "NOWAIT", "WITHOUT", "LOCK", "GROUP", "ORDER", "BY",
"JOIN", "LEFT", "RIGHT", "CASE", "WHEN", "ELSE", "END",
"COMMIT", "ROLLBACK"
};
判断关键字
为了在用户输入时能够立即对所输入的内容有所反映,我们需要override一下RichTextBox的OnTextChanged()方法。
其他属性及方法介绍请参考MSDN
笔者的算法很简单,遍历业已定义好的关键字列表,用其与用户输入的内容进行比较,如果发现相同,则按照字符的index值计算编辑区域。
定义字体属性
在C#中有一个叫做FONT的类能够满足需求。 其他属性及方法介绍请参考MSDN
注意事项
笔者在尝试的过程中发现屏幕中显示的内容会随着用户的输入,出现闪烁。究其原因可能是因为Windows一直在重新绘制窗口造成的,为了回避这个问题,我们可以使用SuspendLayout()和ResumeLayout()暂停和恢复画面重新绘制的过程。
做完这几步,我们的语法着色控件也就大功告成了。下面是全部源代码。在VS2008 Express版本上调试通过。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Drawing;
using System.ComponentModel;
namespace Base
{
public partial class SQLEditor : RichTextBox
{
/// <summary>
/// 关键字列表
/// </summary>
private IList<String> _KeyWords = new List<String>()
{
"SELECT", "UPDATE", "DELETE", "INSERT", "DROP", "FROM", "WHERE", "ALERT", "CREATE", "TABLE",
"VALUES", "NULL",
"AND", "OR", "NOT", "IN", "INTO", "UNION", "AS", "IS",
"COUNT", "MAX", "MIN", "AVG",
"WAIT", "NOWAIT", "WITHOUT", "LOCK", "GROUP", "ORDER", "BY",
"JOIN", "LEFT", "RIGHT", "CASE", "WHEN", "ELSE", "END",
"COMMIT", "ROLLBACK"
};
[CategoryAttribute("KeyWords"), Description("关键字列表")]
public IList<String> KeyWords
{
get
{
return this._KeyWords;
}
set
{
this._KeyWords = value;
}
}
/// <summary>
/// 关键字颜色(默认为蓝色)
/// </summary>
private Color _KeyWordsColor = Color.Blue;
[CategoryAttribute("KeyWordsColor"), Description("关键字颜色")]
public Color KeyWordsColor
{
get
{
return this._KeyWordsColor;
}
set
{
this._KeyWordsColor = value;
}
}
// TODO 数字,字符串属性
/// <summary>
/// 分隔符
/// </summary>
Regex tokens = new Regex(@"[\s\r\n\t\(\)\;]");
/// <summary>
/// 接受输入时语法着色
/// </summary>
/// <param name="e"></param>
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
// TODO DEBUG
Console.WriteLine("POINT:" + this.SelectionStart + "|" + "SIZE:" + this.Text.Length);
// 暂停屏幕刷新,防止闪烁
SuspendLayout();
// 缓存输入焦点位置
int inputPoint = this.SelectionStart;
String[] wrk_words = tokens.Split(this.Text.ToUpper());
// 判断是否有必要进行着色
if (this.Text.Length == 0) return;
// 克隆关键字列表
IList<String> C_KeyWords = new List<String>(this._KeyWords);
// 关键字开始坐标
int iStart = 0;
// 初始化
this.SelectionStart = 0;
this.SelectionLength = this.Text.Length;
this.SelectionColor = Color.Black;
this.SelectionFont = new Font("Courier New", 10, FontStyle.Regular);
// 逐字匹配
foreach (String word in wrk_words)
{
foreach (String keyw in C_KeyWords)
{
// 判断当前值是否为关键字
if (word.Equals(keyw))
{
// 查找坐标
iStart = this.Text.ToUpper().IndexOf(keyw);
// 判断是否存在处理对象
while (iStart >= 0)
{
this.SelectionStart = iStart;
this.SelectionLength = keyw.Length;
this.SelectionColor = Color.Blue;
this.SelectionFont = new Font("Courier New", 10, FontStyle.Bold);
// 获取下一次处理开始坐标
iStart = this.Text.ToUpper().IndexOf(keyw, this.SelectionStart + 1);
this.SelectionStart = this.Text.Length;
this.SelectionLength = 0;
}
}
// break;
}
}
// 计算输入点的位置,如果不在末尾,则说明用户在修改前面的内容,否则将焦点移到末尾
this.SelectionStart = (inputPoint < this.Text.Length) ? inputPoint : this.Text.Length;
this.SelectionLength = 0;
ResumeLayout();
}
}
}