用C++ Builder3 制作NotePad（记事本）
启动C++ Builder3后，在File菜单中选New APPlication来建立一个新程序。这时，显示在屏幕中间的窗口的标题是Form1，这个窗口是程序的主表单（就是主窗口的意恩），它将在程序启动时自动显示。而以后再创建的表单（Form）一般需要用Show()方法（Method）来显示。和组件一样，系统为第一个表单起名为Form1，以后的表单起名为Form2、Form3等等，标题（Caption）也是如此。 当然，你可以将它们改成你喜欢的名字。我们先来把Form1的名字（Name）属性设为MainForm，把MainForm的标题（Caption）属性设为“Untited.txt--我的记事本”。接着可以用鼠标拖动窗口的位置和改 变窗口的大小。以上的这些工作，几乎每个程序都需要如法炮制一番。 随后，我们要开始NotePad的编程工作了。知己知彼，百战不殆，先来分析一下MicroSoft NotePad的结构。其实NotePad是附件里最简单的程序了，它的主窗口中只包含一个占据全部用户区域（Client Area）的多行的编辑控制（Edit）。由于编辑控制最多容纳64KB文字（Windows95），所以NotePad只能打开小于64KB的文件。好，我们来依样画葫芦，选择Standard中的多行编辑（Memo）组件（而不是单行编辑组件）。 放入表单中，将多行编辑Memo1的Align属性设为alClient，这时Memo1撑满了整个窗口，Memo1中显示的是Memo1。选择Memo1的Lines属性，[TString]后面出现了一个按钮。 单击该按钮，将出现一个设置Lines属性的对话框，其中第一行便是“Memo1”。多行编辑（Memo）组件的Lines属性在设计时（Design Time）设定的内容将在该窗口创建时自动显示。由于我们不需要显示什么，所以把“Memo1”擦掉，选OK退出。你一定想看看目前的效果，运行一下吧。编译……连接……启动程序……怎 么样，是不是和NotePad一模一样，能打字、删字、删行，还能使用右键菜单来复制（copy）和粘贴（paste）。 本来就是嘛，用一样的编辑控制，不一样才怪呢。哎，不对，好像字体小了点，而且没有滚动条。没有关 系，关闭程序回到C++ Builder3中，先修改Memo1的字体（Font）属性，选择system字体（当然你爱选什么字 体都可以），然后设置滚动条（ScrollBars）属性，选择ssBoth（两根滚动条都要）。在这些改动之后，先别急着运行，windows的程序没菜单是不行的。在C++ Builder中，菜单也是组件之一。选择Standard中的主菜单（MainMenu）组件放入表单中。主菜单（MainMenu）组件显示为一个图标。顺便提一句菜单，对话框或系统功能之类组件属于非可视组件（nonvisual components），它们的工作基本不是用来满足视觉的，或者在程序运行时根本就看不见（相对于界面上可视组件来说），因此你可以将非可视组件放在表单中的任意位置。 接下来的工作是制作菜单，双击主菜单MainMenu1的图表，会弹出菜单编辑窗口，开始时是一个空菜单。按右键会弹出一个菜单（不是我们要做的菜单），其中： Insert（添加一个菜单项，按Insert键完成相同的功能） Delete（删除一个菜单项，按Delete键完成相同的功能） Create SubMenu（添加一个子菜单，按Ctrl + →键完成相同的功能） Select Menu（选择一个菜单，用来关闭当前编辑的菜单并选择另一个菜单来编辑） Save as Template（将当前菜单保存为模板，供以后使用） Insert From Template（将已有的菜单模板加入当前菜单） Delete Template（删除菜单模板） Insert From Resource（从资源文件中取得菜单加入当前菜单） 制作“我的记事本”的菜单有一种偷懒的方法，就是照抄Microsoft记事本的菜单。你可以用BC5.02的IDE打开Notepad.exe（先把打开文件对话框中的Viewer设为Edit Resource，再把文件类型设为Exe cutable（*.exe），这样才是以编辑资源的方式打开EXE文件，否则默认的打开方式是Debug调试程序），将里面的菜单导出以RC（资源脚本）方式存盘，然后用Insert From Resource导入该菜单；一共用不了2分钟。 还有一种方法就是自己一项一项慢慢地做。当然，不管你用什么方法，最后都要做出菜单来才能继续下面的工作。下面附上从Windows NT4.0中Notepad导出的菜单的资源脚本menu.rc，把下面的文件以menu.rc存盘就能用C++ Builder3来导入了： 1 MENU { POPUP "文件(&F)" { MENUITEM "新建(&N)", 9 MENUITEM "打开(&O)...", 10 MENUITEM "保存(&S)", 1 MENUITEM "另存为(&A)...", 2 MENUITEM SEPARATOR MENUITEM "页面设置(&T)...", 32 MENUITEM "打印(&P)", 14 MENUITEM SEPARATOR MENUITEM "退出(&X)", 28 } POPUP "编辑(&E)" { MENUITEM "撤消(&U)\tCtrl+Z", 25 MENUITEM SEPARATOR MENUITEM "剪切(&T)\tCtrl+X", 768,GRAYED MENUITEM "复制(&C)\tCtrl+C", 769, GRAYED MENUITEM "粘贴(&P)\tCtrl+V", 770，GRAYED MENUITEM "删除(&L)\tDel",771, GRAYED MENUITEM SEPARATOR MENUITEM "全部选定(&A)", 7 MENUITEM "时间/日期(&D)\tF5",12 MENUITEM SEPARATOR MENUITEM "自动换行(&W)", 27 MENUITEM "设置字体(&F)...", 37 } POPUP "搜索(&S)" { MENUITEM "查找(&F)...", 3 MENUITEM "查找下一个(&N)\tF3", 8 MENUITEM "替换(&R)"', 40 } POPUP "帮助(&H)" { MENUITEM "帮助主题(&H)", 5 MENUITEM SEPARATOR MENUITEM "关于记事本(&A)...",11 } } 关于菜单，还有两点要提： 1．一般没有必要去改动菜单项的Name属性，使用系统给出的默认名就可以了。 2．以“-”为标题的菜单项即是菜单分割条，而菜单项的Break属性提供的分割则是一种很奇怪的样子--“垂直分害条”（群山笑：这个名词是我自已加上的）。 OK，到此为止，界面部分的工作已经全部完成了，接下来便是代码部分的编写。首先要做的就是打开文件和存盘的工作。我们来建立一个char *UserFileName的全局变量来存放文件名。按工具条上的Toggle Form/Uint将工作窗口切换到代码编辑区。这个按钮用于表单编辑区和代码编辑区的相互切换，按F11显示对象观察器。在“TMainForm *MainForm；”下加语句： char *UserFileName="untitled.txt"；//默认文件名Untitled.txt BOOL If_saved=TRUE；//表示文件存盘与否 char title_buf[256]；//标题的内容 接着，我们再切换回表单编辑区，从Dialogs组件条中选SaveDialog组件放入表单中。 SaveDialog组件就是用来显示Save As对话框的，当然用Windows API也能实现此功能，这里将使用SaveDialog组件。将SaveDialog1的DefaultExt（默认的扩展名）属性设为.txt，这表示如果用户没有加扩展名，系统会自动加上“.txt”。按Filter属性后的[…]按钮，在弹出的对话框中输入“文本文件”“*.txt”“所有文件”“*.*”。另外再双击Options属性，将展开后其中的ofOverwritePrompt（文件覆盖提醒）属性设为true。接着在表单编辑区的菜单中选菜单项“另存为（A）...”，这时C++ Builder3会自动切换到代码编辑区，而光标停在一个函数的开头。在该例中菜单项“另存为（A）…”的名字属性是A1，所以这个函数的名字是A1Click（TObject * Sender），表示用户选择（Click）这个菜单项而执行的函数。我们所要做的是弹出Save As对话框来获得用户输入的文件名，并将这个文件存盘。所以在这个函数里加如下的语句： if(!SaveDialog1->Execute()) return; //如果用户选择了取消，我们就return UserFileName = SaveDialog1 -> FileName.c_str(); / * FileName保存了用户输入的文件名，但是AnsiString类型的，用其中的c_str()方法可以将其转换成字符串*/ Memo1 ->Lines->SaveToFile（UserFlleName）； If_saved=TRUE; //已经存盘 sprintf(title_buf,"% s - 我的记事本”, UserFileName); /* 设置窗口的标题，记得要加上include <stdio.h> * / Caption=title_buf; / * Memo中的Lines属性即是编辑控制中的内容，Lines属性是Strings类型的，用其中提供的SaveToFile方法可以方便地将编辑控制中的内容存盘 */ 类似地，我们再来处理打开文件的工作。首先，在表单中放置一个OpenDialog组件，DefaultExt和Filter 属性同SaveDialog1，将Options属性中的ofFileMustExist（文件必须存在）设为true。然后在表单编辑区中 的菜单中选菜单项“打开(O)...”，在代码编辑区中输入如下语句。 if(!if_saved) if(Application ->MessageBox("文件还未保存，是否要保存？","我的记事本", MB_OKCANCEL+MB_DEFBUTTON1+MB_ICONWARNING)==ID_OK) S1Click(NULL); / * 如果文件没有保存，我们得先征求用户是否要保存文件。s1Click()是“保存(s)”菜单项的处理函数（就是接下来处理的菜单项），“保存(S) ”菜单项的工作就是将文件存盘，所以我们没有必要写重复的代码，而只要直接调用该函数就可以了。注意，如果你的“保存(S)”菜单项的名字属性不是S1而是其他，那么这个函数的名字也会有所不同，但总是“名字”+Click() */ if(!OpenDialog1->Execute())return; UserFileName=OpenDialog1->FileName.c_str(); Memo1->Lines->LoadFromFile(UserFileName); sprintf(title_buf,"% s - 我的记事本", UserFileName); Caption = title_buf; （为节省篇幅，以下凡是重复的操作描述省略） 处理“保存(S)”菜单项，输入： Memo1->Lines->SaveToFile(UserFileName); If_saved=TRUE; sprintf(title_buf,"% s - 我的记事本", UserFileName)"; Caption = title_buf; 处理“新建(N)”菜单项，输入如下语句： lf(!If_saved) if(Application ->MessageBox ("文件还未保存，是否要保存？","我的记事本", MB_OKCANCEL+MB_DEFBUTTON1+MB_ICONWARNING)==ID_OK)S1Click(NULL); sprintf(UserFileName,"Untitled.txt"); sprintf(title_buf, "% s - 我的记事本-未存盘",UserFileName); Caption=title_buf; Memo1->Lines->Clear(); //将编辑控制中的内容清空 随后，我们要处理Memo1的OnChange事件（Events）。一旦编辑控制中的内容有改变，Memo1就将产生OnChange事件，这时需要将If_saved置为FALSE，表示文件已经改动过了，并在标题上显示“未存盘”。在对象观察器中选Memo1，再单击下面的Events，将列出许多事件，第一个就是OnChange，双击后面的编辑框，后面的操作和处理菜单项是一样的（其实前面处理的菜单项就是处理菜单的OnClick事件），输入： If_saved=FALSE; sprintf(title_buf,"% s -我的记事本-未存盘", UserFileName); Caption=title_buf; 随后处理MainForm的OnClose事件，输入： //OnClose事件在关闭窗口时产生，需要做一些收尾工作 if(!If_saved) if(application->MessageBox("文件还未保存，是否要保存？","我的记事本", MB_OKCANCEL+MB_DEFBUTTON1+MB_ICONWARNING)==ID_OK)S1Click(NULL); 最后，处理“退出(X)”菜单项，输入如下语句： if(!If_saved) if(application->MessageBox（"文件还未保存，是否要保存？","我的记事本", MB_OKCANCEL+MB_DEFBUTTON1+MB_ICONWARNING)==ID_OK)S1Click(NULL); Application->Terminate(); //程序结束 到此为止，终于小功告成了，快把项目保存下来吧。在File菜单中选Save Project As…，首先弹出的对 话框要求输入表单的文件名(不能和表单的名字属性相同)，我们就输入Main。随后弹出的对话框才是要求输入项目（Project）的文件名，该文件名也就是exe文件的名字，我们输入NotePad。保存后再选Project菜单 里的Make NotePad，或按Ctrl+F9，就会产生一个NOtePad.exe，这就是我们自己做的记事本，目前它已经能打开文件、编辑并保存。你一定发现菜单中还有许多功能尚未实现，这就是我只说小功告成的原因，不过 下次我们会完成它。 附件中除了记事本（NotePad），还有一个写字板（WordPad）。它们最大的区别在于前者使用的是编辑 控制（Edit），而后者使用的是Rich Edit控制（有些书翻译成丰富文本控制）。与编辑控制相比，Rich Edit除了能给文本加上各种修饰，还支持简单的对象链接和嵌入技术（Object Linking and Embedding）。 此外，Microsoft Win32 SDK的文档中说Rich Edit控制能接受的文本大小将只受内存大小的限制，这比 编辑控制的64KB强多了。当然，效率又是另一个问题。我曾试图用写字板（WordPad）打开一个15MB的文件，硬盘读了10多分钟还没结果，只能按Reset重新启动了（我可有64MB内存哦）。自然使用Rich Edit控制也比编辑控制难的多。在以后讲到RichEdit组件时，详细讲述它的用法。