Delphi实现树型结构

时间:2021-04-09 11:57:48

生成树型结构有2种方法:

1.动态生成树结点

2.静态生成树结点

这里暂不讨论动态生成树,先实现静态生成!

所谓静态生成树结点是指通过遍历数据源的方式一次性把所有树结点全部加载,说起生成树避免不了谈起数据库结构的设计。

数据库设计的方法有2种:

1.单编号法

单编号法是以每个类为统一编号,如其有子类,则顺着该编号向后排。如水果编号为001,则苹果为水果的一类,则应为001001等等,这种方法易于统计,但不易于维护!如:想要将苹果类变为其它的类,且苹果类下有N层,那会是一件比较麻烦的事情!

2.双编号法

双编号也就是我们经常说的用ID与PARENTID来表示其父子关系,维护起来比较方便,但遍历会稍稍复杂一些!

无论哪种方法,一般情况下都要创建结构体,并把结构体指针放置到树结点中!因为Treeview的树结点不可能放

置更多的信息,我们通常要存该结点的名称(中英文)、结点ID、是否为功能结点、父结点、图标、选中结点后的图标、dll的名称等等等...

以下例子:

  1 01.unit Unit1;  
2 02.interface
3 03.uses
4 04. Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
5 05. Dialogs, StdCtrls, ComCtrls, DB, ADODB;
6 06.type
7 07. PNodeInfoEx = ^TNodeInfoEx;
8 08. TNodeInfoEx = Packed Record
9 09. NodeID : Integer;
10 10. ParentID : Integer;
11 11. NodeType : Integer;
12 12. ChnNodeTitle : String;
13 13. ImageIndex: SmallInt;
14 14. SelectedIndex: SmallInt;
15 15. end;
16 16. TForm1 = class(TForm)
17 17. tv1: TTreeView;
18 18. btn1: TButton;
19 19. qry1: TADOQuery;
20 20. procedure btn1Click(Sender: TObject);
21 21. procedure FormDestroy(Sender: TObject);
22 22. private
23 23. { Private declarations }
24 24. function StaticBuildTree(TreeView:TTreeView ):Boolean;
25 25. function AddTreeItem(TreeView:TTreeView; AddNodeInfo:PNodeInfoEx):TTreeNode;
26 26. function FindTreeItem(TreeView:TTreeView; CurNodeID:integer): TTreeNode;
27 27. public
28 28. { Public declarations }
29 29. end;
30 30.var
31 31. Form1: TForm1;
32 32.implementation
33 33.{$R *.dfm}
34 34.
35 35.function TForm1.StaticBuildTree(TreeView:TTreeView ):Boolean;
36 36.var
37 37. AddNodeInfo : PNodeInfoEx;
38 38.begin
39 39. Result := False;
40 40. qry1.LoadFromFile('c:/AdminixTree.xml');//这里以XML文件做为数据源
41 41. Treeview.Items.BeginUpdate;//记住:在进行批量添加数据时要使用BeginUpdate,来暂时关闭由于添加数据而触发的某些事件(如OnChange事件等)
42 42. Treeview.Items.Clear;//清空Treeview
43 43. try
44 44. try
45 45. if qry1.RecordCount >0 then
46 46. begin
47 47. qry1.First;
48 48. while Not qry1.Eof do
49 49. begin
50 50. New(AddNodeInfo) ;//生成结构体
51 51. AddNodeInfo^.NodeID := qry1.FieldByName('NODE_ID').AsInteger;
52 52. AddNodeInfo^.ParentID := qry1.FieldByName('PARENT_ID').AsInteger;
53 53. AddNodeInfo^.NodeType := qry1.FieldByName('NodeType').AsInteger;
54 54. AddNodeInfo^.ChnNodeTitle := qry1.FieldByName('ChnNodeTitle').AsString;
55 55. AddNodeInfo^.ImageIndex := qry1.FieldByName('ImageIndex').AsInteger;
56 56. AddNodeInfo^.SelectedIndex := qry1.FieldByName('SelectedIndex').AsInteger;
57 57. AddTreeItem(Treeview,AddNodeInfo);//把结构体的指针存到Treeview中
58 58. qry1.Next;
59 59. end;
60 60. end;
61 61. except
62 62. Application.MessageBox('生成树结点失败',MB_ICONSTOP+MB_OK);
63 63. raise;//向上级抛异常
64 64. end;
65 65. qry1.Close;
66 66. Result := True;
67 67. finally
68 68. Treeview.Items.EndUpdate;
69 69. end;
70 70.end;
71 71.//在加入结点时,应先判断加入的是父结点还是子结点,判断的依据是在已存在的树结点中是否存在该结点的ParentID
72 72.function TForm1.AddTreeItem(TreeView:TTreeView; AddNodeInfo:PNodeInfoEx):TTreeNode;
73 73.var
74 74. ParentNode: TTreeNode;
75 75.begin
76 76. ParentNode := FindTreeItem(Treeview,AddNodeInfo^.ParentID);
77 77. If ParentNode <> nil then
78 78. Result := Treeview.Items.AddChildObject(ParentNode, Trim(AddNodeInfo.ChnNodeTitle), Pointer(AddNodeInfo))
79 79. else
80 80. Result := Treeview.Items.AddObject(ParentNode, Trim(AddNodeInfo.ChnNodeTitle), Pointer(AddNodeInfo));
81 81. if Result<>nil then
82 82. begin
83 83. Result.ImageIndex := AddNodeInfo.ImageIndex;
84 84. Result.SelectedIndex := AddNodeInfo.SelectedIndex;
85 85. end;
86 86.end;
87 87.//这里是判断是否存在其父结点
88 88.function TForm1.FindTreeItem(TreeView:TTreeView; CurNodeID:integer): TTreeNode;
89 89.var
90 90. i : Integer;
91 91.begin
92 92. Result := nil;
93 93. for i := 0 to Treeview.Items.Count-1 do
94 94. begin
95 95. if CurNodeID=PNodeInfoEx(Treeview.Items[i].Data)^.NodeID then
96 96. begin
97 97. Result := Treeview.Items[i];
98 98. Exit;
99 99. end;
100 100. end;
101 101.end;
102 102.//生成树结构
103 103.procedure TForm1.btn1Click(Sender: TObject);
104 104.begin
105 105. StaticBuildTree (tv1)
106 106.end;
107 107.//在窗体释放时一定要把树结点中的结构体指针给释放掉,对于在Dispose时为什么要进行强制转型后释放,以前有专门的讲解,在此不在累述
108 108.procedure TForm1.FormDestroy(Sender: TObject);
109 109.var
110 110. i : Integer;
111 111.begin
112 112. for i := 0 to tv1.Items.Count-1 do
113 113. begin
114 114. Dispose( PNodeInfoEx(tv1.Items[i].Data) )
115 115. end;
116 116.end;
117 117.end.
118 01.//如何访问树结点?
119 02.procedure TForm1.tv1MouseDown(Sender: TObject; Button: TMouseButton;
120 03. Shift: TShiftState; X, Y: Integer);
121 04.var
122 05. pNode:TTreeNode;
123 06.begin
124 07. pNode:=tv1.GetNodeAt(x,y);
125 08. if (pNode<>nil) and (Button=mbleft) then
126 09. ShowMessage(PNodeInfoEx(pNode.Data)^.ChnNodeTitle);
127 10.end;