生成树型结构有2种方法:
1.动态生成树结点
2.静态生成树结点
这里暂不讨论动态生成树,先实现静态生成!
所谓静态生成树结点是指通过遍历数据源的方式一次性把所有树结点全部加载,说起生成树避免不了谈起数据库结构的设计。
数据库设计的方法有2种:
1.单编号法
单编号法是以每个类为统一编号,如其有子类,则顺着该编号向后排。如水果编号为001,则苹果为水果的一类,则应为001001等等,这种方法易于统计,但不易于维护!如:想要将苹果类变为其它的类,且苹果类下有N层,那会是一件比较麻烦的事情!
2.双编号法
双编号也就是我们经常说的用ID与PARENTID来表示其父子关系,维护起来比较方便,但遍历会稍稍复杂一些!
无论哪种方法,一般情况下都要创建结构体,并把结构体指针放置到树结点中!因为Treeview的树结点不可能放
置更多的信息,我们通常要存该结点的名称(中英文)、结点ID、是否为功能结点、父结点、图标、选中结点后的图标、dll的名称等等等...
以下例子:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, DB, ADODB; type PNodeInfoEx = ^TNodeInfoEx; TNodeInfoEx = Packed Record NodeID : Integer; ParentID : Integer; NodeType : Integer; ChnNodeTitle : String; ImageIndex: SmallInt; SelectedIndex: SmallInt; end; TForm1 = class(TForm) tv1: TTreeView; btn1: TButton; qry1: TADOQuery; procedure btn1Click(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } function StaticBuildTree(TreeView:TTreeView ):Boolean; function AddTreeItem(TreeView:TTreeView; AddNodeInfo:PNodeInfoEx):TTreeNode; function FindTreeItem(TreeView:TTreeView; CurNodeID:integer): TTreeNode; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} function TForm1.StaticBuildTree(TreeView:TTreeView ):Boolean; var AddNodeInfo : PNodeInfoEx; begin Result := False; qry1.LoadFromFile(‘c:/AdminixTree.xml‘);//这里以XML文件做为数据源 Treeview.Items.BeginUpdate;//记住:在进行批量添加数据时要使用BeginUpdate,来暂时关闭由于添加数据而触发的某些事件(如OnChange事件等) Treeview.Items.Clear;//清空Treeview try try if qry1.RecordCount >0 then begin qry1.First; while Not qry1.Eof do begin New(AddNodeInfo) ;//生成结构体 AddNodeInfo^.NodeID := qry1.FieldByName(‘NODE_ID‘).AsInteger; AddNodeInfo^.ParentID := qry1.FieldByName(‘PARENT_ID‘).AsInteger; AddNodeInfo^.NodeType := qry1.FieldByName(‘NodeType‘).AsInteger; AddNodeInfo^.ChnNodeTitle := qry1.FieldByName(‘ChnNodeTitle‘).AsString; AddNodeInfo^.ImageIndex := qry1.FieldByName(‘ImageIndex‘).AsInteger; AddNodeInfo^.SelectedIndex := qry1.FieldByName(‘SelectedIndex‘).AsInteger; AddTreeItem(Treeview,AddNodeInfo);//把结构体的指针存到Treeview中 qry1.Next; end; end; except Application.MessageBox(‘生成树结点失败‘,MB_ICONSTOP+MB_OK); raise;//向上级抛异常 end; qry1.Close; Result := True; finally Treeview.Items.EndUpdate; end; end; //在加入结点时,应先判断加入的是父结点还是子结点,判断的依据是在已存在的树结点中是否存在该结点的ParentID function TForm1.AddTreeItem(TreeView:TTreeView; AddNodeInfo:PNodeInfoEx):TTreeNode; var ParentNode: TTreeNode; begin ParentNode := FindTreeItem(Treeview,AddNodeInfo^.ParentID); If ParentNode <> nil then Result := Treeview.Items.AddChildObject(ParentNode, Trim(AddNodeInfo.ChnNodeTitle), Pointer(AddNodeInfo)) else Result := Treeview.Items.AddObject(ParentNode, Trim(AddNodeInfo.ChnNodeTitle), Pointer(AddNodeInfo)); if Result<>nil then begin Result.ImageIndex := AddNodeInfo.ImageIndex; Result.SelectedIndex := AddNodeInfo.SelectedIndex; end; end; //这里是判断是否存在其父结点 function TForm1.FindTreeItem(TreeView:TTreeView; CurNodeID:integer): TTreeNode; var i : Integer; begin Result := nil; for i := 0 to Treeview.Items.Count-1 do begin if CurNodeID=PNodeInfoEx(Treeview.Items[i].Data)^.NodeID then begin Result := Treeview.Items[i]; Exit; end; end; end; //生成树结构 procedure TForm1.btn1Click(Sender: TObject); begin StaticBuildTree (tv1) end; //在窗体释放时一定要把树结点中的结构体指针给释放掉,对于在Dispose时为什么要进行强制转型后释放,以前有专门的讲解,在此不在累述 procedure TForm1.FormDestroy(Sender: TObject); var i : Integer; begin for i := 0 to tv1.Items.Count-1 do begin Dispose( PNodeInfoEx(tv1.Items[i].Data) ) end; end; end.
//如何访问树结点? procedure TForm1.tv1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var pNode:TTreeNode; begin pNode:=tv1.GetNodeAt(x,y); if (pNode<>nil) and (Button=mbleft) then ShowMessage(PNodeInfoEx(pNode.Data)^.ChnNodeTitle); end;