用C++ Builder3 制作NotePad（记事本）续篇
上回说到“我的记事本”小功告成，copy给朋友一份，咦，不能用，说是缺文件？噢，像VB的VBRUN??? .dll一样，C++ Builder3编译的程序也需要运行库的支持的。找到并打开它看看我们还有些什么没有做？首先是关于菜单的快捷键问题。如果你是使用我上次所给的资源脚本导人的菜单，你会发现带有快捷键的菜单项的文字都像“撒消(U)\tCtrl+Z”一样。以前用BC的资源编辑器做过菜单的朋友都知道，“\t”表示右对齐，而在C++ Builder3中也作为菜单的文字了。另外C++ Builder3中的快捷键的建立也简单的多，快捷键已经作为菜单的ShortCut属性了。这里，仅改动“编辑(E)”中的“撒消(U)Ctrl+Z”项来示众，其余皆可依样而画葫芦之。 先双击MainMenu1菜单组件，进入菜单编辑器，选中“编辑(E)”中的“撤消(U)Ctrl+Z”项。接着将Caption属性改为“撤消(U)”（即把“\tCtrl+Z”删去），并在shortCut瞩性中选择Ctrl+Z。这样，该项的快捷键就设完了。接着是编辑菜单里的各个项。首先是“撤消(&U)Ctrl+Z”到“全部选定(&A)”六项（必须先将他们的Enabled属性都设为true)，C++ Builder3为这些功能提供了现成的方法（Method）。比如“撤消(&U)Ctral+Z”菜单，可以使用Undo方法，即在该菜单的OnClick事件处理函数中输入“Memo1->Undo();”。同理有： “剪切(&T)\tCtrl+X”项，输入：“Memo1->CutToClipboard();” “复制(&C)\tCtrl+C”项，输入：“Memo1->CopyToClipboard();” “粘贴(&P)\tCtrl+V”项，输入：“Memo1->PasteFromClipboard();” “删除(&L)\tDel”项，输入：“Memo1->ClearSelection();” “全部选定(&A)”项，输入：“Memo1->SelectAll();” 下面是“时间/日期(&D)F5”项，我们必须先取得当前的日期和时间。有两种方法，一种是用getdate和 gettime函数，一种是用C++ Builder3中的TDateTime类。这里选择后一种，但有时使用getdate和gettime函数会更简便。下面给出该菜单项的处理代码： TDateTime * Current_DateTime =newTDateTime; //生成TDateTime对象 * Current_DateTime=Current_DateTime->CurrentDateTime(); //获取当前日期和时间 Memo1->SetselTextBuf(Current_DateTime->DateTimeString().c_str());//将当前日期和时间转换成字符串，并用SetSelTextBuf方法插入当前光标位置 接着是处理‘自动换行(&W)”的代码： //W1是“自动换行(&W)”菜单项的名字(Name)属性，如果你不是W1，请做相应的改动 if(W1->Checked) //该菜单已经检取（Checked） {W1->Checked=false; //取消检取 Memo1->ScrollBars=ssBoth; //Memo1两根滚动条都要 } else { W1->Checked=true; //设置检取 Memo1->ScrollBars = ssVertical; //Memo1只设垂直滚动条，效果就是自动换行 } 接下来处理“设置字体(&F)...”菜单，我们首先需要一个设置字体的公用对话框。从Dialogs组件条中选FontDialog组件放入表单中。下面是处理“设置字体(&F)...”的代码： if(FontDialog1->Execute())Memo1->Font=FontDialog1->Font; 至此，“编辑(E)”菜单的功能都处理完了，下面将是处理“搜索(S)”菜单的工作。其中，我们又将用到两个公用对话框。C++ Builder3只提供了用来获取用户欲查我的字符串的对话框，并未提供具体查找的函数，所以我们得自己写处理函数。下面我给出修改的Win32 SDK的范例代码，你需要将这些代码插在程序的首部，即定义全局变量的地方。 typedef enum { frtFind frtReplace, frtRepAll } FRT #define GETSEL(ichS,ichE) \ SendMessage(MainForm - > Memo1 - > Handle, EM_GETSEL,(WPARAM) & ichs, (LPARAM) & ichE) #define SETSEL(ichS,ichE) \SendMessage(MainForm - > Memo1 - > Handle, EM_SETSEL, (WPARAM) ichS, (LPARAAM) ichE) BOOL FWholeWord(char * sz, char * szStart, char * szEnd, int cch) { if ( sz! = szStart & & * (sz - 1 ) ! = ' ' & & * (sz - 1 ) ! = '\t' & & * (sz - 1 ) ! = ',' & & * (sz - 1 ) ! = '.' & & * (sz - 1 ) ! = '\r' ) { return FALSE; } if ( sz + cch! = szEnd & & * (sz + cch) ! = ' ' & & * (sz +cch) ! = '\t' & & * (sz + cch) ! = ',' & & * (sz + cch) ! = '.' & & * (sz + cch) ! = '\r' ) { return FALSE; } return TRUE; } BOOL Find(char * szFind, BOOL fDown, BOOL fMatchCase, BOOL fWholeWord, BOOL fNoScroll) { char * szText; UINT ichStart; UINT ichEnd; UINT cch; int dch; int dchComp; char * szTerm; char * sz; UINT cchFind; BOOL fFound = FALSE; int dcbOnFind; szText = MainForm - > Memo1 - > Lines - > GetText(); cchFind = strlen (szFind); GETSEL(ichStart, ichEnd); cch = strlen(szText); if ( ichEnd - ichStart = = cchFind & & !strnicmp(szText + ichStart, szFind, cchFind) ) dchOnFind = 1; else dchOnFind = 0; if (fDown) { dch = 1; dchComp = 0; sz = szText + ichStart + dchOnFind; szTerm = szText + cch; } else { dch = -1; dchComp = 1; sz = szText + ichStart - dchOnFind; szTerm = szText; } for (; sz + dchComp! = szTerm; sz + = dch) { if ( tolower(* sz) = = tolower( * szFind) & & (fMatchCase ? !strncmp(sz, szFind, cchFind) : !strnicmp (sz, szFind, cchFind) ) & & ( !fWholeWord || FWholeWord(sz, szText, szTerm,cchFind))) { SendMessage(MainForm -> Memo1 -> Handle,EM_SETSEL, sz-szText, sz-szText + cchFind); if (!fNoScroll) SendMessage(MainForm - > Memo1 - > Handle,EM_SCROLLCARET, 0, 0L); fFound = TRUE; break; } } if ( !fNoScroll & & !fFound) MessageBox(NULL, szFind, "字 符 串 没 有 找 到",MB_OK); return fFound; } VOID Replace(char * szReplace) {SendMessage(MainForm - > Memo1 - > Handle, EM_REPLACESEL, 0, (LPARAM) (LPSTR) szReplace); } VOID ReplaceAll(char * szFind, char * szReplace, BOOLfMatchCase, BOOL fWholeWord) { UINT ichStart; UINT ichEnd; GETSEL(ichStart, ichEnd); SETSEL(0, 0); while(Find(szFind, TRUE, fMatchCase, fWholeWord,TRUE)) { Replace (szReplace); } SETSEL(ichStart, ichEnd); } VOID FindReplace( char * szFind, char * szReplace, FRT frt, BOOL fDown, BOOL fMatchCase, BOOL fWholeWord) { switch (frt) { case frtFind: Find(szFind, fDown, fMatchCase, fWholeWord,FALSE); break; case frtReplace: Find(szFind, fDown, fMatchCase, fWholeWord,FALSE); Replace (szReplace); break; case frtRepAll: ReplaceAll(szFind, szReplace, fMatchCase, fWholeWord); } } 这些代码本来并不是给C++ Builder用的，但我们只要稍加改动就能使用了，这也为我们移植以前的代码提供了新思路，即一般只要将过去程序中的窗口或控制的句柄（HWND）用C++Builder中表单或组件的Handle属性来代替，将过去程序中的程序事例句柄用HInstance代替，就能方便的完成移植。回到程序中来，上面有5个函数和2个宏，其实我们直接使用的只有FindReplace函数。先在表单上放上FindDialog和ReplaceDialog两个对话框，随后写“查找(F)...”菜单项的处理代码： FindDialog1->Execute(); 你可能会很奇怪，这么简单，那查找的过程在哪里呢？当用户按了查找对话框的[查找下一个(F)]按钮后，会产生FindDialog的OnFind事件，在该事件的处理函数中进行查找工作。如何编写组件的事件函数？ （1）在表单编辑区选中该组件（这里是FindDialog1）； （2）在对象观察器（如果没有显示，按F11调出）中选择Event（事件），系统将列出事件； （3）双击事件后面的编辑框（这里是OnFind），系统将切换入代码编辑区； （4）输入事件处理代码。 下面给出OnFind的处理代码： MainForm - > SerFocus(); FindReplace(FindDialog1 - > FindText.c_str(), "", frtFind, FindDialog1 - > Options. Contains(frDown), FindDiaiog1 - > Options.Contains(frMatchCase), FindDialog1 - > Options.Contains(frWholeWord)); 同样的，“替换(&R)”菜单项处理代码是： ReplaceDialog1 - > Execute(); ReplaceDialog1的onFind 事件的处理代码： MainForm - > SetFocus(); if(ReplaceDialog1 - > Options.Contains(frFindNext)) FindReplace(ReplaceDialog1 - > FindText.c_str(),"", frtFind, ReplaceDialog1 - > Options. Contains(frDown), ReplaceDialog1 - > Options. Contains(frMatchCase), ReplaceDialog1 - > Options. Contains(frWholeWord)); ReplaceDialog1的onReplace 事件（用户按了[替换(R)]按钮）的处理代码： MainForm - > SetFocus (); if(ReplaceDialog1 - > Options. Contains (frReplace)) FindReplace(ReplaceDialog1 - > FindText. c_str(), ReplaceDiaiog1 - > ReplaceText. c_str(), frtReplace, ReplaceDialog1 - > Options. Contains(frDown), ReplaceDialog1 - > Options. Contains(frMatchCase), ReplaceDialog1 - > Options. Contains(frWholeWord)); else if(ReplaceDialog1 - > Options. Contains (frReplaceAll))//全部替换 FindReplace(ReplaceDialog1 - > FindText.c_str(), ReplaceDialog1 - > ReplaceText, c_str(), frtRepAll, ReplaceDialog1 - > Options. Contains(frDown), ReplaceDialog1 - > Options. Contains(frMatchCase), ReplaceDialog1 - > Options. Contains(frWholeWord)); 当然，还有“查找下一个(N)”菜单项，处理的方法几乎和OnFind一样： FindReplace(FindDialog1 - > FindText. c_str(),"", frtFind, FindDialog1 - > Options. Contains (frDown), FindDialog1 - > Options. Contains (frMatchCase), FindDialog1 - > Options. Contains(frWholeWord)); 至此为止，终于真的“大”功告成了。至于“帮助(H)”菜单处理，你可以用WinHelp来显示一个帮助文 件，用MessageBox来显示“关于XXX”的对话框，这些都不怎么和C++ Builder有关，我就不赘述了。 最后还有些收尾工作，选择Project菜单Options，将显示一个程序设置的对话框。 1．选择Application页面，这里Title是程序在任务栏上的标题，默认的是程序文件名。我们将它设为 “我的记事本V1.00”。接着，单击[Load Icon...]按钮来为你的软件选择一个图标。 2．选择Compiler页面，单击[Release]按钮，这样就不会向程序中插入调试代码而加快执行速度。 3．选择Linker页面，取消对Use Dynamic RTL的选择，不使用动态链接库。 4．选择Packages页面，取消对Build with runtime packages的选择，不使用运行时包。 按OK退出后，选Project菜单中的Build NotePad来编译一下，你的第一个用C++ Builder3制作的称得上“软件”的程序终于诞生了。