S
sky1001
Unregistered / Unconfirmed
GUEST, unregistred user!
大家都知道,在很多应用场合,都存在着很多像树一样的结构:如组织结构、物品分类、论坛内容分类等等。由于这些结构是可以无限增加,任意构造的。而这样的很多信息大都是存放在数据库中。因此,把存放在数据库中的树形结构数据实现在TTreeView控件上是很实用的技术。目前网络上大都是采用递归的方式来实现。这里采取一种不同的方式来实现: 即采用队列的方式来实现数据库到TTreeView控件上树形结构的构建。下面是几个要点和代码。<br><br>1. 数据库存放树形的表至少要包含三个字段:<br> <br> NodeId-----------------------自动增量,表的关键字<br> NodeParentId-------------整数<br> Nodename-----------------字符串<br><br>2.把树上每个节点在数据库里的关键字索引NodeId存放在其Data指针里。<br><br>3. 为了实现界面和业务的分离,在树的构建中采取的数据集是TClientDataSet.<br><br>4.没有采用DELPHI自带的TQueue,因为其效率不高。这里使用了一种利用循环数组实现队列的方法。效率比DELPHI自带的TQueue要高。是一种非常实用的队列实现技术。<br><br>实现代码:<br><br>{利用队列实现树形结构}<br>unit DBTreeView;<br><br>interface<br>uses<br> ComCtrls,DBClient,Classes;<br><br><br>Type<br><br> TNodeQueue = class<br> private<br> FCount : integer;<br> FList : TList;<br> FHead : integer;<br> FTail : integer;<br> protected<br> procedure Grow;<br> public<br> constructor Create(aCapacity : integer);<br> destructor Destroy; override;<br> procedure Enqueue(aItem : pointer);<br> function Dequeue : pointer;<br> property Count : integer read FCount;<br> end;<br><br><br> TDBTreeView = class<br> private<br> FTreeView : TTreeView;<br> FcdsTree : TClientDataSet;<br> FQueue : TNodeQueue;<br> FRootname : string;<br> public<br> Constructor Create(aTreeView : TTreeView; aRootName : string; cdsTree : TClientDataSet);<br> Destructor Destroy;override;<br> Procedure DrawTree;<br> end;<br><br>implementation<br><br>{------------------------------------------------------------------------------}<br>{--------------------------- TNodeQueue ---------------------------------------}<br>{------------------------------------------------------------------------------}<br>constructor TNodeQueue.Create(aCapacity : integer);<br>begin<br> inherited Create;<br> FList := TList.Create;<br> FCount := 0;<br> FHead := 0;<br> FTail := 0;<br> if (aCapacity <= 1) then<br> aCapacity := 16;<br> FList.Count:=aCapacity;<br>end;<br><br>destructor TNodeQueue.Destroy;<br>begin<br> FList.Free;<br> inherited;<br>end;<br><br>procedure TNodeQueue.Grow;<br>var<br> i : integer;<br> Inx : integer;<br>begin<br> FList.Count := (FList.Count * 3) div 2;<br> if (FHead=0) then<br> FTail:=FCount<br> else<br> begin<br> Inx := FList.count;<br> for i:=Pred(FCount) downto FHead do<br> begin<br> Dec(Inx);<br> FList[Inx]:=FList;<br> end;<br> FHead:=Inx;<br> end;<br>end;<br><br>Procedure TNodeQueue.Enqueue(aItem: Pointer);<br>begin<br> FList[FTail]:=aItem;<br> FTail:=(FTail+1) mod FList.Count;<br> inc(FCount);<br> if(FTail=FHead) then<br> Grow;<br>end;<br><br>Function TNodeQueue.Dequeue : pointer;<br>begin<br> if (FCount = 0) then<br> Result:=nil<br> else<br> begin<br> Result:=FList[FHead];<br> FHead:=(FHead+1) mod FList.Count;<br> Dec(FCount);<br> end;<br>end;<br><br>{------------------------------------------------------------------------------}<br>{--------------------------- TDBTreeView --------------------------------------}<br>{------------------------------------------------------------------------------}<br><br>constructor TDBTreeView.Create(aTreeView: TTreeView; aRootName : string; cdsTree : TClientDataSet);<br>begin<br> inherited Create;<br> FTreeView := aTreeView;<br> FRootName := aRootName;<br> FcdsTree := cdsTree;<br> FQueue := TNodeQueue.Create(16);<br>end;<br><br>destructor TDBTreeView.Destroy;<br>begin<br> FQueue.Free;<br> inherited;<br>end;<br><br>Procedure TDBTreeView.DrawTree;<br>var<br> Root,TreeNode : TTreeNode;<br> ParentNodeId : integer;<br>begin<br> FTreeView.Items.Clear;<br> Root:=FTreeView.Items.Add(nil,FRootName);<br> parentNodeId := 0;<br> FcdsTree.First;<br> while not FcdsTree.Eof do<br> begin<br> if (FcdsTree.FieldByName('ParentNodeId').AsInteger = ParentNodeId) then<br> begin<br> TreeNode:=FTreeView.Items.AddChild(Root,FcdsTree.fieldbyname('Nodename').AsString);<br> TreeNode.Data := pointer(FcdsTree.FieldByName('NodeId').AsInteger);<br> FQueue.Enqueue(TreeNode);<br> FcdsTree.Next;<br> end<br> else<br> begin<br> Root := TTreeNode(FQueue.Dequeue);<br> if (integer(TTreeNode(Root).Data) = FcdsTree.Fieldbyname('ParentNodeId').AsInteger) then<br> ParentNodeId:=FcdsTree.Fieldbyname('ParentNodeId').AsInteger;<br> end;<br> end;<br>end;<br><br>end.<br><br>测试例子代码<br><br>unit Unit1;<br><br>interface<br><br>uses<br> Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,<br> Dialogs, ComCtrls, DB, ADODB, DBTables, StdCtrls, Provider, DBClient,DBTreeView,<br> ExtCtrls, DBCtrls, Grids, DBGrids;<br><br>type<br> TForm1 = class(TForm)<br> TreeView1: TTreeView;<br> cdstree: TClientDataSet;<br> DataSetProvider1: TDataSetProvider;<br> ADOConnection1: TADOConnection;<br> ADOQuery1: TADOQuery;<br> Button1: TButton;<br> procedure FormCreate(Sender: TObject);<br> procedure FormDestroy(Sender: TObject);<br> procedure Button1Click(Sender: TObject);<br> private<br> { Private declarations }<br> public<br> { Public declarations }<br> DBTree : TDBTreeView;<br> end;<br><br>var<br> Form1: TForm1;<br><br>implementation<br><br><br><br>{$R *.dfm}<br><br>procedure TForm1.FormCreate(Sender: TObject);<br>begin<br> With adoQuery1 do<br> begin<br> close;<br> sql.Clear;<br> sql.Add('select * from tb_tree order by parentNodeId, NodeId');<br> open;<br> end;<br> cdsTree.Active:=true;<br> DBTree := TDBTreeView.Create(TreeView1,'测试',cdsTree);<br>end;<br><br>procedure TForm1.FormDestroy(Sender: TObject);<br>begin<br> DBTree.Free;<br>end;<br><br>procedure TForm1.Button1Click(Sender: TObject);<br>begin<br> DBTree.DrawTree;<br>end;<br><br>end.<br><br><br>第一次发原创技术贴,不完善的地方,多包涵。