Borland C++ Builder中MDI应用程序的设计 李明柱 MDI（多文档界面）应用程序是指用户可以同时打开多个文档，每一个文档显示在应用程序主窗口客户区的独立窗口中。如字处理程序Word就是一个典型的MDI应用程序，你可以在Word中同时打开和使用多个文档，PowerPoint中你也可以同时创建和打开多个幻灯片应用程序，电子表格中也允许用户同时对多个图表进行编辑。MDI应用程序一般包括三类窗口： * 框架窗口：框架窗口与应用程序的主窗口类似，有可改变大小的边框、标题栏、系统菜单等。应用程序必须为框架窗口注册一个窗口类。一个MDI应用程序一般只有一个框架窗口，框架窗口控制着其它各类窗口。 * 客户窗口：客户窗口提供对下属MDI子窗口的管理，每一个MDI应用程序有一个客户窗口。 * 子窗口：当用户打开或创建一个文档时，客户窗口便为该文档创建一个子窗口。每个子窗口都有可以改变大小的边框、标题栏、系统菜单、最小最大化按钮等。任何时刻只有一个子窗口是活动的。子窗口不能超出客户窗口的范围。 用一般的开发工具开发MDI应用程序相当麻烦：你必须注册框架窗口和子窗口类、创建框架窗口和客户窗口、写消息循环和回调函数、创建子窗口等等，其中的每一步都不是什么轻而易举之事。但是在Borland C++ Builder（以下简称CB）中实现MDI应用程序却相对简单，你只需设置一下窗体的属性FormStyle即可：把框架窗口的FormStyle设为fsMDIForm，子窗口的FormStyle设为fsMDIChild。另外CB本身也提供了MDI程序模板，选择File|New|Projects|MDI Application，如下图所示： CB就会产生一个MDI应用程序框架。直接编译运行该程序，就可以看到该程序已经具有一个MDI程序的基本特征了。如下图： 我们利用这个模板设计一个MDI应用程序。该程序是一个支持多文本格式和普通文本格式的多文档编辑器，且同时支持工具栏快捷按钮和打印输出。下面我们就来谈一谈其具体开发过程： （1） 选择File|New|Projects|MDI Application，产生MDI程序框架。 （2） 把主框架窗口另存为MDIMain.cpp，子窗口另存为MDIChild.cpp，工程名另存为MDI。 （3） 转到主框架窗口FrameForm，设置其Caption为“ MDI演示程序---可打开多文件的文本编辑器”，在Object Inspector中观察FrameForm的属性，可看到其FormStyle为fsMDIForm。改变主框架窗口FrameForm上的控件（Component）如下图所示： （4）点击菜单控件，改变菜单项和菜单项的OnClick事件如下，使之适合自己的要求： 菜单项 菜单项的OnClick事件 文件：新建 New1Click 打开 Open1Click 退出 Exit1Click 窗口：层叠 Tile1Click 平铺 Cascade1Click 排列图标 Arrangeicons1Click （5） 改变SpeedPanel上的TSpeedButton控件和事件响应，并在其属性Glyph中装入相应的位图图像： 快捷按钮名称 事件响应 SpeedButton1 New1Click SpeedButton2 Open1Click SpeedButton3 Exit1Click （6）修改原有的OpenDialog文件打开对话框控件的属性： 单击Filter属性，弹出Filter Editor对话框，输入： Rich Text Format（*.rtf） *.rtf Text Files（*.txt） *.txt All Files（*.*） *.* （7） 选择View|Project Manager，在Project Manager中单击MDIChild一栏的ChildForm，就可以查看该子窗体的FormStyle属性为fsMDIChild。设置窗体的事件响应： OnClick FormClick OnClose FormClose OnCloseQuery FormCloseQuery OnCreate FormCreate 拖动控件到ChildForm之上，设置好如下图所示界面： （8） 设置ChildForm上菜单控件的菜单项名称和菜单项的OnClick事件： 菜单项 菜单项的OnClick事件 文件：新建 New1Click 打开 Open1Click 关闭 Close1Click 保存 Save1Click 另存为 Saveas1Click 打印 Print1Click 打印设置 Printersetup1Click 退出 Exit1Click 编辑：剪切 Cut1Click 复制 Copy1Click 粘贴 Paste1Click 删除 Delete1Click 全选 Selectall1Click 其他：靠左 Left1Click 靠右 Left1Click 居中 Left1Click 自动换行 Wordwrap1Click 字体 Font1Click （9） 从Dialogs控件板选取对话框控件到ChildForm上，并设置其属性： * SaveDialog Name SaveFileDialog 单击Filter属性，弹出Filter Editor对话框，输入： Rich Text Format（*.rtf） *.rtf Text Files（*.txt） *.txt All Files（*.*） *.* * PrintDialog Name PrintDialog1 * PrinterSetupDialog Name PrinterSetupDialog1 * FontDialog Name FontDialog1 （10） 然后从Win32控件板中选择RichEdit控件，放在ChildForm上，设置其属性： Align alClient Name doc 点击Lines属性，弹出String list editor，清空里面的内容。 （11） 最后，在程序中加入程序源代码。运行该程序，然后打开两个文档，如下图所示（该程序在C++ Builder 3.0和Windows 95下调试通过）： 附源程序清单： （这儿略去大部分系统自己加进去的程序代码部分）： //-------- MDIMain.cpp-------- #include "MDIMain.h" #include "MDIChild.h" __fastcall TFrameForm::TFrameForm(TComponent* Owner) : TForm(Owner) { } void __fastcall TFrameForm::Exit1Click(TObject *Sender) {Close();//应用程序退出} void __fastcall TFrameForm::Tile1Click(TObject *Sender) {Tile();//窗口层叠} void __fastcall TFrameForm::Cascade1Click(TObject *Sender) {Cascade();//窗口平铺} void __fastcall TFrameForm::Arrangeicons1Click(TObject *Sender) { ArrangeIcons();//排列图标} //新建文档 void __fastcall TFrameForm::New1Click(TObject *Sender) { ChildForm = new TChildForm(this); } //打开文件 void __fastcall TFrameForm::Open1Click(TObject *Sender) { if(OpenFileDialog->Execute()){ ChildForm=new TChildForm(this); ChildForm->Open(OpenFileDialog->FileName); } } //----------MDIChild.cpp------------- #include "MDIChild.h" #include "MDIFrame.h" TChildForm *ChildForm; //定义缺省文件名为Untitled const AnsiString DefaultFileName = AnsiString("Untitled"); __fastcall TChildForm::TChildForm(TComponent* Owner) : TForm(Owner) { } //文件打开函数 void __fastcall TChildForm::Open(const AnsiString AFileName) { PathName = AFileName; Caption = ExtractFileName(AFileName); doc->Lines->LoadFromFile(PathName); doc->SelStart = 0; doc->Modified = false; } void __fastcall TChildForm::New1Click(TObject *Sender) { FrameForm->New1Click(Sender); //新建文件} void __fastcall TChildForm::Open1Click(TObject *Sender) { FrameForm->Open1Click(Sender);//打开文件 } void __fastcall TChildForm::Close1Click(TObject *Sender) { Close();//关闭打开的文件} void __fastcall TChildForm::Exit1Click(TObject *Sender) { FrameForm->Exit1Click(Sender);//退出应用程序 } //保存打开或创建的文档 void __fastcall TChildForm::Save1Click(TObject *Sender) { if(Caption == DefaultFileName){ Saveas1Click(Sender); } else{ doc->Lines->SaveToFile(PathName); doc->Modified = false; } } //另存打开或创建的文档 void __fastcall TChildForm::Saveas1Click(TObject *Sender) { SaveFileDialog->FileName = PathName; if (SaveFileDialog->Execute() ){ PathName= SaveFileDialog->FileName; Caption = ExtractFileName(PathName); Save1Click(Sender); } } //文档打印 void __fastcall TChildForm::Print1Click(TObject *Sender) { if (PrintDialog1->Execute()){ try { doc->Print(PathName); } catch(...){ Printer()->EndDoc(); throw; } } } void __fastcall TChildForm::Printersetup1Click(TObject *Sender) { PrinterSetupDialog1->Execute();//打开打印机设置对话框进行打印机设置} void __fastcall TChildForm::Cut1Click(TObject *Sender) {doc->CutToClipboard();//文档剪切到剪贴板} void __fastcall TChildForm::Copy1Click(TObject *Sender) { doc->CopyToClipboard();//文档拷贝到剪贴板} void __fastcall TChildForm::Paste1Click(TObject *Sender) { doc->PasteFromClipboard();//从剪贴板粘贴文档} void __fastcall TChildForm::Delete1Click(TObject *Sender) { doc->ClearSelection();//删除选中的文档} void __fastcall TChildForm::Selectall1Click(TObject *Sender) { doc->SelectAll();//文档全选} //文档向左、向右、居中等各种对齐方式响应函数 void __fastcall TChildForm::Left1Click(TObject *Sender) { Left1->Checked = false; Right1->Checked = false; Center1->Checked = false; if( dynamic_cast<TMenuItem *>(Sender) !=0) dynamic_cast <TMenuItem *>(Sender) ->Checked = true; { if (Left1->Checked ) doc->Paragraph->Alignment = taLeftJustify; else if (Right1->Checked) doc->Paragraph->Alignment= taRightJustify; else if (Center1->Checked) doc->Paragraph->Alignment = taCenter; } } //文档自动换行 void __fastcall TChildForm::Wordwrap1Click(TObject *Sender) { doc->WordWrap = !doc->WordWrap; //toggle word wrapping if (doc->WordWrap) doc->ScrollBars = ssVertical; else doc->ScrollBars = ssBoth; Wordwrap1->Checked = doc->WordWrap; // set menu item check } //设置选中文档的字体 void __fastcall TChildForm::Font1Click(TObject *Sender) {FontDialog1->Font= doc->Font; if (FontDialog1->Execute()) doc->SelAttributes->Assign(FontDialog1->Font); } //子窗口鼠标单击响应函数 void __fastcall TChildForm::FormClick(TObject *Sender) { bool HasSelection; Paste1->Enabled = Clipboard()->HasFormat(CF_TEXT); HasSelection = doc->SelLength > 0; Cut1->Enabled = HasSelection; Copy1->Enabled = HasSelection; Delete1->Enabled = HasSelection; } //子窗口关闭时事件响应 void __fastcall TChildForm::FormClose(TObject *Sender, TCloseAction &Action) {Action=caFree; } //子窗口关闭时询问事件响应 void __fastcall TChildForm::FormCloseQuery(TObject *Sender, bool &CanClose) { if (doc->Modified) { TMsgDlgButtons temp_set; temp_set<< mbYes<<mbNo <<mbCancel; String buffer = "Save changes to " + PathName; switch(MessageDlg(buffer, mtConfirmation,temp_set,0)) { case mrYes: Save1Click(this); break; case mrCancel: CanClose=false; break; } }} void __fastcall TChildForm::FormCreate(TObject *Sender) { PathName = DefaultFileName; } //---------MDIChild.h------------ class TChildForm : public TForm { private: // User declarations AnsiString PathName;//文档文件和路径名定义 public: // User declarations void __fastcall Open(const AnsiString AFileName);//文档打开函数声明 virtual __fastcall TChildForm(TComponent* Owner); }; 另外，你自己也可以根据上述开发思路来开发出各种各样的MDI程序，比如，你可以在文档中利用OLE加入图片，声音等多媒体因素，使你的文档更加丰富多彩！ （全文完） 作者通信地址： 西安电子科技大学295信箱 邮编：710071 李明柱 电话：（029）8207394(宿舍)，（029）8201284(实验室) email: mzli@263.net