本文介紹一個基於 MFC 的電子表格類庫 CGridCtrl,雖尚在持續完善中,但已具備核心試算表功能,適合需求客製化報表元件的專案二次開發。作者開放原始碼並歡迎回饋 (zzxiong@citiz.net)。
圖 1:使用類庫建立的範例報表介面。
類庫核心難點在於公式解析。以下節錄 CGridCtrl::ParserExpression 關鍵邏輯,展示如何將字串運算式 (如 =A1+B2*10) 轉換為可執行的運算序列:
CString CGridCtrl::ParserExpression(CString strExpression)
{
CString strSign = _T("+,-,*,/,(,)"); // 運算子集合
CString strRetu; // 輸出運算式
CString strTmp, strTmp2, strTmp3;
LPCSTR lpstr = strExpression;
char szBuf[200] = {0};
long nTmp = -1, nLen = strExpression.GetLength();
int nStatus = 0; // 0:初始 1:字母/儲存格 2:數值
int nRow, nCol;
strExpression.MakeUpper();
for (long i = 0; i < nLen; i++)
{
char ch = strExpression.GetAt(i);
// 判斷是否為運算子
if (strSign.Find(ch) >= 0)
{
// 處理前一個 token
if (nStatus == 1) // 儲存格參照或函數名
{
strTmp3.Format(_T("%s"), szBuf);
strRetu += strTmp3;
}
else if (nStatus == 2) // 數值或儲存格座標
{
if (!IsValidCol(szBuf)) // 非合法欄位名 → 視為數值
{
strTmp3.Format(_T("(%s)"), szBuf);
strRetu += strTmp3;
}
else // 合法儲存格座標 (如 A1)
{
CCellID cell;
if (ConvertStrToCellID(szBuf, cell))
{
// 遞迴解析被參照儲存格的公式
strTmp3 = ParserCell(cell.row, cell.col);
strRetu += _T("(") + strTmp3 + _T(")");
}
else return _T("#INVALID!");
}
}
nStatus = 0; nTmp = -1; memset(szBuf, 0, 200);
// 將運算子加入輸出
if (!(i+1 >= nLen && ch >= '0' && ch <= '9'))
{
strTmp3.Format(_T("%c"), ch);
strRetu += strTmp3;
}
}
else if (ch >= 'A' && ch <= 'Z') // 字母 → 可能為儲存格欄位
{
nStatus = 1; szBuf[++nTmp] = ch;
}
else if (ch >= '0' && ch <= '9') // 數字 → 可能為列號或數值
{
nStatus = 2; szBuf[++nTmp] = ch;
}
}
return strRetu;
}
關鍵設計點:
A1) 呼叫 ParserCell 遞迴取得該儲存格的計算式,支援巢狀參照。#INVALID!,名稱錯誤回傳 #NAME!。1. 將 GridCtrl.h/.cpp 等核心檔案加入專案。
2. 在對話框或視圖中建立 CGridCtrl 物件,呼叫 Create 初始化。
3. 使用 SetItemText、SetItemFormat、MergeCells 等介面填充資料。
4. 列印預覽請呼叫 PrintPreview。
作者聲明:程式碼可能參考其他公開技術,若涉及版權請來信處理。使用者請尊重原作者意願,商用前請徵求授權。