第1章 文本编辑器
文本编辑是计算机最常用的数据处理功能之一。微软公司推出的Microsoft Word就是一款经典的文本编辑软件,具有很强的文本编辑和处理能力,在学习和工作中应用极为广泛。此外,Windows操作系统自带的记事本也是一个文本编辑软件,能够进行简单的文本处理。本章使用Visual Basic 6.0也可以设计一个文本编辑器。
1.1 需求分析
通常,文本编辑器具有打开、保存、剪切、复制、粘贴等功能,本章设计的文本编辑器包括以下一些基本功能。
(1)能够同时打开多个不同的编辑窗口,对多个文本进行独立编辑。这些窗口包含在一个父窗体中,并都具有独立的编辑功能。
(2)可以打开已经保存在磁盘上的文本文件。将文件中的内容显示在文本编辑器的某个指定窗体中,同时在该窗体的标题栏和状态栏上显示该文件的存放路径。
(3)可以将文本保存到指定的路径下。
(4)可以打印任一窗口中的文本。
(5)能够对文本的字体进行设置,包括字体、大小、字形、删除线和下画线效果,以及文本颜色。
(6)可以从同一个文本编辑窗口中剪切、复制和粘贴文本,也能从不同的文本编辑窗口中剪切、复制和粘贴文本。
(7)可以设置文本的对齐方式,包括左对齐、居中对齐和右对齐。
(8)此外,该文本编辑器界面简洁、操作简单,具有用户熟悉的Word软件的风格。图1-1为多窗体文本编辑器的界面图。
图1-1 多窗体文本编辑器界面
1.2 技术要点
本章将设计一个多窗体文本编辑器,可以同时对多个文本进行编辑操作。多窗体文本编辑主要通过多文档界面MDI的编程技术实现,其中涉及的技术要点有窗体对象、上下级菜单、指定活动子窗体、设置子窗体和子窗体排列等。
在文本编辑器中,使用RichTextBox控件放置文本。通过RichTextBox控件的SelAlignment属性可以设置文字的对齐方式,包括左对齐、居中和右对齐。
文本的打开、另存、打印、字体和颜色对话框是通过CommonDialog控件实现的。
使用Clipboard对象实现文本的剪切、复制和粘贴功能。
通过菜单编辑器设计子窗体菜单和父窗体菜单,以及RichTextBox控件上的弹出式编辑菜单。
工具栏和状态栏分别使用Toolbar和StatusBar控件设计,工具栏按钮上的图片通过ImageList控件添加。
1.3 系统结构
创建一个新的工程,添加一个MDI窗体和一个Form窗体。在Form窗体上添加ImageList、Toolbar、StatusBar、CommonDialog和RichTextBox控件。工程的对象及其属性值如表1-1所示。
表1-1 工程的对象及其属性值
MDI窗体和Form窗体分别有各自的菜单栏,通过菜单栏中的各项命令,可以实现文本的编辑操作。MDI窗体和Form窗体的菜单栏列表,如表1-2所示。
表1-2 窗体对象中的菜单栏列表
和MDI窗体不同的是,Form窗体还有工具栏,该工具栏中的大部分命令与Form窗体的菜单项命令可以实现相同的编辑操作。表1-3为Form窗体中的工具栏列表。
表1-3 Form窗体中的工具栏列表
工具栏控件Toolbar中索引为5、9、11等按钮的样式被设置为3,作为按钮间的分隔使用。因此,在表1-3中并未将其列出。
此外,工具栏控件Toolbar的按钮与ImageList控件中的图像是相对应的,其对应关系如表1-4所示。
表1-4 Toolbar与ImageList控件对应关系
1.4 实现过程
根据设计过程和程序功能,可以将文本编辑器的实现过程分为创建工程、父窗体设计、子窗体设计,以及子窗体中的各菜单项、工具栏和状态栏的设计。
1.4.1 创建工程
新建一个工程,默认情况下该工程已经包含一个Form窗体,用做文本编辑窗体。
此外,还要向该工程中添加一个MDI Form窗体,用做父窗体。添加MDI Form窗体方法一般有两种:其一,从“工程”菜单项中选择“添加MDI窗体”子菜单项即可;其二,单击工具栏“添加窗体”的下三角按钮,从下拉列表框中选择“添加MDI窗体”即可。
Form窗体和MDI Form窗体的属性设置见表1-1。通常,新建工程后,启动对象为Form窗体。本章要将MDI Form窗体,即父窗体设置为启动对象,设置步骤如下所示。
(1)从“工程”菜单项的下拉列表框中,选择 “工程属性…”子菜单项,弹出“工程属性”对话框。
(2)打开“工程属性”对话框中的“通用”选项卡。
(3)从“启动对象”下拉列表框中,选择父窗体即可。图1-2为“工程属性”对话框。
图1-2 “工程属性”对话框
1.4.2 父窗体设计
父窗体是个容器,主要用于放置文本编辑窗体,图1-3为父窗体的设计界面。从“工程”菜单项的下拉列表框中,选择“部件…”子菜单项,在“控件”选项卡列表框中选择Microsoft Common Dialog Control 6.0 (SP3)选项,将CommonDialog控件添加到控件工具箱中。向窗体上添加一个CommonDialog控件,用做程序中的“打开”对话框。
图1-3 父窗体的设计界面
父窗体的菜单比较简单,当所有子窗体都关闭时,可以通过该菜单新建或直接打开一个子窗体。而当父窗体中只要有一个子窗体获得焦点时,该菜单会被子窗体的菜单取代,父窗体的菜单根据表1-2编辑。
1. 子窗体对象
本章设计的文本编辑器可以在程序运行时创建若干个子窗体,同时可以编辑多个文本,也可以实现子窗体间文本的复制与粘贴操作。子窗体的创建不是通过程序设计时添加多个Form窗体实现的。事实上,本章在设计时只添加了一个Form子窗体,作为文本编辑窗体的原型。通过窗体对象的方法,创建窗体对象的实例,每个实例都继承对象原型的所有属性、方法和事件。而且,在应用程序中,这些实例都是独立于对象原型的。同时,实例之间虽然具有完全相同的属性、方法和事件,但是,各实例之间的操作却并不相互影响。
以新建文本编辑窗体为例,创建窗体对象实例的完整程序代码如程序清单1-1 所示。父窗体加载程序代码与此类似。
程序清单1-1新建文本
1. Public intSubFormCounts As Integer '声明创建的子窗体对象实例数量变量 2. 3. Private Sub menuFileNew_Click() '新建文本 4. Dim frmSubForm As Form '声明Form类型的窗体对象变量 5. 6. Set frmSubForm=New frmTextEdit '给变量frmSubForm赋值 7. frmSubForm.Show '显示子窗体对象实例 8. intSubFormCounts=intSubFormCounts+1 '更新子窗体数量 9. frmSubForm.Caption="文本"&Str(mdfMainForm.intSubFormCounts) 10. End Sub
程序说明:本段代码主要是使用面向对象的方法,创建窗体对象实例,实现新建文本功能。在父窗体加载、执行新建和打开命令时,程序都声明和创建子窗体对象实例。第1行代码是在父窗体的通用代码区声明intSubFormCounts变量,每次创建新的子窗体对象实例时,都累计子窗体对象实例的数量。第4行声明frmSubForm为Form类型的窗体对象变量。窗体对象变量的声明与其他类型变量的声明方法是一样的,可以使用Dim、Public和Provate等关键字声明。声明了窗体对象变量之后,第6行就使用Set语句和New关键字创建窗体对象实例。第7 行通过Show方法将刚创建窗体对象实例显示出来。同时,更新intSubFormCounts变量来累积子窗体对象实例的数量。
2. 打开文本
打开文本的程序主要包括三大部分,分别是设置和显示“打开”对话框、创建子窗体对象实例和加载文件。打开文本的程序代码如程序清单1-2所示。
程序清单1-2打开文本
1. Private Sub menuFileOpen_Click() '打开文本 2. On Error GoTo ErrorTrap 3. 4. '-----------------------“打开”对话框设置----------------------------------------- 5. cdlDialog.CancelError=True '设置“取消”为True 6. cdlDialog.Flags=cdlOFNHideReadOnly '隐藏“打开”对话框上的只读复选框 7. cdlDialog.Filter="Text Files"&"(*.txt)|*.txt" '设置文件过滤器 8. cdlDialog.FilterIndex=1 '设置默认过滤器 9. cdlDialog.ShowOpen '显示“打开”对话框 10. 11. '-----------------------调用新建文本程序来创建子窗体对象实例--------------- 12. menuFileNew_Click 13. 14. '-----------------------向子窗体文本编辑区加载文本------------------------------ 15. mdfMainForm.ActiveForm.rtbRichText.LoadFile cdlDialog.FileName,1 '加载文件 16. mdfMainForm.ActiveForm.Caption="文本 "&cdlDialog.FileName '窗体标题 17. '栏上显示打开的文件名 18. sbrStatus.Panels(2).Text=cdlDialog.FileName '将文件路径显示在状态栏上 19. 20. Exit Sub 21. ErrorTrap: 22. End Sub
程序说明:第5~9行是将CommonDialog控件设置为“打开”对话框。第12行通过调用了新建文本过程,创建子窗体对象实例,简化了程序编写。第15行和第16行中的ActiveForm属性用于返回父窗体中获得焦点的子窗体,即向正在活动的子窗体中加载文本。
1.4.3 子窗体设计
子窗体是进行文本编辑的主要窗体,其菜单和工具栏包含了文本编辑的所有命令。图1-4为子窗体的设计界面。
图1-4 子窗体的设计界面
从“部件”对话框中选择Microsoft Rich Textbox Control 6.0 (SP4) 和Microsoft Windows Common Control 6.0 (SP6)选项,将RichTextBox控件添加到控件工具箱中。向子窗体上添加相应控件,并根据表1-1、表1-2和表1-3分别设置控件属性、编辑菜单和设计工具栏。
1.4.4 用户区尺寸设置
RichTextBox控件是文本存放和编辑的容器,即用户区。其位置和大小应充满子窗体,但又不能遮掩工具栏和状态栏等。在程序运行时,用户随时都可能改变子窗体的大小,因此设置用户区的尺寸和位置状态非常必要。用户区的设置分为程序加载时的初始化设置和用户改变窗体大小时的自动设置。
当改变窗体大小时,通过子窗体的Resize过程,改变用户区的位置和大小,使之随窗体大小一起改变,程序代码如程序清单1-3 所示。程序加载时,用户区的初始化程序代码与此相同。
程序清单1-3用户区尺寸设置
1. Private Sub Form_Resize() 2. On Error GoTo ErrorTrap 3. 4. With mdfMainForm.ActiveForm 5. sbrStatus.Top=frmTextEdit.ScaleHeight-tbrToolbar.Height-sbrStatus.Height 6. 7. .rtbRichText.Left=.ScaleLeft 8. .rtbRichText.Top=.tbrToolbar.Top+.tbrToolbar.Height 9. .rtbRichText.Height=.sbrStatus.Top-.rtbRichText.Top 10. .rtbRichText.Width=.ScaleWidth 11. End With 12. ErrorTrap: 13. End Sub
程序说明:本段代码主要用于实现当窗体大小变化时,自动改变RichTextBox控件的大小,使之随窗体大小一起改变。第1行窗体的Resize事件在窗体尺寸调整时被调用,第5行用于确定状态栏的位置,避免状态栏被RichTextBox控件覆盖。第7~10 行即RichTextBox控件的大小随窗体尺寸的变化而做相应调整。
1.4.5 文件菜单设计
“文件”菜单项主要用于实现新建、打开、另存和打印等功能,新建、打开功能的程序代码与父窗体中的基本相同。
1. 另存文本
另存文本的程序主要包括两大部分,分别是设置和显示“另存为”对话框和保存文件,程序代码如程序清单1-4所示。
程序清单1-4另存文本
1. Private Sub menuFileSaveAs_Click() '另存文本 2. On Error GoTo ErrorTrap 3. 4. '-----------------------“另存为”对话框设置--------------------------
5. cdlDialog.CancelError=True '设置“取消”为True 6. cdlDialog.Flags=cdlOFNHideReadOnly'隐藏“打印”对话框上的只读复选框 7. cdlDialog.Filter="Text Files"&"(*.txt)|*.txt" '设置文件过滤器 8. cdlDialog.FilterIndex=1 '设置默认过滤器 9. cdlDialog.Action=2 '显示“另存为”对话框 10. 11. '-----------------------保存文件-------------------------------------------- 12. If rtbRichText.Text<>""Then 13. rtbRichText.SaveFile cdlDialog.FileName,1 14. End If 15. 16. Exit Sub 17. ErrorTrap: 18. End Sub
程序说明:第5~9行是将CommonDialog控件设置为“另存为”对话框,第12~14行将文本编辑窗口中的文本保存到指定的路径下。
2. 打印文本
打印文本的程序主要包括两大部分,分别是设置和显示“打印”对话框和打印文件,程序代码如程序清单1-5所示。
程序清单1-5打印文本
1. Private Sub menuFilePrint_Click() '打印文本 2. On Error GoTo ErrorTrap 3. Dim intCopies As Integer '声明打印页数变量 4. 5. '-----------------------“打印”对话框设置-------------------------- 6. cdlDialog.CancelError=True '设置“取消”为True 7. cdlDialog.Flags=cdlPDAllPages '设置全部页选项按钮状态 8. cdlDialog.Action=5 '显示“打印”对话框 9. intCopies=cdlDialog.Copies '对打印页数变量赋值 10. o 11. '-----------------------打印文本----------------------------------------- 12. Printer.Print 13. rtbRichText.SelPrint Printer.Hdc '打印用户区内容 14. Printer.EndDoc '打印结束 15. 16. Exit Sub 17. ErrorTrap: 18. End Sub
程序说明:第6~9行是将CommonDialog控件设置为“打印”对话框,第12~14行将文本编辑窗口中的文本打印出来。
1.4.6 编辑菜单设计
“编辑”菜单项包含剪切、复制、粘贴和查找功能,文本的剪切、复制和粘贴是通过剪贴板Clipboard对象实现的。
1. 复制文本
以复制文本为例,通过RichTextBox控件的SelText属性返回被反白选中的文本,然后使用Clipboard对象的SetText方法将SelText属性返回的文本写入到Clipboard中。复制文本的程序代码如程序清单1-6所示。
程序清单1-6复制文本
1. Private Sub menuEditCopy_Click() '复制 2. If rtbRichText.SelText<>""Then '如果选中了文本就复制 3. Clipboard.SetText rtbRichText.SelRTF,vbCFRTF '将文本复制到剪贴板上 4. End If 5. End Sub
程序说明:第2 行使用RichTextBox控件的SelText属性判断是否有文本被选中,第3行将文本复制到剪贴板上。
剪切文本的程序代码与此类似。所不同的是,用SetText方法将被选中的文本写入到Clipboard后,设置SelText属性值为空即可。
2. 粘贴文本
Clipboard对象的GetText方法可以从Clipboard中取得文本数据,RichTextBox控件的SelRTF属性可用于设置当前选择的文本。将GetText方法取得的文本数据赋给SelRTF属性,即可将被选中的文本替换为Clipboard中的数据,即实现粘贴功能。粘贴文本的程序代码如程序清单1-7所示。
程序清单1-7粘贴文本
1. Private Sub menuEditPaste_Click() '粘贴 2. rtbRichText.SelRTF=Clipboard.GetText(vbCFRTF) 3. End Sub
程序说明:第2行即通过RichTextBox控件的SelRTF属性将剪贴板上的文本粘贴到文本中。
如果用户当前没有选择任何文本,那么上述粘贴文本的程序将向光标位置插入Clipboard中的数据。
细心的读者也许会想到,本章设计的文本编辑器是否可以通过快捷键Ctrl+C、Ctrl+X和Ctrl+V来分别实现复制、剪切和粘贴功能,答案是肯定的。在表1-2中,并没有对这三个操作设置快捷键。因为RichTextBox控件可以直接响应上述三个快捷键操作,如果在菜单设计的时候设置了相同的快捷键,那么程序会响应两次,从而相同的文本可能会被粘贴两次。
3. 查找文本
查找是使用RichTextBox控件的Find方法实现的。设计思路是通过InputBox函数返回用户输入的欲查找的文字,再将该文字作为Find方法的参数,从指定的位置开始查找其所在的位置。查找文本的程序代码如程序清单1-8所示。
程序清单1-8查找文本
1. Private Sub menuEditFind_Click() '查找文本 2. Dim strFindString As String 3. 4. strFindString=InputBox("输入要查找的字符","查找",rtbRichText.SelText) 5. rtbRichText.SelStart=0 6. rtbRichText.Find strFindString,,Len(rtbRichText.TextRTF) 7. End Sub
程序说明:第4行使用InputBox函数输入要查找的文本。第5行设置从什么位置开始查找,本段代码是从头开始查找。第6行返回查找到的文本。
在很多文本编辑软件中,都可以通过弹出式菜单进行编辑操作。本章也对作为用户区的RichTextBox控件设计了弹出式编辑菜单。在RichTextBox控件的MouseDown事件中使用PopupMenu方法即可实现。
1.4.7 格式菜单设计
“格式”菜单项包含字体和颜色设置功能,字体和颜色设置都是通过CommonDialog控件实现的。
文字的粗体、倾斜和下画线属性需要同时反映在“字体”对话框和工具栏相关按钮上。打开“字体”对话框时,需要将当前文字字体属性值赋给CommonDialog控件的相应属性,使得对话框打开后显示当前文字的状态。给CommonDialog控件属性赋值程序代码如程序清单1-9所示。
程序清单1-9字体
1. Private Sub menuFormatFont_Click() '设置文本字体 2. On Error GoTo ErrorTrap 3. 4. '-----------------------“字体”对话框设置---------------------------------------- 5. cdlDialog.CancelError=True '设置“取消”为True 6. cdlDialog.Flags=cdlCFEffects Or cdlCFBoth '设置对话框具有下画线和颜色效果等 7. 8. '将RichTextBox控件中文本属性赋给CommonDialog控件 9. '如果当前选择了文本,将文本的字体属性赋给CommonDialog控件 10. If rtbRichText.SelText<>""Then 11. cdlDialog.FontName=rtbRichText.SelFontName 12. cdlDialog.FontSize=rtbRichText.SelFontSize 13. cdlDialog.FontBold=rtbRichText.SelBold 14. cdlDialog.FontItalic=rtbRichText.SelItalic 15. cdlDialog.FontUnderline=rtbRichText.SelUnderline 16. cdlDialog.FontStrikethru=rtbRichText.SelStrikeThru 17. 18. '如果当前没有选中任何文本,将光标处的字体属性赋给CommonDialog控件 19. Else 20. cdlDialog.FontName=rtbRichText.Font.Name 21. cdlDialog.FontSize=rtbRichText.Font.Size 22. cdlDialog.FontBold=rtbRichText.Font.Bold 23. cdlDialog.FontItalic=rtbRichText.Font.Italic
24. cdlDialog.FontUnderline=rtbRichText.Font.Underline 25. cdlDialog.FontStrikethru=rtbRichText.Font.Strikethrough 26. End If 27. 28. cdlDialog.Action=4 '显示“字体”对话框 29. 30. '-----------------------设置RichTextBox控件中字体---------------------------- 31. '如果当前选择了文本,更新文本字体 32. If rtbRichText.SelText<>""Then 33. rtbRichText.SelFontName=cdlDialog.FontName 34. rtbRichText.SelFontSize=cdlDialog.FontSize 35. rtbRichText.SelBold=cdlDialog.FontBold 36. rtbRichText.SelItalic=cdlDialog.FontItalic 37. rtbRichText.SelUnderline=cdlDialog.FontUnderline 38. rtbRichText.SelStrikeThru=cdlDialog.FontStrikethru 39. 40. '如果当前没有选中任何文本,更新光标处的字体属性 41. Else 42. rtbRichText.Font.Name=cdlDialog.FontName 43. rtbRichText.Font.Size=cdlDialog.FontSize 44. rtbRichText.Font.Bold=cdlDialog.FontBold 45. rtbRichText.Font.Italic=cdlDialog.FontItalic 46. rtbRichText.Font.Underline=cdlDialog.FontUnderline 47. rtbRichText.Font.Strikethrough=cdlDialog.FontStrikethru 48. End If 49. 50. '-----------设置工具栏上的三个字体风格按钮的状态,弹起或按下---------- 51. '设置工具栏上的粗体按钮风格 52. If rtbRichText.SelBold=True Then 53. tbrToolbar.Buttons.Item(14).Value=tbrPressed 54. blnBold=True 55. Else 56. tbrToolbar.Buttons.Item(14).Value=tbrUnpressed 57. blnBold=False 58. End If 59. 60. '设置工具栏上的斜体按钮风格 61. If rtbRichText.SelItalic=True Then 62. tbrToolbar.Buttons.Item(15).Value=tbrPressed 63. blnItalic=True 64. Else 65. tbrToolbar.Buttons.Item(15).Value=tbrUnpressed 66. blnItalic=False 67. End If 68. 69. '设置工具栏上的下画线按钮风格 70. If rtbRichText.SelUnderline=True Then 71. tbrToolbar.Buttons.Item(16).Value=tbrPressed 72. blnUnderline=True 73. Else 74. tbrToolbar.Buttons.Item(16).Value=tbrUnpressed 75. blnUnderline=False 76. End If
77. 78. Exit Sub 79. ErrorTrap: 80. End Sub
程序说明:在设置字体属性时,用户可能选择了部分文字,此时要将该文字字体属性赋给CommonDialog控件;用户也可能没选择任何文字,此时要将光标处的字体属性赋给CommonDialog控件。第10~26行就是将选中文本或光标处的字体属性赋给CommonDialog控件,第32~48行是设置用户区文本字体程序,其功能正好与设置CommonDialog属性相反,第50~76行主要是在设置了字体属性后,更新工具栏上按钮的状态,弹起或按下。
颜色设置比较简单,只要将CommonDialog控件的Color属性赋给RichTextBox控件的SelColor属性即可。
1.4.8 窗口菜单设计
“窗口”菜单项主要用于设置子窗体在父窗体中的排列方式,有层叠排列、水平平铺、垂直平铺和重排图标四种。程序代码如程序清单1-10所示。
程序清单1-10子窗体排列
1. Private Sub menuCascade_Click() 2. mdfMainForm.Arrange vbCascade 3. End Sub 4. 5. Private Sub menuTileHorizontal_Click() 6. mdfMainForm.Arrange vbTileHorizontal 7. End Sub 8. 9. Private Sub menuTileVertical_Click() 10. mdfMainForm.Arrange vbTileVertical 11. End Sub 12. 13. Private Sub menuArrangeIcons_Click() 14. mdfMainForm.Arrange vbArrangeIcons 15. End Sub
程序说明:第2行是层叠排列所有非最小化的子窗体,第6行是水平平铺所有非最小化的子窗体,第10行是垂直平铺所有非最小化的子窗体,第14行是重排最小化子窗体的图标。
1.4.9 工具栏和状态栏设计
工具栏的大部分功能与菜单栏相同。在工具栏按钮的Click事件中调用相应的菜单项Click事件即可,而不需要单独编写响应程序。
文本的对齐方式可以通过设置RichTextBox控件的SelAlignment属性值实现。SelAlignment设置为0、1和2时,文本分别左对齐、右对齐和居中对齐。设置文本对齐时,选中的文本行或光标所在的行会执行相应的动作。
状态栏主要用于显示大写、被打开文件的保存路径和系统时间等,通过设置StatusBar控件的属性即可实现。