|
MyEditor是一个功能完备的文本编辑器,它以.NET Framework所提供的RichTextBox控件为核心开发而成,集中体现了以功能强大的控件为核心打造一个Windows应用程序的方法。通过对这一程序的介绍,可以让读者看到一个功能完备、真实可用的程序是如何开发出来的。并通过对代码的分析,讲解编程技巧,让读者掌握实用的编程知识。
程序运行界面如图2-84所示。
图2-84 MyEditor运行界面
MyEditor程序共由3个窗体组成,如表2-10所示。
表2-10 MyEditor程序的3个窗体
窗 体 名 |
说 明 |
frmEditor |
编辑器主窗体 |
frmFind |
“查找”窗体 |
frmAbout |
“关于…”窗体 |
在后面的章节中将向读者逐步介绍这一软件功能的具体实现。为节省篇幅,不把全部代码清单都列举到书中,而只介绍关键点。学习时,请读者打开MyEditor项目在本书的指导下直接阅读源代码。另外,在实例代码中也加有详细的注释。
2.3.1 主窗体frmEditor界面设计
MyEditor的界面采用了通用的文本编辑器界面,由菜单、工具条、状态条和RichTextBox控件组成。
主菜单使用MainMenu控件设计,工具条是ToolBar,其Dock属性设置为Top,按钮图标使用VS .NET图标编辑器绘制。状态条是StatusBar,其Dock属性设置为Bottom,分为三个面板。
窗体中央占据大部分面积的是RichTextBox控件,其属性设置如表2-11所示。
表2-11 RichTextBox属性
属 性 名 |
值 |
说 明 |
Dock |
Fill |
让控件大小占满窗体区域 |
MultiLine |
True |
允许输入多行 |
ReadOnly |
False |
允许用户修改文件 |
ScrollBars |
Both |
文档过长过宽时,出现水平和垂直滚动条 |
TabStop |
False |
文档中允许使用Tab键跳格 |
WordWrap |
True |
当一行最后写不完一个英文单词时,整个单词自动换到下一行 |
本书在前面的章节中已介绍了使用VS .NET进行界面设计的相关知识,相信读者一定能很快地用相应控件“搭”出这个程序界面。
2.3.2 结束程序运行
当一个程序需要结束运行时,只需执行以下命令:
Application.Exit()
或
End
上述两个语句关闭所有打开的窗体,并立即结束程序。除此之外,在所有的Windows Form应用程序中,都有一个窗体是主窗体(本例是frmEditor窗体)。在主窗体上调用Close()方法,也会导致整个应用程序的退出。但对主窗体以外的任何窗体调用Close()方法,仅会关闭这个窗体,而不会结束整个应用程序。
2.3.3 打开文件功能的实现
在MyEditor中,从“文件”菜单中选择“打开文档”,或者是直接单击工具栏上的“打开”按钮,均会打开一个选择文件对话框,如图2-85所示。
图2-85 打开文档对话框
选择一个RTF或TXT文档后,MyEditor将显示出文档内容。
在具体开发时,把一个OpenFileDialog组件拖动到窗体上,设置其Filter属性为“RTF文档(*.rtf)|*.rtf|TXT文档(*.txt)|*.txt|所有文档(*.*)|*.*”,这将会使OpenFileDialog过滤出所有可显示的文档文件。接着,设置ShowReadOnly=True,Title=“打开文档”,这样设置之后,OpenFileDialog控件显示时将如图2-86所示。
MyEditor支持两种格式的文档文件:
一种是纯文本格式的文档,其文件后缀名是“.txt”,这种格式文件的特点是文件中只存有字符信息,没有格式信息,换句话说,不能给文字指定字体、颜色、大小等特征。
另一种是RTF格式的文档,其文件后缀名是“.rtf”,这种格式文件的特点是可以保存文字的格式信息,就像大家非常熟悉的Word文档一样,同一篇文章中的文字可以选择不同的字体、颜色等特性。
MyEditor处理文档的核心控件是.NET提供的RichTextBox控件,这一控件可以正确显示并处理RTF和TXT两种格式的文档。
技术内幕
读者可能会好奇RTF文档到底是怎样保存文档信息的。请用Windows附件中的记事本(这是一个只能打开纯文本格式文档的文本编辑器)打开“陋室铭.rtf”这个示例文档(参见本书配套光盘),您将会看到图2-86所示的情形。
图2-86 RTF文档内容
在文档中看不到任何一个汉字,而充满了反斜框开头的字符和单词,要想弄明白这些字符和单词的含义,请您去学习RTF格式的技术文档。
以下网址可以获取RTF格式的更多资料:
http://msdn.microsoft.com/library/en-us/dnrtfspec/html/rtfspec.asp
MyEditor程序定义了一个枚举类型的变量用于区分RTF和TXT两种文档格式。
'文件格式常数
Public Enum DocumentType
TXTDocument
RTFDocument
End Enum
只要打开了一个文档,则它必定属于TXT和RTF两种格式之一,所以,在窗体frmEditor中定义了两个类的私有变量用于记录下文档信息:
'当前打开的文件名
Private curFileName As String = ""
'当前打开文档的类型
Private curFileType As DocumentType = DocumentType.RTFDocument
注意到MyEditor的默认格式是RTF,这是合理的,因为RichTextBox控件本来就是设计用于显示RTF格式的文档的。
现在来看一下MyEditor中“打开文档”功能的具体实现代码:
01 '打开已有文档
02 Private Sub OpenFile()
03 Dim filename As String = ""
04 '获取用户选取的文件名
05 If Me.OpenFileDialog1.ShowDialog() = DialogResult.OK Then
06 filename = Me.OpenFileDialog1.FileName
07 End If
08
09 Dim fileExt As String
10
11 If filename.Trim <> String.Empty Then
12 '重置为默认值
13 Me.RichTextBox1.ResetText()
14 Try
15 '获取打开文件的扩展名(转为大写)
16 fileExt = System.IO.Path.GetExtension(filename).ToUpper
17 If fileExt = ".RTF" Then
18 '打开RichText格式的文档
19 Me.RichTextBox1.LoadFile(filename, _
RichTextBoxStreamType.RichText)
20 Me.curFileType = DocumentType.RTFDocument
21 Me.StatusBar1.Panels(0).Text = "RTF格式文档"
22 Else
23 '打开纯文本文档
24 Me.RichTextBox1.LoadFile(filename, _
RichTextBoxStreamType.PlainText)
25 Me.curFileType = DocumentType.TXTDocument
26 Me.StatusBar1.Panels(0).Text = "纯文本格式文档"
27 End If
28
29 curFileName = filename
30 '如果成功,则设置主窗体标题栏
31
32 Me.showFormTitle()
33 '设置改动标志
34 Me.RichTextBox1.Modified = False
35 Catch ex As Exception
36 MsgBox("不能打开这种类型的文档。本编辑器只能打开RTF与纯文本格式 的文档。系统返回的信息是:" + ex.Message,_
MsgBoxStyle.Information Or MsgBoxStyle.OKOnly, "提示信息")
37 End Try
38 End If
39
40 End Sub
可以看到,首先需要获取用户选择的文件名。当用户在“打开文档”对话框中选择了一个有效的文件名并单击【打开】按钮时才真正打开文档。否则,OpenFileDialog的FileName属性是空串。
然后检查打开的文件是否以“.RTF”结尾,因为可能有的文件扩展名不规范,如“.rtf”、“.Rtf”之类,所以,第16句调用System.IO.Path类的GetExtension() 方法获取文件扩展名,由于GetExtension() 方法返回一个字符串,所以,可以调用String类的ToUpper()方法将得到的扩展名全部转为大写。
提示
凡是返回一个对象的函数都可以当成是一个对象使用。
RichTextBox打开文档都是用LoadFile()方法实现的,只不过需要根据不同的文档类型使用不同的参数(第19行和第24行)。
一旦打开文档之后,马上设置curFileType与curFileName字段为有效的值,供窗体中其他的方法使用。
特别注意第14行、第35行和第37行引入了一个Try…Catch…End Try语句,用这个语句结构把打开文档的功能代码“包”了起来。
这种Try…Catch语句称为异常处理语句,其格式如下:
Try
'可能出错的语句块
Catch ex As Exception
'出错处理
End Try
在Try部分可以有多条语句,其中任何一条语句引发错误时,都会跳转到Catch部分执行,而Try中发生错误语句之后(到Catch部分之前)的任何语句就不再被执行。
为什么要在打开文档时加上Try…Catch?因为有可能以“.RTF”为扩展名的文件并不真的是RTF格式的文件。为了验证这一点,请在“我的电脑”中把一个JPG文件扩展名改为“.RTF”,然后在代码中注释掉Try…Catch…End Try语句,MyEditor在按RTF格式装入这个JPG文件时,VS .NET将会报告出现如图2-87所示的错误。
图2-87 无效的文件格式错误
如果是在“资源管理器”中直接运行MyEditor.exe文件,则此错误将导致MyEditor程序退出。
使用Try…Catch之后,再打开这个文件,得到的提示如图2-88所示。
图2-88 意义明确的出错信息
这时,程序不会异常结束。
提示
异常捕获语句是程序健壮性的重要保证,在3.4节中会对异常处理进行更深入的介绍。
阅读MyEditor源代码时还有另一个需要注意的地方。由于“打开文档”这个功能在两个地方都需要被调用(一个是菜单,一个是工具栏),所以,笔者把实现这一功能的代码抽取出来放在一个Sub过程中,作为窗体的一个私有方法(Private Sub OpenFile()),而在菜单项和工具栏按钮的单击事件处理代码中只写这个方法的名字,如下所示:
Private Sub mnuFileOpen_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles mnuFileOpen.Click
Me.OpenFile()
End Sub
提示
不在界面控件的事件处理过程中书写太多的功能代码,而将这些功能代码抽取出来作为类私有的函数和Sub 过程,仅在界面控件的事件处理过程中调用这些私有的函数和Sub 过程,这是一种好的编程风格。请读者在开发自己的程序时,始终遵循这一基本编程原则。
2.3.4 编辑功能的实现
在20世纪90年代初期,中国发行量最大的普及性计算机报纸《电脑报》曾报道过著名作家马识途年过花甲开始学“五笔字型”,用电脑来写作的事迹,于是在中国的作家之中掀起了一轮“换笔”高潮。搞文学的作家却对中国的计算机普及起了独特的作用,这恐怕是计算机科学家们预料不到的事。
作家“换笔”,其实就是用文本编辑器来写文章。现在大家非常熟悉的功能,比如复制、查找、替换,对于那些多年来使用手工改稿的作家而言,带来的写作效率上的提升无疑是革命性的。
计算机在中国的普及,最早就是以“五笔字型”汉字输入法 + WPS字处理软件开始的,这两者是如此地流行,以至于当时普通人对电脑的基本概念就是一台“打字机”。当时的主流操作系统还是字符界面的DOS,中国第一代优秀的程序员求伯君先生一人开发出了当时在DOS上功能最强最好用的中文处理软件WPS,占据了绝大部分市场。
然而软件世界风云变幻,求伯君没有把握住从操作系统DOS向Windows转移的机遇,决策失误,终于让支持“所见即所得”文本编辑功能的微软Word攻城略地,最终占据了中文文字处理的主要市场份额,而WPS现在虽然还在,但已无法对Word造成威胁了。
客观地说,Word所见即所得的特性,较原先DOS下的WPS编辑与打印状态分离的方式,要方便得多。而这么些年来,软件技术的进步又是惊人的,在.NET下,要实现比以前WPS更强大的功能,实在是轻而易举。
在本节中,将向读者介绍MyEditor中常用编辑功能的实现方法。
1.选择文字
在RichTextBox中有几种方式可以非常方便地选择文字:
(1)鼠标拖动选择;
(2)按住Shift键后,再用光标键手动选择文字;
(3)按Ctrl+A组合键可选择整篇文档。
RichTextBox以深色的底色表示选中之后的文字。这部分功能是由RichTextBox本身提供的,不需要做任何附加工作即可使用。
许多编辑功能的实现都是针对选中的文字而言的,所以,如果没有选中文字,则许多功能不起作用。
RichText有许多以“Selection”开头的属性(如SelectionColor),通过编程设置这些属性可以改变选中文字的特性。
可以通过编程来选择特定范围的文字。SelectionStart属性用于定义选中区域的起始位置,SelectionLength属性定义要选中多少个字符,而SelectedText属性则返回选中的文字,如果把SelectedText属性设置为空串,则会清除选中的文字。
2.复制、剪切和粘贴
在MyEditor中,可以用鼠标拖动选取若干文字,从“编辑”菜单中选择“复制”(或直接按Ctrl+C组合键),然后,将光标移到想插入选中文字的地方,从“编辑”菜单中选择“粘贴”(或直接按Ctrl+V组合键),就可以将选中文字复制到此处。多次按Ctrl+V组合键就可以多次复制。
“剪切”功能与“复制”类似,不同之处在于剪切之后,原来位置上的文字消失。
RichTextBox直接支持这一功能,只需调用它的Copy()、Cut()和Paste()方法即可。
3.项目符号
图2-89是加了项目符号的《陋室铭》,要实现这一功能,只需设置RichTextBox的SelectionBullet属性为True即可。
4.对齐方式
一段文字还可以设置它是左对齐、右对齐和居中对齐,如图2-90所示。
图2-89 项目符号功能 图2-90 对齐方式
要实现图2-90的效果,只需设置RichTextBox的SelectionAlignment属性为特定值即可。
5.段落缩进
可以调整(增加或减少)某个段落距页边距的距离,参见图2-91。
要实现图2-91的功能,需要增加或减少RichTextBox的SelectionHangingIndent和Selection Indent属性值。
6.上标和下标
上标和下标功能可以通过设置SelectionCharOffset属性来实现。以图2-92为例,要输入X的平方时,可以直接输入“X2”,然后选中“2”减少它的字体大小(将在后面介绍),再从菜单中选择“上标”(其实就是增加SelectionCharOffset属性的值)即可。可以多次执行这一命令以调整上标位置。
不需要上下标功能时,先选中上标或下标文字,从菜单中选“取消上下标”即可。
图2-91 段落缩进 图2-92 上标功能
7.设置字体与字号
中国的书法艺术源远流长,真草隶篆四种书体涌现了无数的书法大家,图2-93展示了楷书与行书两种字体。
但Windows本身并未选配太多的字体,只有宋体、楷体、隶书等有限的几种。一台电脑中可用的字体取决于是否安装了扩充的字体,可以编程来查看计算机中已安装的字体:
图2-93 不同的汉字字体
Dim ff As FontFamily
For Each ff In System.Drawing.FontFamily.Families
listBox1.Items.Add(ff.Name) '将字体名显示在ListBox中
Next
在电脑中显示一个汉字,除了指定它的字体,还可设定它的字形(FontStyle),有粗体、斜体、下划线这三种基本字形,它们之间可以相互叠加,比如粗斜体就是粗体、斜体两种字形的叠加。
字体还可以设置大小,这是一个整数,一般默认情况下宋体字大小为10(称为字号,单位为Point,中文译为“点”)。各种字体字形的样例如图2-94所示。
RichTextBox中可以给选中的文字设置字体,以下代码将选中文字的字体设置为宋体,字号为10。
Dim curFont As Font
curFont = New Font("宋体", 10, FontStyle.Regular, GraphicsUnit.Point)
RichTextBox1.SelectionFont = curFont
在MyEditor程序可以通过字体对话框设置字体(从“格式”菜单中选择“字体...”命令,如图2-95所示。
图2-94 字体样例 图2-95 字体对话框
这个对话框是Windows提供的标准对话框,在VS .NET中可以通过FontDialog控件显示此对话框。
用户选中的设置可以通过FontDialog控件的Font属性取回(字体颜色则通过Color属性取回)。
提示
在显示Font对话框之前,先设置Font和Color属性,可以让FontDialog控件按指定的Font和Color属性值进行初始化。
MyEditor还可以设置字体的大小和字形(粗体、斜体、下划线及其组合),但在阅读这些代码时,必须注意一点:在Visual Basic .NET中,字体的许多属性在运行时是只读的,比如以下代码并不能让标签控件的字体大小加1:
Label1.Font.Size += 1
要想改变已有字体的属性,只能重新创建一个新的字体对象。
' 让标签字体增大一号
With Label1.Font
Label1.Font = New System.Drawing.Font(.FontFamily.Name, _
.Size + 1, .Style)
End With
粗体、斜体等字形由于是可以取消及相互叠加的,所以比较复杂,以设置粗体为例:
01 '粗体
02 Private Sub SetBold()
03
04 Dim _font As Font
05 _font = Me.RichTextBox1.SelectionFont
06 '如果不能识别出字体
07 If _font Is Nothing Then
08 Exit Sub
09 End If
10 If _font.Bold = False Then
11 _font = New Font(_font, _font.Style Or FontStyle.Bold)
12 Else
13 '除去粗体选项
14 _font = New Font(_font, _font.Style - 1)
15 End If
16
17 Me.RichTextBox1.SelectionFont = _font
18
19 End Sub
特别注意第11句,不管选中的文字是不是粗体,通过将Sytle属性值与FontStyle.Bold常量相或(Or),都可以设置选中的文字为粗体。
第14句则反之,通过减去一个特定的数字“1”,从而将原来的粗体特性去掉。
技术内幕
除去斜体特性减“2”,除去下划线特性减“4”,正好是2的整数倍(请参见SetItalic()和SetUnderline()两个Sub过程)。
为什么会这样?请看表2-12:
表2-12 用二进制设置字体特性
二 进 制 |
十 进 制 |
说 明 |
0000 |
0 |
标准字体 |
0001 |
1 |
粗体 |
0010 |
2 |
斜体 |
0100 |
4 |
下划线 |
1000 |
8 |
删除线 |
从表2-13中可以看到,四个二进制位分别代表了四种不同的字体特性,特定的位被置1时,表示拥有这项特性,置0时,表示取消这种特性。特性可以组合,比如粗体加斜体,用二进制数表达就是“0011”,即十进制数2+1=3;取消斜体之后,用二进制数表达就是“0001”(十进制数1),刚好等于原来的值减去代表斜体的数值2。其他的特性组合可依次类推。
这是布尔代数在编程中应用的一个实例。
粗体、斜体等特性可以通过工具栏上的按钮来设置。在MyEditor中选中一些文字,您会发现随着选中文字的字形特性不同,粗体、斜体和下划线三个按钮会自动地按下或弹起,与选中文字的字形特性保持一致。这是怎样做到的?
其实很简单,RichTextBox控件有一个SelectionChanged事件,当在RichTextBox控件中选中一段文字时此事件发生。在此事件中取出选中文字的Font属性,就可以正确设置粗体、斜体和下划线三个按钮的状态,代码如下:
'正确设置粗体、斜体和下划线的组合方式
Private Sub RichTextBox1_SelectionChanged(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles RichTextBox1.SelectionChanged
Dim _font As Font
_font = Me.RichTextBox1.SelectionFont
If Not (_font Is Nothing) Then
Me.btnBold.Pushed = _font.Bold
Me.btnItalic.Pushed = _font.Italic
Me.btnUnderLine.Pushed = _font.Underline
End If
Me.ShowStatus()
End Sub
8.缩放
缩放与改变字体大小是不同的,前者就像是用一个放大镜去看文章,文章本身的文字字体没变,但人透过放大镜看到的文字是变大了的。
要实现缩放是很简单的,只需设置ZoomFactor值即可。增加值则放大,减少值则缩小。
2.3.5 查找功能的实现
任何一个文本编辑器都提供查找与替换功能。MyEditor的查找与替换功能如图2-96所示。
在MyEditor中,查找与替换功能由窗体frmFind实现。此窗体具有以下特点:
— 窗体被设置为总在最前面,即它的TopMost属性为True。
— 窗体的最大化按钮不能用。
图2-96 查找与替换
— 当用户在编辑器主窗体中选中了文字,再打开此窗体时,选中的文字会自动地加入到“查找”组合框中。
— 只要用户单击过查找、替换、全部替换三个按钮之一,其在两个组合框中输入的文字将会被自动加到这两个组合框的下拉列表中,这样第二次查找相同的文字时不再需要手工键入。
— 当用户多次打开查找窗体时,他原先进行过查找和替换的文字可以全部保存在两个组合框的下拉列表中,除非他关闭了MyEditor。
下面分析MyEditor查找与替换功能的实现代码。
1.将窗体“关闭”行为改为“隐藏”
一般情况下,关闭一个窗体意味着销毁一个窗体,关闭之后这个窗体就不能用了。请看以下代码:
Dim frm As New Form2 '创建一个Form2对象
frm.Show() '显示它
frm.Close() '关闭它
frm.Show() '再次显示它
上述代码执行时将会出现如图2-97所示的错误。
图2-97 访问“已处置对象”异常
引发这个错误的原因是窗体类的Close()方法执行后,计算机认为这个窗体不再用了,它所占用的各种资源(比如内存)可以被回收。这样,frm这个变量所代表的窗体实际上已经不存在了,再次调用Show()方法自然会出错。
在MyEditor中需要在多次显示查找窗体时,用户原先的输入仍然保存。因此,不能采用每次调出“查找与替换”窗体时都新创建一个窗体对象的方法(如果这样做,则每次窗体显示时,“查找”与“替换”组合框的下拉列表总是空的,用户原先的输入都不见了)。
解决方法是当用户单击“查找与替换”窗体上的【关闭】按钮时,并不调用窗体的Close()方法关闭窗体,而仅仅是将它隐藏起来,代码如下所示:
Private Sub btnClose_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnClose.Click
'将窗体隐藏起来,并不销毁它,以保存用户原先的输入
Me.Visible = False
End Sub
当用户在主窗体frmEditor中单击工具栏上的【查找】按钮调出“查找与替换”窗体时,只需将其Visible属性设为True就可以了。
比较麻烦的是如果用户不是单击【关闭】按钮而是直接单击窗体右上角的那个“′”(即所有窗体都有的关闭按钮)关闭窗体的,默认情况下这么做会自动调用窗体的Close()方法关闭窗体,这样一来,再次单击工具栏对应按钮显示“查找与替换”窗体时就会出现前面所介绍的 “访问已处置对象”异常。
为此必须“取消”单击窗体右上角的那个“′”关闭窗体的功能。方法很简单,处理窗体的Closing事件即可:
Private Sub frmFind_Closing(ByVal sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
Me.Visible = False '隐藏自己
e.Cancel = True '取消关闭窗体的功能
End Sub
注意,Closing事件的参数e中有一个Cancel成员,如果设置它为True,则窗体不会关闭。
2.在主窗体与查找窗体间传送信息
查找窗体frmFind中有一个rt1成员变量:
'要处理的控件,从外界传入
Public rt1 As RichTextBox = Nothing
这实际上就是主窗体中的RichTextBox控件,当在主窗体中创建frmFind窗体对象时,就给此成员变量赋值,参见以下主窗体代码:
'查找与替换窗体
Private findwindow As frmFind = Nothing
Private Sub Init()
'……
'创建“查找与替换”窗体
Me.findwindow = New frmFind
'将主窗体RichTextBox对象变量传给“查找与替换”窗体
Me.findwindow.rt1 = Me.RichTextBox1
'……
End Sub
真正实现向frmFind窗体传送字符串的功能,是在主窗体frmEditor中的FindAndReplace()过程中实现的:
'查找与替换
Private Sub FindAndReplace()
If findwindow Is Nothing Then
Exit Sub
End If
'如果用户选中了一个字词,则认为他想查找此字词,所以将其自动加入到
'查找窗体的“查找”组合框中
If Me.RichTextBox1.SelectedText <> String.Empty Then
findwindow.cboFind.Text = Me.RichTextBox1.SelectedText
End If
'如果窗体是最小化的,则恢复原大小
findwindow.WindowState = FormWindowState.Normal
'显示此窗体
findwindow.Show()
End Sub
3.查找功能的实现
实现查找功能的核心是由RichTextBox控件本身提供的Find()方法。RichTextBox控件提供了许多个Find()方法的重载,MyEditor选择了参数最多也最灵活的那个重载函数:
Overloads Public Function Find( _
ByVal str As String, _ '要搜索的文字
ByVal start As Integer, _ '开始搜索的位置
ByVal end As Integer, _ '结束搜索的位置
ByVal options As RichTextBoxFinds _ '搜索选项
) As Integer
上述函数在RichTextBox显示的文档中搜索str,从第start个字符开始,到第end个字符结束,如果找到了str的第一次出现,则返回str中第一个字符距文档开头的字符数(即字符索引),如果找不到,则返回-1。
RichTextBox把它所显示的文档看成是一个字符的数组,每个字符都有一个位置索引(一个正整数),可以通过指定一个索引值获取这一字符。
start和end参数确定了一个查找范围,如果end参数值为“-1”,则表示搜索到文档结尾。
Find()方法的最后一个参数options是一个枚举值,说明如下(表2-13)。
表2-13 options成员
成员名称 |
说 明 |
值 |
MatchCase |
仅定位大小写正确的搜索文本的实例 |
4 |
NoHighlight |
如果找到搜索文本,不突出显示它 |
8 |
None |
定位搜索文本的所有实例,而不论在搜索中找到的实例是否是全字 |
0 |
Reverse |
搜索在控件文档的结尾处开始,并搜索到文档的开头 |
16 |
WholeWord |
仅搜索全字(以空格及标点符号定界)匹配的文字 |
2 |
Find()方法的使用非常直观:
Dim found As Integer
found = Me.RichTextBox1.Find("China", 0, 100, _
RichTextBoxFinds.MatchCase Or RichTextBoxFinds.WholeWord)
If found <> -1 Then
MsgBox("在第" & found & "个字符处找到了China这个单词")
Else
MsgBox("没找到")
End If
上述代码在RichTextBox中查找“China”这个单词,区分大小写,要求全字匹配。
由于RichTextBox控件已提供了功能强大的查找功能,所以,MyEditor的查找功能就是对RichTextBox控件Find()方法的一个包装,由frmFind窗体中的FindNext()过程实现:
'完成“查找下一个”的功能
Private Sub FindNext()
'--------------------------------------
'获取要查找的单词
'--------------------------------------
Dim WordToFind As String '要查找的单词
If rt1 Is Nothing OrElse Me.cboFind.Text = String.Empty Then
Exit Sub
Else
WordToFind = Me.cboFind.Text '从组合框中取出单词
End If
'-----------------------------------------------
'设置搜索选项
'-----------------------------------------------
Dim options As System.Windows.Forms.RichTextBoxFinds = _
RichTextBoxFinds.None
If Me.chkMatchCase.Checked = True Then '搜索区分大小写
options = options Or RichTextBoxFinds.MatchCase
End If
If Me.chkWholeWord.Checked = True Then '是否全字匹配
options = options Or RichTextBoxFinds.WholeWord
End If
'----------------------------------------------
'开始查找
'----------------------------------------------
Dim iLoc As Integer '找到的单词的位置索引
'调用RichTextBox提供的Find()方法查找单词
iLoc = rt1.Find(WordToFind, curPos, -1, options)
If iLoc <> -1 Then '如果找到了
'将开始找此单词下一次出现的位置保存在窗体级变量curPos中
curPos = iLoc + WordToFind.Length
Else
'回到开头
curPos = 0
MsgBox("没能找到匹配项", MsgBoxStyle.OKOnly Or _
MsgBoxStyle.Information, "提示信息")
End If
'将查找过的单词加入组合框的下拉列表中
Me.AddTextToList(Me.cboFind)
End Sub
在上面的代码中,调用Find()方法查找的结果放到了变量curPos中,这一变量是在窗体中定义的,所以,下次再调用FindNext()时将从curPos开始继续查找。
实现了查找功能,替换功能就非常容易实现了,查找成功时,这一单词会被自动选中,这时,只需将RichTextBox的SelectedText属性置为想要替换的单词即可。
比较复杂一点的是“全部替换”功能,需要不断地调用Find()方法查找出所有需要被替换的单词的位置,然后逐个替换。全部替换功能由ReplaceAll()过程实现:
'全部替换
Private Sub ReplaceAll()
Dim WordToFind As String '要查找的单词
If rt1 Is Nothing OrElse Me.cboFind.Text = String.Empty Then
Exit Sub
Else
WordToFind = Me.cboFind.Text
End If
'确定搜索选项
'……
Dim i, j As Integer
Dim count As Integer
Dim start As Integer
'在全部文本中查找WordToFind
i = rt1.Find(WordToFind, 0, -1, options)
'找到了WordToFind的第一次出现
If i <> -1 Then
j = 0 '初始化变量j,此变量用于保存每次调用Find的结果
count = 0 '初始化替换次数计数器
'不断查找
While j <> -1
start = Me.rt1.SelectionStart
'保存被替换的单词的字体和颜色信息
'……
'实现替换
Me.rt1.SelectedText = Me.cboReplace.Text
'设置替换后文本字体和颜色与被替换的单词一致
'……
count += 1 '统计替换次数
'从新的位置进行查找
j = rt1.Find(WordToFind, start + WordToFind.Length, -1, options)
End While
MsgBox("替换完成,共替换 " & count & " 处。", _
MsgBoxStyle.Information Or MsgBoxStyle.OKOnly, "提示信息")
Else
'回到开头
curPos = 0
MsgBox("没能找到匹配项。", MsgBoxStyle.OKOnly Or _
MsgBoxStyle.Information, "提示信息")
End If
End Sub
提示
阅读这段代码重点要弄明白Find()方法的起始坐标是如何确定的。