支援龐大樹狀資料 (如圖書分類、組織架構) 的動態載入、新增、修改、刪除,且不將整棵樹序列化儲存,而是將結構分散在資料庫每筆記錄的編碼欄位中,實現「按需載入、效能優異」。
LongID)每筆記錄擁有固定長度編碼 LongID (範例 6 碼),格式定義:CTreeCodeFormat = '122222' 表示:
cTreeMaxLevel = 6)範例:
| 節點 | LongID | 層級 | 父節點 LongID 前綴 |
|---|---|---|---|
| 根節點 | 000000 | 0 | - |
| 文學 | 100000 | 1 | 000000 (取前 1 碼) |
| 小說 | 101000 | 2 | 100000 (取前 3 碼) |
| 武俠 | 101010 | 3 | 101000 (取前 5 碼) |
GetNodeLevelfunction GetNodeLevel(const Format, Code: string): Integer;
var I, AccLen: Integer;
begin
Result := -1;
AccLen := 0;
for I := 1 to Length(Format) do begin
AccLen := AccLen + StrToInt(Format[I]);
if Length(Code) = AccLen then begin
Result := I;
Break;
end;
end;
end;
GetNodeItemfunction GetNodeItem(const Code: string): Integer;
var I: Integer; Tmp: string;
begin
Result := 0;
for I := 0 to Tree.Items.Count - 1 do begin
Tmp := Copy(Tree.Items[I].Text, 1, Pos('-', Tree.Items[I].Text) - 1);
if Tmp = Code then begin Result := I; Break; end;
end;
end;
LoadTreeprocedure LoadTree(TreeDB: TDataSet);
var CurID, NodeTxt: string; Level: Integer;
NodeArray: array[0..6] of TTreeNode; // 索引=層級,值=該層最新節點
begin
Screen.Cursor := crHourGlass;
Tree.Items.Clear;
Tree.Enabled := True;
// 根節點
NodeArray[1] := Tree.Items.Add(nil, CTreeRootTxt); // '所有圖書'
NodeArray[1].ImageIndex := 0; NodeArray[1].SelectedIndex := 1;
TreeDB.Open;
TreeDB.First;
while not TreeDB.Eof do begin
CurID := Trim(TreeDB.FieldByName('tsglb').AsString); // 編碼
NodeTxt := CurID + '-' + Trim(TreeDB.FieldByName('tsglbn').AsString); // 顯示文字
Level := GetNodeLevel(CTreeCodeFormat, CurID);
if Level > 0 then begin
// 父節點 = 上一層最新節點
NodeArray[Level] := Tree.Items.AddChild(NodeArray[Level-1], NodeTxt);
NodeArray[Level].ImageIndex := 2;
NodeArray[Level].SelectedIndex := 3;
end;
TreeDB.Next;
end;
NodeArray[1].Expand(False); // 展開根節點
TreeDB.Close;
Screen.Cursor := crDefault;
end;
UpdateTreeprocedure UpdateTree(var CurNode: TTreeNode; const NodeTxt, State: string);
begin
if State = 'add' then begin
CurNode := Tree.Items.AddChild(CurNode, NodeTxt);
CurNode.ImageIndex := 2; CurNode.SelectedIndex := 3;
end
else if State = 'del' then
CurNode.Delete
else if State = 'edi' then
CurNode.Text := NodeTxt;
end;
| 操作 | TreeView 動作 | 資料庫 SQL |
|---|---|---|
| 新增 | AddChild | INSERT INTO TreeTable (LongID, Name) VALUES (:NewCode, :Name) |
| 修改 | Node.Text := NewName | UPDATE TreeTable SET Name = :Name WHERE LongID = :Code |
| 刪除 | Node.Delete (含所有子孫) | DELETE FROM TreeTable WHERE LongID LIKE :Code + '%' |
大量資料時,OnExpanding 事件動態查詢資料庫:
procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean);
var Qry: TQuery; Child: TTreeNode; ParentCode: string;
begin
Child := Node.GetFirstChild;
// 檢查佔位節點 (Text 為空、Data=nil)
if (Child <> nil) and (Child.Text = '') and (Child.Data = nil) then begin
Child.Delete;
ParentCode := GetNodeCode(Node); // 從 Node.Data 或 Text 解析
Qry.SQL.Text := 'SELECT LongID, Name FROM TreeTable WHERE LongID LIKE :Prefix AND Level = :Lvl';
Qry.ParamByName('Prefix').AsString := ParentCode + '%';
Qry.ParamByName('Lvl').AsInteger := Node.Level + 1;
Qry.Open;
while not Qry.Eof do begin
NewNode := Tree.Items.AddChildObject(Node, Qry.FieldByName('Name').AsString,
TObject(Qry.FieldByName('LongID').AsString));
// 若有子節點,加入佔位節點
if HasChildren(Qry.FieldByName('LongID').AsString) then
Tree.Items.AddChildObject(NewNode, '', nil);
Qry.Next;
end;
end;
end;
以固定長度層級編碼 (LongID) 為核心,結合 NodeArray[Level] 追蹤各層最新節點,可高效載入龐大樹狀結構。動態操作同步更新資料庫與 TreeView,虛擬模式進一步支援百萬級節點。關鍵在編碼規則設計與 ID 生成策略,需依業務增長模式權衡簡單性與擴展性。