TTreeView 是展示多層級關係資料的最佳控制項,但在資料庫開發中面對層次多、結構複雜的資料,如何快速構造樹形目錄並實現導航?傳統遞歸或多層巢狀迴圈效率低、程式碼冗長。本文介紹基於**編碼字典**的高效算法:程式碼精簡、執行極快、支援任意層次、資料變動無需改代碼。
bianma(編碼,字串型)與 name(名稱)欄位'222' 表示三層,每層 2 位:第 1 層 2 碼(10~99)、第 2 層 4 碼(1001~1099)、第 3 層 6 碼const
cTreeCodeFormat = '222'; // 編碼格式:XX XX XX
cTreeMaxLevel = 3; // 最大層次
cTreeRootTxt = '城市'; // 根節點名稱
procedure TForm1.LoadTree(treeDB: TDBDataSet);
var curID, nodeTxt: string; level: Integer; mynode: array[0..3] of TTreeNode;
begin
Screen.Cursor := crHourGlass;
Tree.Items.Clear;
level := 0;
// 根節點
mynode[level] := Tree.Items.Add(nil, cTreeRootTxt);
mynode[level].ImageIndex := 1;
with treeDB do try
if not Active then Open;
First;
while not Eof do begin
curID := Trim(FieldByName('bianma').AsString);
nodeTxt := curID + '-' + Trim(FieldByName('cityname').AsString);
level := GetNodeLevel(cTreeCodeFormat, curID); // 解析層級
if level > 0 then
mynode[level] := Tree.Items.AddChild(mynode[level-1], nodeTxt); // 掛在上層節點
mynode[level].ImageIndex := 2;
Next;
end;
finally Close; end;
mynode[0].Expand(False);
Screen.Cursor := crDefault;
end;
function TForm1.GetNodeLevel(sFormat, sCode: string): Integer;
var i, iLen: Integer;
begin
Result := -1; iLen := 0;
if (sFormat <> '') and (sCode <> '') then
for i := 1 to Length(sFormat) do begin
iLen := iLen + StrToInt(sFormat[i]);
if Length(sCode) = iLen then begin Result := i; Break; end;
end;
end;
透過右鍵選單操作節點,直接對應資料庫操作並同步更新 TreeView:
Items.AddChild + Table.InsertNode.Text := 新名稱 + Table.Edit/PostNode.Delete + Table.Delete關鍵輔助函數:
// 取得當前節點編碼
function GetCurrentNodeBianma(curNode: TTreeNode): string;
var p: Integer; begin p := Pos('-', curNode.Text); Result := Copy(curNode.Text, 1, p-1); end;
// 取得子節點最大編碼(含自動補位邏輯)
function GetCurrChildNodeMaxMa(curNode: TTreeNode): string;
// ... 以 SQL LIKE 查詢同前綴最大編碼,無子節點時自動補 '00'
| 傳統遞歸/巢狀迴圈 | 編碼字典法 |
|---|---|
| 多次查詢/遞歸調用 | 單次順序遍歷 |
| 層次固定需改代碼 | 修改常數即可適應任意層次 |
| 資料變動需重構全樹 | 增刪改局部同步,無閃爍 |
| 效能隨資料量指數下降 | 線性複雜度,極快 |
原文發表於《電腦編程技巧與維護》2003 年第 1 期 P27。此算法經多年實戰驗譱,至今仍是 Delphi 資料庫樹形介面開發的經典範式。