C#简单实现高亮语法编辑器(一)
——TextBox ,RichTextBox的局限性
一、RichTextBox基本设置
二、实现语法高亮
三、关键字提示
四、实现行号
就简单快速得开发文本编辑器TextBox 最为简单,大家用得也多,缺点是无法实现复杂的操作。RichTextBox虽然是则功能比它强大很多。
图 1.1 输入框控件关系
这里要实现以下功能的编辑器:
1、实现语法高亮;
2、关键字提示;
3、行号。
显然TextBox 无法完成我们的任务,虽然都派生自TextBoxBase,但就控制力而言RichTextBox比它优秀很多。这里选用RichTextBox尝试开发。
注:以下只讨论简单开发,不考虑复杂的关键字查找机制。
一、RichTextBox基本设置
这里先建立一个工程,建立窗体Form1。
可以简单添加RichTextBox控件,可以在Form1_Load中建立。代码如下:
1
this
.WindowState
=
System.Windows.Forms.FormWindowState.Maximized;
2
3
RichTextBox rich
=
new
RichTextBox();
4
rich.Multiline
=
true
;
5
rich.Height
=
this
.Height
-
100
;
6
rich.Width
=
this
.Width
-
100
;
7
rich.Left
=
40
;
8
rich.Top
=
40
;
9
rich.WordWrap
=
true
;
10
rich.Text
=
"
12345678
"
;
11
rich.ScrollBars
=
RichTextBoxScrollBars.ForcedVertical;
12
this
.Controls.Add(rich);
这样就建立了简单的RichTextBox,宽度和高度都设置了。没有做Form1窗体缩放的处理。
二、实现语法高亮
在RichTextBox里实现语法高亮还是非常简单的。可以使用
1
rich.Select(
0
,
1
);
2
rich.SelectionFont
=
new
Font(
"
宋体
"
,
12
, (FontStyle.Regular));
3
rich.SelectionColor
=
Color.Blue;
意思是,先选择第一个字母,按上面的设置,选择到了数字‘1’,然后设置这个字的字体大小,再设置字的颜色。
如果对关键字进行处理(这里只处理光标向后流动的情况)
首先添加输入事件
1
rich.KeyDown
+=
new
KeyEventHandler(rich_KeyDown); //这一行添加到Form1_Load中
2
3
void
rich_KeyDown(
object
sender, KeyEventArgs e)
4
{
5
//
throw new Exception("The method or operation is not implemented.");
6
}
建立关键字
1
public
static
List
<
string
>
AllClass()
2
{
3
List
<
string
>
list
=
new
List
<
string
>
();
4
list.Add(
"
function
"
);
5
list.Add(
"
return
"
);
6
list.Add(
"
class
"
);
7
list.Add(
"
new
"
);
8
list.Add(
"
extends
"
);
9
list.Add(
"
var
"
);
10
return
list;
11
}
当KeyDown事件发生时,向前查找
1
//
返回搜索字符
2
public
static
string
GetLastWord(
string
str,
int
i)
3
{
4
string
x
=
str;
5
Regex reg
=
new
Regex(
@"
/s+[a-z]+/s*
"
,RegexOptions.RightToLeft);
6
x
=
reg.Match(x).Value;
7
8
Regex reg2
=
new
Regex(
@"
/s
"
);
9
x
=
reg2.Replace(x,
""
);
10
return
s;
11
}
1
void
rich_KeyDown(
object
sender, KeyEventArgs e)
2
{
3
RichTextBox rich
=
(RichTextBox)sender;
4
//
throw new Exception("The method or operation is not implemented.");
5
string
s
=
GetLastWord(rich.Text, rich.SelectionStart);
6
7
if
(AllClass().IndexOf(s)
>
-
1
)
8
{
9
MySelect(rich, rich.SelectionStart, s, Color.CadetBlue,
true
);
10
}
11
}
1
//
设定颜色
2
public
static
void
MySelect(System.Windows.Forms.RichTextBox tb,
int
i,
string
s, Color c,
bool
font)
3
{
4
tb.Select(i
-
s.Length, s.Length);
5
tb.SelectionColor
=
c;
//是否改变字体
6
if
(font)
7
tb.SelectionFont
=
new
Font(
"
宋体
"
,
12
, (FontStyle.Bold));
8
else
9
tb.SelectionFont
=
new
Font(
"
宋体
"
,
12
, (FontStyle.Regular));
//以下是把光标放到原来位置,并把光标后输入的文字重置
10
tb.Select(i,
0
);
11
tb.SelectionFont
=
new
Font(
"
宋体
"
,
12
, (FontStyle.Regular));
12
tb.SelectionColor
=
Color.Black;
13
}
这样就完成了高亮工作。
三、关键字提示
实现关键字提示也是在KeyDown中实现,在提示字种搜索GetLastWord返回的文字,如果前半部分匹配。那么就建立ListBox控件。
1
void
tb_KeyDown(
object
sender, KeyEventArgs e)
2
{
3
RichTextBox tb
=
(RichTextBox)sender;
4
if
(
//
条件搜索到匹配字符)
5
{
6
//
搜索ListBox是否已经被创建
7
Control[] c
=
tb.Controls.Find(
"
mylb
"
,
false
);
8
if
(c.Length
>
0
)
9
((ListBox)c[
0
]).Dispose();
//
如果被创建则释放
10
11
ListBox lb
=
new
ListBox();
12
lb.Name
=
"
mylb
"
;
13
lb.Items.Add(
"
asdasdasd
"
);
14
lb.Items.Add(
"
asdasdasd
"
);
15
lb.Items.Add(
"
asdasdasd
"
);
16
lb.Items.Add(
"
asdasdasd
"
);
17
lb.Items.Add(
"
asdasdasd
"
);
18
lb.Items.Add(
"
asdasdasd
"
);
19
lb.Items.Add(
"
asdasdasd
"
);
20
lb.Show();
21
lb.TabIndex
=
100
;
22
lb.Location
=
tb.GetPositionFromCharIndex(tb.SelectionStart);
23
lb.Left
+=
10
;
24
tb.Controls.Add(lb);
25
}
26
}
当然,另外一面,如果创建ListBox,而又在RichTextBox 点击了鼠标也去释放。
1
void
rich_MouseClick(
object
sender, MouseEventArgs e)
2
{
3
RichTextBox tb
=
(RichTextBox)sender;
4
Control[] c
=
tb.Controls.Find(
"
mylb
"
,
false
);
5
if
(c.Length
>
0
)
6
((ListBox)c[
0
]).Dispose();
7
}
当然还得在Form1_Load里注册事件
1
rich.MouseClick
+=
new
MouseEventHandler(rich_MouseClick);
然后设置ListBox 被选择后用被选择的关键字替换前文搜索到的字符。
下面我们来看看实现行号。
四、实现行号
这个是RichTextBox 唯一令我遗憾的地方,居然无法实现行号问题。为什么呢?我首先想到的是自己画。用rich.CreateGraphics()来画。但是,由于画的时候发生在窗体被创建时,所以画不成功,而被RichTextBox 本身的绘制给覆盖了。
然后我选择了在里面添加Label控件
1
Label l
=
new
Label();
2
l.Name
=
"
l
"
;
3
l.Top
=
0
;
4
l.TextAlign
=
ContentAlignment.TopRight;
5
l.Width
=
40
;
6
l.Text
=
"
1
"
;
7
l.Font
=
new
Font(
"
宋体
"
,
12
, (FontStyle.Regular));
8
l.Height
=
this
.Height;
9
l.BackColor
=
Color.Gray;
10
l.BorderStyle
=
BorderStyle.None;
11
rich.Controls.Add(l);
12
13
rich.SelectionIndent
=
40
;
rich.SelectionIndent = 40;是把光标对齐到左边距40的位置,防止光标被Label覆盖。
实现编号还不是太难。麻烦出在如何让Lable能跟随RichTextBox 的滚动条滚动。不说实现的 细节,我就假设,如果滚动条向上滚,那么Lable的Top属性增加,反之则减少。但是,RichTextBox 居然无法对ScollBar进行监测。
根本每办法知道滚动条滚动了多少位置,甚至都没办法知道滚动条滚动的方向。
尝试去除滚动条,然后之间添加新的滚动条
但是非常难于实现同步滚动,位置很难控制。这个就是目前遇到的RichTextBox 的最大局限性了,非常遗憾,无法开发出这个功能。
1
VScrollBar vs
=
new
VScrollBar();
2
//
vs.Dock = DockStyle.Right;
3
vs.Name
=
"
vs
"
;
4
vs.Maximum
=
0
;
5
vs.Minimum
=
0
;
6
vs.MaximumSize
=
new
Size(
0
,
0
);
7
vs.Top
=
0
;
8
vs.Left
=
rich.Parent.Width
-
100
-
22
;
9
vs.Height
=
rich.Parent.Height
-
100
-
1
;
10
vs.Value
=
0
;
11
vs.Scroll
+=
new
ScrollEventHandler(vs_Scroll);
12
13
rich.Controls.Add(vs);