專案架構
- trpt/:存放報表樣板
table.xls (標題、表頭、格式預設)
- report/:輸出填好資料的報表供使用者開啟
- 介面:
SaveButton (產生報表)、ReadButton (開啟報表)
必要標頭與連結
// Form1.h
#include "Excel_2K_SRVR.h" // Office 2000 類型庫產生的標頭
#pragma link "Excel_2K_SRVR" // 連結類型庫
private:
Variant Ex, Wb, Sheet, ERange, EBorders;
SaveButtonClick:資料寫入與儲存
void __fastcall TForm1::SaveButtonClick(TObject *Sender) {
try {
SaveButton->Enabled = false;
ReadButton->Enabled = false;
AnsiString ExcelFileName = GetCurrentDir() + "\\trpt\\table.xls";
if (!FileExists(ExcelFileName)) {
MessageBox("報表樣板不存在", "錯誤", MB_ICONSTOP|MB_OK);
return;
}
// 建立 Excel 物件
try { Ex = Variant::CreateObject("Excel.Application"); }
catch(...) { MessageBox("無法啟動 Excel", "錯誤", MB_ICONSTOP|MB_OK); return; }
Ex.OlePropertySet("Visible", false);
Ex.OlePropertyGet("WorkBooks").OleProcedure("Open", ExcelFileName.c_str());
Wb = Ex.OlePropertyGet("ActiveWorkBook");
Sheet = Wb.OlePropertyGet("ActiveSheet");
// 清空舊資料 (第 3~300 列,6 欄)
for (int r = 3; r < 300; r++) {
for (int c = 1; c < 7; c++)
Sheet.OlePropertyGet("Cells", r, c).OlePropertySet("Value", "");
// 移除框線
AnsiString rng = "A" + IntToStr(r) + ":F" + IntToStr(r);
ERange = Sheet.OlePropertyGet("Range", rng.c_str());
EBorders = ERange.OlePropertyGet("Borders");
EBorders.OlePropertySet("LineStyle", xlNone);
}
// 表頭:標題、日期
AnsiString title = "報表標題";
Sheet.OlePropertyGet("Cells", 1, 1).OlePropertySet("Value", title.c_str());
DateSeparator = '-'; ShortDateFormat = "yyyy/m/d";
AnsiString dt = DateToStr(Date());
AnsiString yr = dt.SubString(1,4);
AnsiString md = dt.SubString(6, dt.Length()-5);
AnsiString mon = md.SubString(1, md.Pos("-")-1);
AnsiString day = md.SubString(md.Pos("-")+1, md.Length()-md.Pos("-"));
AnsiString dtStr = yr + "年" + mon + "月" + day + "日";
Sheet.OlePropertyGet("Cells", 2, 5).OlePropertySet("Value", dtStr.c_str());
// 欄位名稱 (第 3 列)
const char* cols[6] = {"欄位1","欄位2","欄位3","欄位4","欄位5","欄位6"};
for (int c = 0; c < 6; c++)
Sheet.OlePropertyGet("Cells", 3, c+1).OlePropertySet("Value", cols[c]);
// 畫表頭框線
AnsiString hdrRng = "A3:F3";
ERange = Sheet.OlePropertyGet("Range", hdrRng.c_str());
EBorders = ERange.OlePropertyGet("Borders");
EBorders.OlePropertySet("LineStyle", xlContinuous);
EBorders.OlePropertySet("Weight", xlThin);
EBorders.OlePropertySet("ColorIndex", xlAutomatic);
// 從資料庫讀取並填入 (示範 Query1)
int row = 4;
Query1->Open();
while (!Query1->Eof) {
for (int c = 1; c < 7; c++) {
AnsiString val = Query1->Fields->Fields[c-1]->AsString;
Sheet.OlePropertyGet("Cells", row, c).OlePropertySet("Value", val.c_str());
}
// 畫每列框線
AnsiString dRng = "A" + IntToStr(row) + ":F" + IntToStr(row);
ERange = Sheet.OlePropertyGet("Range", dRng.c_str());
EBorders = ERange.OlePropertyGet("Borders");
EBorders.OlePropertySet("LineStyle", xlContinuous);
EBorders.OlePropertySet("Weight", xlThin);
EBorders.OlePropertySet("ColorIndex", xlAutomatic);
row++; Query1->Next();
}
// 儲存、關閉、釋放
Wb.OleProcedure("Save");
Wb.OleProcedure("Close");
Ex.OleFunction("Quit");
// 複製到 report 目錄供使用者操作
AnsiString dest = GetCurrentDir() + "\\report\\table.xls";
if (!CopyFile(ExcelFileName.c_str(), dest.c_str(), false))
MessageBox("複製失敗,檔案可能被佔用", "錯誤", MB_ICONSTOP|MB_OK);
else
MessageBox("報表產生完成", "提示", MB_ICONINFORMATION|MB_OK);
} catch(...) {
MessageBox("操作 Excel 失敗", "錯誤", MB_ICONSTOP|MB_OK);
if (Wb) Wb.OleProcedure("Close");
if (Ex) Ex.OleFunction("Quit");
}
SaveButton->Enabled = true;
ReadButton->Enabled = true;
}
ReadButtonClick:開啟報表供編輯
void __fastcall TForm1::ReadButtonClick(TObject *Sender) {
try {
AnsiString fn = GetCurrentDir() + "\\report\\table.xls";
if (!FileExists(fn)) { MessageBox("檔案不存在", "錯誤", MB_ICONSTOP|MB_OK); return; }
try { Ex = Variant::CreateObject("Excel.Application"); }
catch(...) { MessageBox("無法啟動 Excel", "錯誤", MB_ICONSTOP|MB_OK); return; }
Ex.OlePropertySet("Visible", true);
Ex.OlePropertyGet("WorkBooks").OleProcedure("Open", fn.c_str());
} catch(...) {
MessageBox("開啟失敗", "錯誤", MB_ICONSTOP|MB_OK);
if (Ex) Ex.OleFunction("Quit");
}
}
關鍵技術要點
- 樣板分離:版面設計在 Excel 完成,程式只負責填資料,維護容易。
- Visible = false:背景產生報表不干擾使用者,速度較快。
- 清空策略:固定範圍清值+移除框線,避免舊資料殘留。
- 框線繪製:透過
Range.Borders 設定 LineStyle/Weight/ColorIndex。
- 資源釋放:
Workbook.Close → App.Quit → Variant = Unassigned (隱含於離開作用域)。
- 檔案複製:產生完成後複製到
report/,保護樣板不被異動。
- 錯誤處理:
try...catch 確保 Excel 進程一定會被關閉。
擴充建議
- 使用
ADOQuery 直接連接資料庫,避免 Query1 元件依賴。
- 支援多工作表報表:依
Sheet.Name 切換。
- 匯出 PDF:
Workbook.Exec(PR("ExportAsFixedFormat") << xlTypePDF << pdfPath) (Office 2007+)。
- 非同步產生:放入執行緒避免 UI 凍結。