| 角色 | 類別/屬性 | 職責 |
|---|---|---|
| 框架視窗 | FormStyle = fsMDIForm | 主視窗:選單列、工具列、狀態列、管理子視窗 |
| 客戶區視窗 | 系統自動建立 (MDIClient) | 容納所有子視窗,處理平鋪/層疊/排列命令 |
| 子視窗 | FormStyle = fsMDIChild | 文件視窗:各自獨立的編輯區、標題列、系統選單 |
File → New → Projects → MDI ApplicationMainForm (fsMDIForm) + ChildForm (fsMDIChild)。TMainMenu:檔案(新建/開啟/儲存/另存/列印/結束)、編輯(剪貼)、視窗(平鋪/層疊/排列/清單)、說明。TToolBar + TActionList:對應選單命令,Action 統一邏輯。TStatusBar:顯示游標位置、修改狀態、編碼格式。
// ChildForm.h
__published:
TRichEdit *RichEdit1; // 支援 RTF/純文字
TActionList *ActionList1;
// 儲存/列印等 Action
private:
String FFileName;
bool FModified;
public:
__fastcall TChildForm(TComponent* Owner);
bool __fastcall LoadFile(const String &fileName);
bool __fastcall SaveFile(const String &fileName = "");
void __fastcall UpdateTitle();
void __fastcall TMainForm::ActionNewExecute(TObject *Sender)
{
TChildForm *child = new TChildForm(this); // Owner = MainForm
child->Caption = "未命名 " + IntToStr(MDIChildCount + 1);
child->Show();
}
bool __fastcall TChildForm::LoadFile(const String &fileName)
{
try {
RichEdit1->PlainText = (ExtractFileExt(fileName).LowerCase() == ".txt");
RichEdit1->Lines->LoadFromFile(fileName);
FFileName = fileName;
FModified = false;
UpdateTitle();
return true;
}
catch (...) { return false; }
}
void __fastcall TChildForm::UpdateTitle()
{
Caption = ExtractFileName(FFileName);
if (FFileName.IsEmpty()) Caption = "未命名";
if (FModified) Caption = "*" + Caption;
}
bool __fastcall TChildForm::SaveFile(const String &fileName)
{
String fn = fileName.IsEmpty() ? FFileName : fileName;
if (fn.IsEmpty()) return SaveAs(); // 彈出對話框
try {
RichEdit1->Lines->SaveToFile(fn);
FFileName = fn;
FModified = false;
UpdateTitle();
return true;
}
catch (...) { return false; }
}
void __fastcall TChildForm::ActionPrintExecute(TObject *Sender)
{
TPrintDialog *pd = new TPrintDialog(this);
if (pd->Execute()) {
RichEdit1->Print(FFileName.IsEmpty() ? "未命名文件" : FFileName);
}
delete pd;
}
void __fastcall TChildForm::RichEdit1Change(TObject *Sender)
{
if (!FModified) {
FModified = true;
UpdateTitle();
}
// 更新狀態列游標位置
StatusBar1->Panels->Items[0]->Text =
"行: " + IntToStr(RichEdit1->CaretPos.y + 1) +
" 欄: " + IntToStr(RichEdit1->CaretPos.x + 1);
}
| 需求 | 實現方式 |
|---|---|
| 單一實例開啟文件 | 遍歷 MDIChildren 比對 FFileName,存在則 BringToFront() |
| 關閉確認 | 子表單 OnCloseQuery 檢查 FModified,提示儲存 |
| 視窗狀態持久化 | 主表單關閉時寫入註冊表/INI:子視窗位置、大小、開啟文件清單 |
| 拖放開啟 | 主表單 OnDropFiles 處理拖入文件 |
| 多執行緒載入大文件 | 背景執行緒 LoadFromFile,完成後 Synchronize 指派給 RichEdit |
OnClose Action = caFree。Menu 屬性設為 MainMenu1,VCL 自動合併。Action 的 OnUpdate 檢查 ActiveMDIChild 是否存在。
C++Builder 的 MDI 支援極其成熟,善用模板與 Action 架構,可在極短時間內構建專業級多文件應用。