Treeview的使用介绍Delphi
delphi中Treeview的使用介绍 Delphi 开发 2008-06-06 08:37:12 阅读181 评论0 字号:大中小 订阅 今天重点学习了TreeView的使用方法,基本的已经写了,现在主要想说的是如何显示数据库的资料,今天只是做了个较简单的例子,一个父节点下显示数据库中某个field的值。代码如下: procedure TMainForm.TreeviewShow(Sender: TObject); var node1,subnode1: TTreeNode; i: Integer; begin Treeview1.Selected := nil; node1 := Treeview1.Items.AddFirst(nil,'user'); ADOTable1.Active := True; while not ADOTable1.Eof do for i := 0 to ADOTable1.RecordCount - 1 do begin Treeview1.Items.AddChildObject(node1, ADOTable1.FieldByName('USERNAME').AsString, nil); ADOTable1.Next; end; end; 使用的是ADO中的ADOTable1来完成。 心得:今天下午比较重要的心得或许就是当你在某个函数中写代码的时候,它会自动把那些不符合它返回值的函数功能屏蔽掉。打个比方,象Treeview1.Items.AddChildObject(node1, ADOTable1.FieldByName('USERNAME').AsString, nil);中的第二项,它要求的是返回String ,如果你使用得失ADODATASET的话,它就自动帮你屏蔽掉了FieldByName这个功能,因为返回的不是String,所以,在这种情况下最好是把你需要的函数在空白处得到在copy到里面去。 每一个节点下子节点形成这一节点的Items属性,当前节点有一个唯一的Index(TreeNode的Index属性),用于说明子节点在Items中的位置,每一个节点下的子节点是顺序编号的,第一个是0,第二个是1,依次类推。用IndexOf方法获得子节点的顺序,绝对顺序(AbsoluteIndex)则是指从Treeview第一个项开始的顺序值,第一个是0,如此推下去。Item属性则根据Index的值返回当前节点的第Index个子节点。Count则表明属于此项的所有子节点的数量。用MoveTo方法将Item由一个位置移到另一个位置。 Expanded属性表明是否所有的子项都全部展开(包括子项的子项),为True表示全部展开。IsVisible属性表明一个项是否在树中能被看到,如果树全部展开那么这个Item是肯定可以被看到。HasChildren属性表明一个项是否有子项。 GetFirstChild, GetLastChild, GetPrevChild, and GetNextChild分别返回当前项子项的第一个、最后一个和前一个、后一个项。GetNextSibling and GetPrevSibling则返回在同一Level下的下一个和上一个项。GetNextVisible and GetPrevVisible则返回能看得到的下一个和上一个项。如果一个节点有Parent,则HasAsParent方法返回True. Parent为当前项的父项。Focused属性确定焦点是否落在此节点上,被Focus时会一个标准的方框围住。很显然,只有一个节点会被聚焦。 Selected属性表明一个节点是否被选中,同样只有一个节点会被选中。DropTarget属性表明节点在拖动操作中是源还是目标。 .1.添加、删除、修改节点: 静态的方法可以在设计时通过Items的编辑器设置各节点的内容。 在添加和删除前必须保证有节点被选中(Treeview.Selected = nil) 用AddFirst, AddFirstChild, AddChild等先添加根节点,如Treeview.Items.AddFirst( nil, 'Root'); 然后以此为基础,添加此项的子节点。 删除节点 Treeview.Selected.Delete 编辑节点内容 Treeview.Selected.EditText 注意:由于根节点没有父节点 (TTreeNode.Parent= nil) 此外,在大批量添加数据到Treeview中时最好使用 TreeView.Items.BeginUpdate; 添加节点 TreeView.Items.EndUpdate 这样能加快显示速度。 2.在节点上添加图象 Treeview中几个与图象相关的属性: SelectedIndex:当节点被选中时在TimageList 中选什么样的图象 OverlayIndex:选那副图象作为掩图(一幅图象透明地显示在另一幅图象的前面),比如一个节点不可用时加一副X图象在其前面。 ImageIndex:在常态时选用的图的序号 StateIndex: 在StateImages这个ImageList中对应的序号,-1时不显示图象 比较典型的,象在文件管理器中的所显示的一样,Treeview控件在节点之前也可以显示图象。在Form中放置一ImageList控件,加入几个图片,分别被Index为0,1,…在Treeview的Image属性项填入你所加入的ImageList的控件名称。TreeNode的ImageIndex表示节点未被选中时(Selected=nil)的图片序号,SelectedIndex表示节点被选中时图片序号。 加入父节点使用的命令是Treeview.Items.AddFirst(nil,'father');里面的nil的意思是初始标记(不是0的意思,0已经代表了建立了一个关于节点的对象了,nil代表的是初始化时候的一个node)。在使用该函数来创建节点的时候,你会发现很多例子中都会建立一个TTreeNode对象来把刚才建立父节点的命令赋给这个对象,比如现在有个TTreeNode对象node1,那么就可以写成node1 := Treeview1.Items.AddFirst(nil,'father'),目的是把该节点的标识位置赋予node1,这里不需要再写多一个相同的AddFirst了,因为在赋予node1的同时就已经建立了这个父节点了。那为什么需要这个node1呢?当然,如果你只有一层节点没有子节点的话,可以不需要这个,这个node1主要是为了给建立子节点的时候给它一个标识,告诉系统这个子节点是属于哪个父节点的,命令如下reeview1.Items.AddChildObject(node1, 'child', nil); delphi中TreeView使用常见问题 编程心得 1,在Delphi中,TreeView控件是一款很出色而且很常用的控件。 在使用过程中,了解到其TTreeNode对象的data属性存储相关数据很有用,一般情况下,我们先声明一个结构体以及其指针,例如: type PMyRc = ^TMyRc; TMyRc = Record id:string; name:string; age:integer; end; 添加一个节点,显示信息为TMyRc的name,同时存储id,age。方法如下: var p:PMyRc; i:integer; begin Randomize; for i:= 0 to 9 do begin New(p); p.id:=inttostr(random(100)); p.name:='name'+ inttostr(random(205)); p.age:=random(90); // Caption := p.id+' '+P.name + ' '+inttostr(p.age); TreeView1.Items.AddObject(nil,p.name,Tobject(p)); //dispose(p); 如果在这里释放指针,id,age并不能存在树中,而是在这里就被释放了。应该在释放树的事件里书写。 end; end; 释放树的事件deletion, 即使是删除也会执行这些代码。所以不用担心内存泄漏。但是如果不书写以下代码,或者用相关的方式释放内存,必定会造成内存泄漏。 procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode); begin dispose(pmyrc(node.data)); end; 访问某个树枝中的age值: Pmyrc(TreeView1.Selected.data)^.age
delphi中Treeview的使用介绍
delphi treeview的使用 delphi中Treeview的使用介绍- - 每一个节点下子节点形成这一节点的Items属性,当前节点有一个唯一的Index(TreeNode的Index属性),用于说明子节点 在Items中的位置,每一个节点下的子节点是顺序编号的,第一个是0,第二个是1,依次类推。用IndexOf方法获得子节点的顺序,绝对顺序 (AbsoluteIndex)则是指从Treeview第一个项开始的顺序值,第一个是0,如此推下去。Item属性则根据Index的值返回当前节点 的第Index个子节点。Count则表明属于此项的所有子节点的数量。用MoveTo方法将Item由一个位置移到另一个位置。 Expanded属性表明是否所有的子项都全部展开(包括子项的子项),为True表示全部展开。IsVisible属性表明一个项是否在树中能被看到, 如果树全部展开那么这个Item是肯定可以被看到。HasChildren属性表明一个项是否有子项。 GetFirstChild, GetLastChild, GetPrevChild, and GetNextChild分别返回当前项子项的第一个、最后一个和前一个、后一个项。GetNextSibling and GetPrevSibling则返回在同一Level下的下一个和上一个项。GetNextVisible and GetPrevVisible则返回能看得到的下一个和上一个项。如果一个节点有Parent,则HasAsParent方法返回True. Parent为当前项的父项。Focused属性确定焦点是否落在此节点上,被Focus时会一个标准的方框围住。很显然,只有一个节点会被聚焦。 Selected属性表明一个节点是否被选中,同样只有一个节点会被选中。DropTarget属性表明节点在拖动操作中是源还是目标。 .1.添加、删除、修改节点: 静态的方法可以在设计时通过Items的编辑器设置各节点的内容。 在添加和删除前必须保证有节点被选中(Treeview.Selected = nil) 用AddFirst, AddFirstChild, AddChild等先添加根节点,如Treeview.Items.AddFirst( nil, 'Root'); 然后以此为基础,添加此项的子节点。 删除节点 Treeview.Selected.Delete 编辑节点内容 Treeview.Selected.EditText 注意:由于根节点没有父节点 (TTreeNode.Parent= nil) 此外,在大批量添加数据到Treeview中时最好使用 TreeView.Items.BeginUpdate; 添加节点 TreeView.Items.EndUpdate 这样能加快显示速度。 2.在节点上添加图象 Treeview中几个与图象相关的属性: SelectedIndex:当节点被选中时在TimageList 中选什么样的图象 OverlayIndex:选那副图象作为掩图(一幅图象透明地显示在另一幅图象的前面),比如一个节点不可用时加一副X图象在其前面。 ImageIndex:在常态时选用的图的序号 StateIndex: 在StateImages这个ImageList中对应的序号,-1时不显示图象 比较典型的,象在文件管理器中的所显示的一样,Treeview控件在节点之前也可以显示图象。在Form中放置一ImageList控件,加入几个图 片,分别被Index为0,1,…在Treeview的Image属性项填入你所加入的ImageList的控件名称。TreeNode的 ImageIndex表示节点未被选中时(Selected=nil)的图片序号,SelectedIndex表示节点被选中时图片序号。 //////////////////////////////////////////////////////////////// delphi下treeview控件基于节点编号的访问 有时我们需要保存和重建treeview控件,本文提供一种方法,通过以树结构节点的编号访问树结构,该控件主要提供的方法如下: function GetGlobeNumCode(inNode:TTreeNode):String; 功能:返回当前节点的编号,编号规则见源码内说明。 function LocatOrGenerateNode(inNumCode:String):TTreeNode; 功能:以编号返回节点,假如节点的父节点和它的前继兄弟节点不存在,该方法会创建它们,名称为'Temp',当然假如已经存在,就不执行创建工作。 通过以上两个函数,这样我们就可以不加限制的创建和访问节点。该控件在我以前开发的,现在提供给大家做一个参考,希望能对你有帮助。 源码: // *********************************************** // // 用于实现对TreeView控件的树结构的保存和重建 // 编写该控件主要用于实现对行政文件等具有树的层次结构的对象 // 实现保存和显示 // 节点编号规则: // + ***** ->1 // + ***** ->1.1 // + ***** ->1.1.1 // + ***** ->1.2 // 作者:Jack // 最后修改日期:2002-12-24 // // ********************************************** unit CtrlTree; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls; type TCtrlTree = class(TTreeView) private { Private declarations } function GetPosAtBound(inString:String;inStart:Integer):Integer; function GetTheLastPointPos(inString:String):Integer; protected { Protected declarations } public { Public declarations } function GetNumInSameLevel(inNode:TTreeNode):Integer; function GetGlobeNumCode(inNode:TTreeNode):String; function GetParent(inNumCode:String):TTreeNode; function LocateNodeInLevel(parNode:TTReeNode;LevelCode:integer):TTReeNode; published { Published declarations } function LocatOrGenerateNode(inNumCode:String):TTreeNode; function InsertAsFinalChild(inString:String;inNode:TTreeNode):TTReeNode; function InsertAsPreviousSibling(inString:String;inNode:TTreeNode):TTReeNode; end; procedure Register; implementation procedure Register; begin RegisterComponents('Standard', [TCtrlTree]); end; { TCtrTree } function TCtrlTree.GetNumInSameLevel(inNode: TTreeNode): integer; {功能:产生已存在节点在兄弟节点层中对应的编号,从1起编 入口参数:inCode:TTreeNode节点 返回:同层编号 } var i:integer; tmp:TTreeNode; begin i:=0; tmp:=inNode; while tmp<>nil do begin tmp:=tmp.getPrevSibling; i:=i+1; end; Result:=i; end; function TCtrlTree.GetGlobeNumCode(inNode: TTreeNode): string; {功能:产生已存在节点对应的全局编号 入口参数:inCode:TTreeNode节点 返回:全局编号 } var nocode:string; tmp:TTreeNode; begin tmp:=inNode; nocode:=IntToStr(GetNumInSameLevel(tmp)); while tmp.Level<>0 do begin tmp:=tmp.Parent; nocode:=inttostr(GetNumInSameLevel(tmp))+'.'+nocode; end; Result:=nocode; end; function TCtrlTree.LocatOrGenerateNode(inNumCode: String): TTreeNode; {功能:根据提供的全局编号进行定位,如路径不全,则创建路径 在定位过程产生的节点的Text为Temp 最终返回对应于全局编号的子节点 入口参数:inNumCode:String为全局编号 返回:全局编号对应的字节点 } var i,j:Cardinal; NumInLevel:integer; tmp:TTreeNode; par:TTreeNode; begin tmp:=nil; i:=1; while i<=StrLen(PChar(inNumCode)) do begin //得到下一个点号的开始位 j:=GetPosAtBound(inNumCode,i); //得到在兄弟节点中的排行数 NumInLevel:=StrToInt(Copy(inNumCode,i,j-i+1)); //定位父节点 par:=GetParent(Copy(inNumCode,1,j)); //得到对应的节点 tmp:=LocateNodeInLevel(par,numInLevel); i:=j+2; end; Result:=tmp; end; function TCtrlTree.GetParent(inNumCode: String): TTreeNode; {功能:根据提供的全局编号找到对应的父节点 如果是第一层的节点,则父节点为nil 入口参数:inNumCode:String为全局编号 返回:全局编号对应的父节点 } var GoStep:integer; i:integer; j:integer; k:integer; SearChInNode:TTReeNode; ReturnNode:TTReeNode; begin //是第一层节点,返回nil; k:=GetTheLastPointPos(inNumCode); if k=0 then begin Result:=nil; Exit; end; //是第二层或第二层以上节点 i:=1; SearchInNode:=Items.GetFirstNode; while i < GetTheLastPointPos(inNumCode) do begin j:=GetPosAtBound(inNumCode,i); GoStep:=StrToInt(Copy(inNumCode,i,j-i+1)); if i=1 then //在第一层节点中搜索 begin ReturnNode:=SearchInNode; for k:=1 to GoStep-1 do ReturnNode:=ReturnNode.getNextSibling; end else //在第二层或第二层以上节点中搜索 begin GoStep:=StrToInt(Copy(inNumCode,i,j-i+1)); ReturnNode:=SearchInNode.Item[GoStep-1]; end; SearchInNode:=ReturnNode; i:=j+2 end; Result:=SearchInNode; end; function TCtrlTree.LocateNodeInLevel(parNode: TTReeNode;LevelCode: integer): TTReeNode; {功能:根据父节点以及在兄弟节点中的编号找到对应的节点 如果要创建兄弟及自己,则新创建的节点的Text为Temp 入口参数:parNode: TTReeNode为父节点 LevelCode: integer为编号 返回:在parNode中编号为LevelCode的孩子节点 } var i:integer; j:integer; tmp:TTreeNode; tmps:TTreeNode; begin //父节点为空,是第一层节点 tmp:=nil; if parNode=nil then begin i:=1; tmps:=Items.GetFirstNode; while (tmps<>nil) and (i<=LevelCode) do begin tmp:=tmps; tmps:=tmps.getNextSibling; i:=i+1; end; i:=i-1; for j:=1 to LevelCode-i do tmp:=Items.AddChild(nil,'Temp'); Result:=tmp; end else //父节点不为空,正常处理 begin if parNode.Count for i:= 1 to LevelCode-parNode.Count do Items.AddChild(parNode,'Temp'); Result:=parNode.Item[LevelCode-1]; end; end; function TCtrlTree.GetPosAtBound(inString: String;inStart:Integer): Integer; {功能:根据起始位置找到下一个'.'的前一个位置 入口参数:inString: String节点编号 inStart: Integer当前处理层次的起始位置 返回:当前处理层次的结束位置 } var tmp:Char; pos:integer; begin pos:=inStart+1; while pos <= Integer(StrLen(PChar(inString))) do begin tmp:=inString[pos]; if tmp='.' then Break else pos:=pos+1; end; Result:=pos-1; end; function TCtrlTree.GetTheLastPointPos(inString: String): Integer; {功能:找到编号中最后的'.'的位置 入口参数:inString: String为节点编号 返回:节点编号中最后的'.'的位置 } var tmp:Char; pos:integer; begin pos:=Integer(StrLen(PChar(inString))); while pos>=1 do begin tmp:=inString[pos]; if tmp='.' then Break else pos:=pos-1; end; Result:=pos; end; function TCtrlTree.InsertAsFinalChild(inString: String; inNode: TTreeNode):TTReeNode; {功能:为当前节点插入一个孩子节点,位置为最后 入口参数:inString: String为节点编号为待插入节点的字符串 inNode: TTreeNode,当前节点 } begin Result:=Items.AddChild(inNode,inString); end; function TCtrlTree.InsertAsPreviousSibling(inString: String; inNode: TTreeNode):TTReeNode; {功能:为当前节点插入一个前导的兄弟节点 入口参数:inString: String为节点编号为待插入节点的字符串 inNode: TTreeNode,当前节点 } begin Result:=Items.AddChildFirst(inNode,inString);end;end. //////////////////////////////////////////////////////////////// delphi中treeview图标 if TreeView.Items[i].parent = nil then begin TreeView.Items[i].ImageIndex := 0; end; if TreeView.Items[i].Text = '退出' then begin TreeView.Items[i].ImageIndex := 3; end; 通过这种方式,设几种图标都行 //////////////////////////////////////////////////////////////// 在Delphi 6 IDE中,最显眼的新功能可能就是Object TreeView了。由于Delphi 6比较新,介绍它的资料还很少,所以很多人不知道如何使用Object TreeView,甚至嫌它太占地方而将它关闭了。事实上,当窗体上的构件越来越多的时候,你才会发现Object TreeView的强大功能。 以下是关于Object TreeView功能的一个简单介绍,其主要内容取自《Mastering Delphi 6》,我只是做一些整理和翻译的工作罢了。希望能对使用Delphi 6的朋友有所帮助。 关于Object TreeView的使用 1.在Delphi 5的Data Module设计器中,使用了一个TreeView来显示非可视构件(如DataSet, Fields,Actions等等)之间的关系。Delphi 6中的Object TreeView则是它的扩展,它不仅对于Data Module,对于普通窗体和其他对象也可用。 如果Object TreeView当前不可见,而又想使用它,选择View | Object TreeView即可。 2.Object TreeView使用层次视图来显示窗体上的构件以及它们之间的关系。最典型的是父子关系:例如,所有Form上的构件都是Form的子节点;又如,放一 个Panel到窗体上,再往Panel上面放一个Button,则Button会成为Panel的子节点。 Object TreeView,Object Inspector和窗体设计器是同步的。这就是说,在窗体上选择一个构件,则在Object TreeView中也会自动选中该构件,同时Object Inspector中显示该构件的属性和事件;同理,在Object TreeView中选择一个构件,窗体上也会同时选择该构件,在Object Inspector中显示其属性和事件。因此,Object Inspector可以看作窗体设计器的一个增强,在窗体上构件比较多,而且彼此覆盖的时候,在Object Inspector种选择一个构件往往比从窗体上面选择或者从Object Inspector的列表中查找要方便快捷。 3.Object TreeView中还能够显示构件之间的其他关系。举例如下: (1)用Menu Designer设计菜单,所有菜单项按其在菜单中的层次显示在MainMenu节点下; (2)对于数据集构件,例如TTable,Object TreeView还会显示它在运行时刻将产生的一些对象,例如Session,Alias等,但由于它们是运行时刻才产生的,所以无法编辑它们的属性,在 Object TreeView中这些对象将以无名称的节点和一个特殊的图标来表示,并且在选中这些节点的时候,Object Inspector中没有属性可以编辑。 (3)对于ListView,Object TreeView还会列出它的Columns,在Columns上面点击右键,就可以直接编辑各个列。这比从Form Designer上面选择ListView,再选择Columns Editor来的方便。其他一些构件也有类似的情况,例如HeaderControl的Sections,CoolBar的Bands, StatusBar的Panels,都可以从快捷菜单中直接编辑。(ToolBar默认情况下虽然没有将Buttons列出,但是仍然可以在从 Object TreeView中ToolBar的快捷菜单中建立Button和Separator。) 4.想精确设置构件之间的Contains关系,Object TreeView也是一个有用的工具。可以通过下面的试验来验证: (1)在窗体上面放置一个Panel; (2)从构件面板上选择Button构件; (3)将鼠标定位在Object TreeView上面(但先不要按下)。你会注意到,当鼠标指向可以作为Container的构件,例如Form和Panel的时候,光标形状会变成一个 箭头带一页纸的形状,这说明构件可以放置到Container上面。相反,如果鼠标停留在ListBox,Label这样的构件上面,那么光标将会变成 No Drop形状,说明它们不能作为Container。 (4)将光标定位在Panel上面,并按下左键。Button将自动成为Panel的子节点,同时,Panel和Button也建立起了contains关系。 你可以很容易的改变构件之间的Contains关系。例如,在Object TreeView中拖动Button到Form上面,则现在拥有Button的Container就不再是Panel,而是Form本身了。在过去,这种 操作是通过Cut&Paste来完成的,但是Cut&Paste也有一个缺点,那就是一个构件被剪切后,它和其他构件之间的关联就丢失 了。而使用Object TreeView,不会有这种缺点。 在窗体上面的构件很多,并且彼此覆盖的时候,在Form Designer中查找和操作某个构件就变得相当麻烦。在这个时候,Object TreeView更能够显示它的作用。 Object TreeView和Code Editor中的Diagram视图配合,还能够发挥更为强大的作用。不过这些内容相当多,需要另外用一个专题来讲述了。 Object TreeView很有用,不过代价是要多占一点屏幕空间。由于Delphi IDE支持各个工具的Docking,你可以将Object TreeView和Object Inpector组合在一起,成为一个窗口,这样可以稍稍节省一点屏幕空间。Object TreeView和Object Inspector既可以左右排列,也可以上下排列,这两种方式各有优缺点。你可以选择你喜欢的方式,然后用Desktop工具栏上Save Desktop按钮,来将你的桌面设置保存起来供下次使用。
TreeView由节点构成,建树通过对TreeView.items属性进行操作。Items是一个TTreeNodes对象,这是一个TTreeNode集。
一、针对TTreeNodes,也就是 TreeView.Items,有这些属性:
1、count,节点个数。
2、item[index] ,通过index得到节点。
二、针对TTreeNodes,也就是 TreeView.Items,常用的添加节点的操作有:
AddFirst添加第一个根节点。由此函数添加的节点总排在前面,除非后来又使用此函数添加了一个节点,则后添加的节点将排在前面。该函数返回新添加的节点。
AddChildFirst添加第一个子节点,要求有父节点作为其参数。返回新添加的节点。
AddChild添加一个子节点,要求有父节点作为其参数。返回新添加的节点。
Add添加一个兄弟节点,要求有兄弟节点作为其参数。返回新添加的节点。
三、针对TTreeNodes,也就是 TreeView.Items,常用的得到节点的操作有:
GetFirstNode() 得到根节点。然后配合TTreeNode.GetNext(),就可以访问所有的节点。
四、建树举例:
var
root_node,cur_node:TTreeNode;
begin
root_node:=AddFirst(nil,根节点1);
cur_node:=addChildfirst(root_node,nil,根节点1_child1);
add(cur_node,根节点1_child2);
root_node:=Add(nil,根节点2);
AddChildFirst(root_node,根节点2_child1);
end;
五、事件触发:
当从一个节点跳到另一个节点,会触发TTreeView.OnChange事件。该事件中,将传递node,即当前被选中的节点。
当修改一个节点的text时,会触发TTreeView.onEdit事件。
六、将节点和节点所对应的数据联系起来
对于每个TTreeNode,有个Data属性,可以存放一个指针。我们可以利用这个域来存放与节点对应的自己的数据。
1.我们先定义一个数据结构,作为记录我们要记录的数据。如:
type PMyData=^TMyData; TMyData=Record sFName:string; sLName:String; nIndex:integer; end;
2.然后,创建数时,将节点和节点数据联系起来:
procedure TForm1.Button1Click(Sender: TObject); var myshuju: PMyData cur_node:TTreeNode; begin New(myshuju); //记住,一定要先分配内存。有几个节点,就要分配几次内存。 myshuju^.FName:=Edit1.Text; Myshuju^.LName := Edit2.Text; TreeViewIndex := StrToInt(Edit3.Text); with TreeView1 do begin cur_node:=items.AddFirst(nil,first); cur_node.data:=myshuju; end; end;
3.当我们选中一个节点时,就可以使用我们的数据了。
procedure TForm1.TreeView1Change(Sender:TObject;Node:TTreeNode); begin if node.data<>nil then self.label1.caption:=pmyData(node.data)^.Fname+pmyData(node.data)^.Lname end;
4.内存释放
procedure TForm1.ListView1Deletion(Sender: TObject; Item: TListItem); begin Dispose(Item.Data); end;
七、一般使用流程:
1、添加全局变量:
b_first:boolean; //记录是否是第一次访问节点,因为此时数据还未准备好,而一旦访问节点就会触发OnChange事件,在此事件处理函数中也许会出错。
2、在FormCreate中,
a、设置b_first:=true;
b. 创建数并将节点与数据联系。
3、在FormShow中设置b_first:=false;
4.在事件OnChange中处理节点被选中事件。
5.在Edit中处理节点被修改Text事件。并调用OnChange.
6.在 TreeView.Destory中 释放Data 中指向的内存空间。
另一篇相关介绍:
每一个节点下子节点形成这一节点的Items属性,当前节点有一个唯一的Index(TreeNode的Index属性),用于说明子节点在Items中的位置,每一个节点下的子节点是顺序编号的,第一个是0,第二个是1,依次类推。用IndexOf方法获得子节点的顺序,绝对顺序(AbsoluteIndex)则是指从Treeview第一个项开始的顺序值,第一个是0,如此推下去。Item属性则根据Index的值返回当前节点的第Index个子节点。Count则表明属于此项的所有子节点的数量。用MoveTo方法将Item由一个位置移到另一个位置。
Expanded属性表明是否所有的子项都全部展开(包括子项的子项),为True表示全部展开。IsVisible属性表明一个项是否在树中能被看到,如果树全部展开那么这个Item是肯定可以被看到。HasChildren属性表明一个项是否有子项。 GetFirstChild, GetLastChild, GetPrevChild, and GetNextChild分别返回当前项子项的第一个、最后一个和前一个、后一个项。GetNextSibling and GetPrevSibling则返回在同一Level下的下一个和上一个项。GetNextVisible and GetPrevVisible则返回能看得到的下一个和上一个项。如果一个节点有Parent,则HasAsParent方法返回True. Parent为当前项的父项。Focused属性确定焦点是否落在此节点上,被Focus时会一个标准的方框住。很显然,只有一个节点会被聚焦。 Selected属性表明一个节点是否被选中,同样只有一个节点会被选中。DropTarget属性表明节点在拖动操作中是源还是目标。
1.添加、删除、修改节点:
静态的方法可以在设计时通过Items的编辑器设置各节点的内容。
在添加和删除前必须保证有节点被选中(Treeview.Selected = nil)用AddFirst, AddFirstChild, AddChild等先添加根节点,如Treeview.Items.AddFirst( nil, 'Root');然后以此为基础,添加此项的子节点。
删除节点
Treeview.Selected.Delete
编辑节点内容
Treeview.Selected.EditText
注意:由于根节点没有父节点 (TTreeNode.Parent= nil)
此外,在大批量添加数据到Treeview中时最好使用 TreeView.Items.BeginUpdate;
添加节点
TreeView.Items.EndUpdate
这样能加快显示速度。
2.在节点上添加图象
Treeview中几个与图象相关的属性:
SelectedIndex:当节点被选中时在TimageList 中选什么样的图象
OverlayIndex:选那副图象作为掩图(一幅图象透明地显示在另一幅图象的前面),比如一个节点不可用时加一副X图象在其前面。
ImageIndex:在常态时选用的图的序号
StateIndex:在StateImages这个ImageList中对应的序号,-1时不显示图象
比较典型的,象在文件管理器中的所显示的一样,Treeview控件在节点之前也可以显示图象。在Form中放置一ImageList控件,加入几个图片,分别被Index为0,1,…在Treeview的Image属性项填入你所加入的ImageList的控件名称。TreeNode的ImageIndex表示节点未被选中时(Selected=nil)的图片序号,SelectedIndex表示节点被选中时图片序号。
TreeView 的用法
/************* TreeView 的用法开始******************// 一:TreeView.Items[0].Expanded := True; // 展开第一个节点 二:TreeView.Items[0].Item[0].Selected := True; // 移动到第一个节点的第一个子节点 找当前节点的下一个节点,按序号找如下: if treeview1.Selected.GetNext<>nil then treeview1.Selected.GetNext.Selected:=true; TreeView1.SetFocus; 找当前节点的下一个同层兄弟如下: if treeview1.Selected.getNextSibling<>nil then treeview1.Selected.getNextSibling.Selected:=true; TreeView1.SetFocus; TreeView.Selected.getPrevSibling //当前选中节点的上一个兄弟节点 TreeView.Selected.Parent // 当前选中节点的父节点 getfirstchild是跳到该节点子结点中的第一个 getlastchild是跳到该节点子结点中的最后一个 如果你是想跳到同层兄弟结点的第一个 if treeview1.selected.parent<>nil then treeview1.selected.parent.getfirstchild.selected:=true else treeview1.items.item[0].selected:=true; 如果你是想跳到同层兄弟结点的最后一个 if treeview1.selected.parent<>nil then treeview1.selected.parent.getlastchild.selected:=true else treeview1.Items.Item[treeview1.Items.Count-1].Selected:=true; TreeView的使用方法 基本信息: TreeView 是一个显示树型结构的控件,每一个节点都是一个新类, 使用具有代表性每个节点都有四个值: TEXT:显示文字 Image Index:显示图形序号 Selected Index: State Index: (1)建立目录项(本例中使用的TREEVIEW名称为:TvwTips) 增加根目录下的节点:(节点) var CatNode : TTreeNode; //先建立一个TREEVIEW使用的子对象 begin TvwTips.SetFocus; //将焦点置到这个TREEVIEW控件上 { 在根标题下建立一个新的子标题 } CatNode := TvwTips.Items.AddChild( TvwTips.Items.GetFirstNode,'New Category' ); CatNode.ImageIndex := 1; CatNode.SelectedIndex := 2; CatNode.EditText; { 允许用户改变这个标题 } end; 增加下一级目录(内容): var ParentNode, TipNode : TTreeNode; //先建立TREEVIEW使用 的子对象 VersionNum : Integer; begin TvwTips.SetFocus; //将焦点置到这个TREEVIEW控件上 VersionNum := TMenuItem( Sender ).Tag; { Ver num of new tip } ParentNode := TvwTips.Selected; { 取出当前的选中节点 } if ParentNode.Level = nlTip then{ Parent cannot be a tip node } ParentNode := TvwTips.Selected.Parent; TipNode := TvwTips.Items.AddChildObject( ParentNode,'New Subject',Pointer( VersionNum ) ); TipNode.ImageIndex := 3; { Normal tip bitmap } TipNode.SelectedIndex := 4; { Highlighted tip bitmap } TipNode.MakeVisible; { Move new tip node into view } TipNode.EditText; { Immediately allow user to edit subject } EnableTreeViewFunctions( TipNode.Level ); RtfTip.Clear; RtfTip.Modified := False; end; (2)说明 TvwTips.Items.GetFirstNode 返回TREEVIEW的第一个节点,函数类型为:TTreeNode TvwTips.Items.Count 返回当前TreeView的全部节点数,整数 TvwTips.Selected.Level 返回当前选中节点的在目录树中的级别, 根目录为0 TvwTips.Selected.Parent 返回当前选中节点上级节点,函数类型为 :TTreeNode 三、下面这段程序是用TREEVIEW连数据库及一些操作: unit bmzd; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, Db, DBTables, ImgList, Buttons, ExtCtrls, Grids, DBGrids; type Tfrmbmzd = class(TForm) Panel1: TPanel; cmd_new: TBitBtn; cmd_delete: TBitBtn; cmd_exit: TBitBtn; tvwcode: TTreeView; ScrollBox1: TScrollBox; GroupBox2: TGroupBox; Label3: TLabel; Label2: TLabel; Label1: TLabel; Label4: TLabel Label5: TLabel; Edit2: TEdit; Edit3:TEdit; Edit4: TEdit; Edit5: TEdit; ImageList1: TImageList;Edit1: TEdit; cmd_save: TBitBtn; cmd_update: TBitBtn; procedure FormShow(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure cmd_newClick(Sender: TObject); procedure cmd_deleteClick(Sender: TObject); procedure Edit2KeyPress(Sender: TObject; var Key: Char); procedure tvwcodeChange(Sender: TObject; Node: TTreeNode); procedure cmd_updateClick(Sender: TObject); procedure BitBtn2Click(Sender: TObject); procedure tvwcodeClick(Sender: TObject); private { Private declarations } function LoadCode(crTbl:TDBDataSet):Integer; function GetLevel(sFormat,sCode:String):Integer; public { Public declarations } end; var frmbmzd: Tfrmbmzd; ii:integer; tv:ttreenode; const SCodeFormat = '222222'; //科目代码结构 SFirstNodeTxt = '部门分类'; implementation uses dm; {$R *.DFM} function tfrmbmzd.LoadCode(crTbl:TDBDataSet):Integer; var NowID,sName,ShowTxt:String; i,Level:Integer; MyNode:array[0..6]of TTreeNode; //保存各级节点,最长支持6级(重点) begin Screen.Cursor:=crHourGlass; Level:=0; With frmdm.tabbm do begin try if not Active then Open; First; tvwCode.Items.Clear; //以下是增加第一项 MyNode[Level]:=tvwCode.Items.Add (tvwCode.TopItem,SFirstNodeTxt); MyNode[Level].ImageIndex:=0; MyNode[Level].SelectedIndex:=0; //以上是增加第一项 While Not Eof do begin NowID:=Trim(FieldByName('bumeng_id').AsString); ShowTxt:=FieldByName('bumeng_name').AsString; Level:=GetLevel(SCodeFormat,NowID); //返回代码的级数 //以下是增加子项 //以下用上一级节点为父节点添加子节点 if Level>0 then//确保代码符合标准 begin MyNode[Level]:=tvwCode.Items.AddChild (MyNode[Level-1],NowID+' '+ShowTxt); MyNode[Level].ImageIndex:=1; MyNode[Level].SelectedIndex:=2; end; //以上是增加子项 Next; end; finally Close; end; end; MyNode[0].Expand(False);//将首节点展开 Screen.Cursor:=crDefault; end; //以上函数将Code.db表中的科目代码和科目代码名称显示出来 //下面函数的功能是返回一代码的级数, //参数sFormat传递科目代码结构; //参数sCode传递某一科目代码 function tfrmbmzd.GetLevel (sFormat,sCode:String):Integer; var i,Level,iLen:Integer; begin Level:=-1;//如果代码不符合标准,则返回-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 Level:=i; Break; end; end; Result:=Level; end; procedure Tfrmbmzd.FormShow(Sender: TObject); begin if not frmdm.tabbm.active then frmdm.tabbm.Open; frmdm.tabbm.IndexFieldNames:='BUMENG_ID'; //按字段aCode排序(不要漏掉) LoadCode(frmdm.tabbm); end; procedure Tfrmbmzd.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end; procedure Tfrmbmzd.cmd_newClick(Sender: TObject); begin tvwcode.SetFocus ; if tvwcode.selected.AbsoluteIndex<>0 then begin cmd_delete.Enabled:=true; cmd_update.Enabled:=true; cmd_save.Enabled :=true; if not frmdm.tabbm.Active then frmdm.tabbm.Open; edit1.text:=frmdm.tabbm.Fields[0].asstring; end; tvwcode.SetFocus ; tvwcode.Items.AddChild(tvwcode.selected,'新建部门'); if tvwcode.Selected.HasChildren then begin if tvwcode.Selected.GetLastChild.index+1>9 then edit2.text:=inttostr(tvwcode.Selected.GetLastChild.index+1) else edit2.text:='0'+inttostr(tvwcode.Selected.GetLastChild.index+1); edit3.text:='新建部门'; edit2.Enabled:=true; edit3.Enabled:=true; end else begin edit2.text:='01'; edit3.text:='新建部门'; edit2.Enabled:=true; edit3.Enabled:=true; end; if not frmdm.tabbm.Active then frmdm.tabbm.Open; frmdm.tabbm.edit; frmdm.tabbm.Append; cmd_new.Enabled :=false; cmd_delete.Enabled :=false; cmd_update.Enabled :=false; cmd_save.Enabled :=true; //新建下级部门 tvwcode.Selected.GetLastChild.Selected:=true;//设选择 tv:=tvwcode.Selected ; tvwcode.autoExpand:=true; tvwcode.SetFocus ; end; procedure Tfrmbmzd.cmd_deleteClick(Sender: TObject); var I:integer; seno,setext:string; begin if tvwcode.Selected.HasChildren=true then begin application.messagebox('该部门包含下级,不能删除','提示',MB_OKCANCEL+mb_iconquestion ) ; end else begin if application.messagebox('你确实要删除当前部门吗','提示',MB_OKCANCEL+mb_iconquestion ) =IDOK then begin setext:=tvwcode.selected.text; i:=0; while setext[i] <> ' ' do begin I := I + 1; seno:=seno+setext[i]; end; if not frmdm.tabbm.Active then frmdm.tabbm.Open; frmdm.tabbm.setkey; frmdm.tabbm.Fields[0].asstring:=seno; if frmdm.tabbm.GotoKey then begin frmdm.tabbm.edit; frmdm.tabbm.Delete; end; tvwcode.Selected.Delete ; cmd_new.Enabled :=true; tvwcode.autoExpand:=true; tvwcode.SetFocus ; end; end; end; procedure Tfrmbmzd.Edit2KeyPress(Sender: TObject; var Key: Char); begin if key in ['0'..'9',Chr(vk_Back)] then else begin key:=#0; application.messagebox('','',mb_ok); end; end; procedure Tfrmbmzd.tvwcodeChange(Sender: TObject; Node: TTreeNode); var i,lev:integer ; strr,seno,setext:string; begin if cmd_new.Enabled =true then begin if tvwcode.selected.AbsoluteIndex<>0 then begin cmd_delete.Enabled:=true; cmd_update.Enabled:=true; cmd_save.Enabled :=true; setext:=tvwcode.selected.text; i:=0; while setext[i] <> ' ' do begin I := I + 1; seno:=seno+setext[i]; end; if not frmdm.tabbm.Active then frmdm.tabbm.Open; frmdm.tabbm.setkey; frmdm.tabbm.Fields[0].asstring:=seno; frmdm.tabbm.GotoKey; strr:=''; case tvwcode.Selected.Level of 2: strr:=copy(frmdm.tabbm.fields[0].asstring,1,2); 3: strr:=copy(frmdm.tabbm.fields[0].asstring,1,4); end; edit1.text:=strr; //加上级编码 edit3.text:=frmdm.tabbm.Fields[1].asstring; //本级编码 edit2.text:=copy(frmdm.tabbm.fields[0].asstring,length(frmdm.tabbm.fields[0].asstring)-1,2); end else begin edit1.text :=''; edit2.text :=''; edit3.text :='部门分类'; cmd_delete.Enabled :=false; cmd_update.Enabled :=false; end; end; end; procedure Tfrmbmzd.cmd_updateClick(Sender: TObject); var i:integer; begin for i:=0 to ComponentCount-1 do begin if (Components[I] is tedit ) then (Components[I] as tedit).enabled:=false; end; for i:=0 to ComponentCount-1 do begin if (Components[I] is tbitbtn ) then (Components[I] as tbitbtn).enabled:=true; end; if tvwcode.Selected.AbsoluteIndex<>0 then begin if not frmdm.tabbm.Active then frmdm.tabbm.Open; frmdm.tabbm.Edit; frmdm.tabbm.Fields[0].asstring:=edit1.text+edit2.text; frmdm.tabbm.Fields[1].asstring:=edit3.text; try frmdm.tabbm.Post; //表的保存 except on edbengineerror do begin edit2.Enabled :=true; application.messagebox('编号不能重复,请重新输入','提示',MB_OK+mb_iconquestion ) ; exit; end; end; tvwcode.Selected.Text:=edit1.text+edit2.text+' '+edit3.text; tvwcode.SetFocus ; //修改树 end; cmd_new.Enabled :=true; edit2.Enabled :=false; end; procedure Tfrmbmzd.BitBtn2Click(Sender: TObject); begin cmd_new.Enabled :=false; cmd_delete.Enabled :=false; edit2.enabled:=true; edit3.Enabled :=true; end; procedure Tfrmbmzd.tvwcodeClick(Sender: TObject); var i:integer; begin if cmd_new.Enabled =false then begin application.MessageBox('当前是修改状态,请单击保存按钮','提示',mb_ok); tv.Selected :=true; end; if tvwcode.selected.AbsoluteIndex=0 then begin for i:=0 to ComponentCount-1 do begin if (Components[I] is tedit ) then (Components[I] as tedit).enabled:=false; end; for i:=0 to ComponentCount-1 do begin if (Components[I] is tbitbtn ) then (Components[I] as tbitbtn).enabled:=false; cmd_new.Enabled :=true; cmd_exit.Enabled :=true; end; end; end; end 四、使用概述 树形图(Treeview)是Win95下新增加的通用显示部件(Common Control,在COMCTL32.DLL中)之一,从 Delphi2.0开始也增加了相应的控件Treeview,用于取代原Outline控件。由于树形图结构较复杂,使用起 来常不知如何下手。这里就使用中的一些问题作些介绍。 Treeview用于显示按照树形结构进行组织的数据,这在实际当中用途还是比较广泛的,如计算机中的 文件系统(Windows95中的资源管理器)、企业或公司的组成结构等等。Treeview控件中一个树形图由节点 (TreeNode)和连接线组成。TtreeNode是TTreeview的基本组成单元。一个树的节点又包含文本(Text)和数 据(Data)。Text为String类,Data则为无定形指针(Untyped Pointer),可以指向一个与节点相联系的数 据结构。 每一个节点下子节点形成这一节点的Items属性,当前节点有一个唯一的Index(TreeNode的Index属性 ),用于说明子节点在Items中的位置,每一个节点下的子节点是顺序编号的,第一个是0,第二个是1,依 次类推。用IndexOf方法获得子节点的顺序,绝对顺序(AbsoluteIndex)则是指从Treeview第一个项开始的 顺序值,第一个是0,如此推下去。Item属性则根据Index的值返回当前节点的第Index个子节点。Count则 表明属于此项的所有子节点的数量。用MoveTo方法将Item由一个位置移到另一个位置。 Expanded属性表明是否所有的子项都全部展开(包括子项的子项),为True表示全部展开。IsVisible 属性表明一个项是否在树中能被看到,如果树全部展开那么这个Item是肯定可以被看到。HasChildren属 性表明一个项是否有子项。 GetFirstChild, GetLastChild, GetPrevChild, and GetNextChild分别返回 当前项子项的第一个、最后一个和前一个、后一个项。GetNextSibling and GetPrevSibling则返回在同 一Level下的下一个和上一个项。GetNextVisible and GetPrevVisible则返回能看得到的下一个和上一个 项。如果一个节点有Parent,则HasAsParent方法返回True. Parent为当前项的父项。Focused属性确定焦 点是否落在此节点上,被Focus时会一个标准的方框围住。很显然,只有一个节点会被聚焦。 Selected属 性表明一个节点是否被选中,同样只有一个节点会被选中。DropTarget属性表明节点在拖动操作中是源还 是目标。 1.添加、删除、修改节点: 静态的方法可以在设计时通过Items的编辑器设置各节点的内容。 在添加和删除前必须保证有节点被选中(Treeview.Selected = nil) 用AddFirst, AddFirstChild, AddChild等先添加根节点,如Treeview.Items.AddFirst( nil, 'Root'); 然后以此为基础,添加此项的子节点。 删除节点Treeview.Selected.Delete 编辑节点内容Treeview.Selected.EditText 注意:由于根节点没有父节点 (TTreeNode.Parent= nil) 此外,在大批量添加数据到Treeview中时最好使用 TreeView.Items.BeginUpdate; 添加节点 TreeView.Items.EndUpdate 这样能加快显示速度。 2.在节点上添加图象 Treeview中几个与图象相关的属性: SelectedIndex:当节点被选中时在TimageList 中选什么样的图象 OverlayIndex:选那副图象作为掩图(一幅图象透明地显示在另一幅图象的前面),比如一个节点不可用 时加一副X图象在其前面。 ImageIndex:在常态时选用的图的序号 StateIndex: 在StateImages这个ImageList中对应的序号,-1时不显示图象 比较典型的,象在文件管理器中的所显示的一样,Treeview控件在节点之前也可以显示图象。在Form中放 置一ImageList控件,加入几个图片,分别被Index为0,1,…在Treeview的Image属性项填入你所加入的 ImageList的控件名称。TreeNode的ImageIndex表示节点未被选中时(Selected=nil)的图片序号, SelectedIndex表示节点被选中时图片序号。 3.关于Level Level的概念可以用下图表示:Level0 Level1 Level2 4.排序 SortType决定什么时候进行排序; TreeView.AlphaSort对节点进行排序,如果不能满足要求,你可以定义自己的CustomSort方法。 5.Drag&Drop操作,与标准的拖放操作使用方法一样。 五、一个例子: 首先,在库的构建上要有一定策略以下几个字段非常重要:'codeID' 代码集名称 (例如:'AB') 'code' 节点代码 (例如:'新疆'节点的代码为'65','乌鲁木齐'的节点代码为'6501'等) 'description' 节点描述 (例如:如上面的'新疆'、'乌鲁木齐'等) 'pptr'父节点代码 (例如:'乌鲁木齐'节点的父代码为'65',最高层节点的父节点代码可同其代码集, 例如:'新疆'的父节点为 'AB')'cptr'子节点标识 (例如:该节点是否有下级节点,有则取值'1',没 有则取值'0')建立树由两部分构成: procedure TForm1.FormActivate(Sender: TObject); var code_ID,new_sql,node_text,table_name:string; new_node:Ttreenode; begin //要传递的参数 code_ID:='AB'; table_name:='sr_codeitem'; //******** treeview1.Items.BeginUpdate; //清空所有项目 treeview1.Items.Clear; new_sql:='select * from '+ table_name + ' where codeID='+''''+code_ID+'''' + ' and '+'pptr='+''''+code_ID+''''; //数据集中先筛选出所有要增加节点 with dm1.Query1 do begin close; sql.Clear; sql.Add(new_sql); open; //增加第一层节点 while not eof do begin node_text:=fieldbyname('code').asstring; node_text:=node_text+' '+fieldbyname('description').asstring; new_node:=treeview1.Items.add(nil,node_text); new_node.HasChildren:=true; next; end;//end while end;//end with treeview1.Items.EndUpdate; end; procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var new_sql,node_text,child_text,code_ID,table_ID:string; child_node:Ttreenode; begin //获取参数 code_ID:='AB'; table_ID:='sr_codeitem'; treeview1.Items.BeginUpdate; node_text:=node.Text; //截取所点击节点的code node_text:=copy(node_text,1,pos(' ',node_text)-1); with dm1.Query1 do begin close; sql.Clear; new_sql:='select * from '+table_ID+' where pptr='+''''+node_text+'''' + ' and codeID=' +''''+code_ID+''''; sql.Add(new_sql); open;//定位所点的项 //清除原来的下级节点 node.DeleteChildren; while not eof do begin//增加下级节点 child_text:=fieldbyname('code').asstring+' '+fieldbyname('description').asstring; child_node:=treeview1.Items.AddChild(node,child_text); if fieldbyname('cptr').asstring='1' then child_node.HasChildren:=true; next; end;//end while end;//end with treeview1.Items.EndUpdate; end; 第二部分:在 treeview 的onExpanding 事件中添加如下代码 procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var new_sql,node_text,child_text,code_ID,table_ID:string; child_node:Ttreenode; begin //获取参数 code_ID:='AB'; table_ID:='sr_codeitem'; treeview1.Items.BeginUpdate; node_text:=node.Text; //截取所点击节点的code node_text:=copy(node_text,1,pos(' ',node_text)-1); with dm1.Query1 do begin close; sql.Clear; new_sql:='select * from '+table_ID+' where pptr='+''''+node_text+'''' + ' and codeID=' +''''+code_ID+''''; sql.Add(new_sql); open;//定位所点的项 //清除原来的下级节点 node.DeleteChildren; while not eof do begin//增加下级节点 child_text:=fieldbyname('code').asstring+' '+fieldbyname('description').asstring; child_node:=treeview1.Items.AddChild(node,child_text); if fieldbyname('cptr').asstring='1' then child_node.HasChildren:=true; next; end;//end while end;//end with treeview1.Items.EndUpdate; end; 六、让我们一个一个来回答: 第一个:取出节点的text属性值,赋给Richedit的Lines.text即可; 例如: self.RichEdit1.Lines.text:= self.Treeview1.selected.text; 第二个:TtreeView控件有函数保存和载入树的结构到文件 例如:self.Treeview1.SaveToFile('文件名');//保存结构 self.TreeView1.LoadFromFile('文件名');//载入结构 第三个:TreeView的每个节点都有ItemID(句柄),AbsoluteIndex(相对于树头节点的索引位置), Index(在当前层的位置),Level(本节点的层数)通过访问这些可以知道其位置; 例如:self.TtreeView1.selected.ItemID;//返回句柄 self.TreeView1.selected.Level;//返回层数 self.TreeView1.Items.count//返回所有节点的个数; **寻找某一个节点的话,就只有写一个循环来遍历整个树,直到找到符合条件的节点为止,关于查找节点 的代码我也在想,先参考上面同志们的回答吧;??**为某个节点加上子节点,例如: var NewNode:TTreeNode; begin NewNode:=self.Treeview1.Items.addChild(self.Treeview1.Items[3],'新的字节点'); //为self.Treeview1.Items[3]加上一个字节点,标签是'新的字节点', //还有其他的ADD方法,可以查找Delphi帮助 //然后可以给刚加上去的子节点赋值; NewNode.ImageIndex:=n; NewNode.SelectedIndex:=M; NewNode.Data:=X;//注意X是一个指针类型;关于这方面的帮助 //可以找一下TTreeNode.Data 属性例子 end; ??**想选中某个节点:self.TreeView1.Items[n].selected:=true;//这样就选中了 ??**展开某节点就容易了:self.TreeView1.Items[n].Expand;//展开啦! //想合上就用Collapse(Recurse: Boolean)函数, 总之呢,去查一下TtreeNode的帮助吧! ??**清除某节点下的所有子节点,用DeleteChildren是没错的,例如: self.Treeview1.Items[n].DeleteChildren;//OK,下面都没了! 七、query1.close; query1.sql.clear; query1.sql.add('select * from t08 order by t0801'); try query1.open; except on e:exception do begin application.MessageBox(pchar('打开数据库文件失败!'+#13+#13+e.Message),'错误信息',16); close; exit; end; end; //创建结构树 while not query1.eof do begin s:=trim(query1.fields[0].asstring); case length(s) of 2:begin tv1.Selected:=tv1.items.add(nil,trim(query1.fields[1].asstring)); tv2.Selected:=tv2.items.add(nil,trim(query1.fields[0].asstring)); end; 4:begin p1:=tv1.items.addchild(tv1.Selected,trim(query1.fields[1].asstring)); p2:=tv2.items.addchild(tv2.Selected,trim(query1.fields[0].asstring)); end; 6:begin tv1.Items.addchild(p1,trim(query1.fields[1].asstring)); tv2.Items.addchild(p2,trim(query1.fields[0].asstring)); end; end; query1.next; end; query1.close; if tv1.Items.Count>0 then begin tv1.Selected:=tv1.Items[0]; tv1.Selected.Expand(true); end; tv1.OnClick(self); 八、clear速度慢的问题 问题的关键在于Clear方法会一个一个地删除每个Node, 于是TreeView不断刷新,所以速度就.... 代码修正一下: with MyTreeView.Items do <B>try</B> BeginUpdate; Clear; <B>finally</B> EndUpdate; <B>end;</B> 另外 1、你现在遇到的问题是大量删除, 如果是大量插入,也应该同样处理 2、ListView有同样问题,注意 其他Windows控件也有,但不很明显 比如Memo、RichEdit等等总之,凡大量数据修改的情况下,应该注意BeginUpdate和EndUpdate 3、<B>BeginUpdate和EndUpdate应该一一对应</B>, 因为Delphi实际上是用<B>计数器</B>来处理的, 如果忘了EndUpdate,或者BeginUpdate比EndUpdate次数多, 可能导致控件不刷新了 //************* TreeView 的用法结束******************//
TreeView直接连接数据表
认真研究如下代码:DBTreeView--TreeView直接连接数据表 unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, DBTables, ComCtrls, Grids, DBGrids, ExtCtrls, DBCtrls, Mask, ImgList; type TForm1 = class(TForm) TreeView1: TTreeView; ImageList1: TImageList; DataSource1: TDataSource; DBEdit1: TDBEdit; Label1: TLabel; Label2: TLabel; DBEdit2: TDBEdit; Table1: TTable; Label3: TLabel; DBNavigator1: TDBNavigator; Label4: TLabel; DBEdit3: TDBEdit; DBEdit4: TDBEdit; procedure FormCreate(Sender: TObject); procedure TreeView1Change(Sender: TObject; Node: TTreeNode); procedure DataSource1StateChange(Sender: TObject); procedure Table1AfterInsert(DataSet: TDataSet); procedure Table1BeforeDelete(DataSet: TDataSet); procedure Table1BeforeEdit(DataSet: TDataSet); procedure Table1AfterDelete(DataSet: TDataSet); procedure Table1AfterPost(DataSet: TDataSet); private function GetFieldList: TStringList; { Private-Declarationen } public { Public-Declarationen } end; var Form1: TForm1; FieldList: TStringList; implementation uses TreeFunc; {$R *.DFM} function TForm1.GetFieldList: TStringList; begin FieldList.clear; FieldList.add(Table1.fieldbyname('Country').asstring); FieldList.add(Table1.fieldbyname('city').asstring); FieldList.add(Table1.fieldbyname('Company').asstring); Result := FieldList; end; procedure TForm1.FormCreate(Sender: TObject); begin FieldList := TStringList.create; TreeView1.items.BeginUpdate;//forbid treeview update Table1.first; while not Table1.eof do begin TreeAddItem(TreeView1, GetFieldList, Table1.getBookmark, false);//生成结点 Table1.next; end; FieldList.clear; TreeView1.Alphasort; TreeView1.items.Endupdate; //make first record selected: TreeView1.items[2].selected := true; end; procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode); begin Datasource1.enabled := Node.data <> nil; if DataSource1.enabled then Table1.Gotobookmark(node.data); end; procedure TForm1.DataSource1StateChange(Sender: TObject); var ItemList: TStringList; Node: TTreeNode; begin end; procedure TForm1.Table1AfterInsert(DataSet: TDataSet); begin FieldList.clear; end; procedure TForm1.Table1BeforeDelete(DataSet: TDataSet); begin GetFieldList; end; procedure TForm1.Table1BeforeEdit(DataSet: TDataSet); begin GetFieldList; end; procedure TForm1.Table1AfterDelete(DataSet: TDataSet); var CascadeDeleteLevel: Integer; begin CascadeDeleteLevel := 0; TreeDeleteItem(TreeView1, FieldList, CascadeDeleteLevel); end; procedure TForm1.Table1AfterPost(DataSet: TDataSet); begin TreeView1.items.beginUpdate; if FieldList.count > 0 then TreeDeleteItem(TreeView1, Fieldlist, 0); TreeView1.selected := TreeAddItem(TreeView1, GetFieldlist, Table1.getbookmark, True); TreeView1.items.endUpdate; end; end. ///------------------------ unit TreeFunc; interface uses Windows, Messages, SysUtils, Classes, Graphics, ComCtrls, DB, Forms, Dialogs; function TreeFindItem(Sender: TTreeView; NodeItem: TTreeNode; Name: String): TTreeNode; function TreeAddItem(Sender: TTreeView; ItemList: TStrings; Bookmark: TBookmark; Resort: Boolean): TTreeNode; function TreeGetItem(Sender: TTreeView; ItemList: TStrings): TTreeNode; procedure TreeDeleteItem(Sender: TTreeView; ItemList: TStrings; Level: Integer); implementation function TreeAddItem(Sender: TTreeView; ItemList: TStrings; Bookmark: TBookmark; Resort: Boolean): TTreeNode; var ThisNode, Node: TTreeNode; I: Integer; begin Node := nil; //nil = level 0 has no parent node //this is checked by TreeFindItem for I := 0 to Itemlist.count -1 do begin //for ThisNode := TreeFindItem(Sender, node, Itemlist[i]); if ThisNode <> nil then Node := ThisNode else begin if I < Itemlist.count -1 then begin if I = 0 then Node := Sender.items.Add(Node, Itemlist[i]) else Node := Sender.items.AddChild(Node, Itemlist[i]); end else begin if I = 0 then Node := Sender.items.AddObject(Node, Itemlist[i], Bookmark) else Node := Sender.items.AddChildObject(Node, Itemlist[i], Bookmark); end; Node.stateIndex := Node.level + 1; if Resort and (Node.parent <> nil) then Node.parent.alphasort; end; end; //for Result := Node; end; function TreeFindItem(Sender: TTreeView; NodeItem: TTreeNode; Name: String): TTreeNode; begin if NodeItem = nil then NodeItem := Sender.items.getfirstnode else NodeItem := NodeItem.getfirstchild; //NodeItem is now the first item of the desired level //if this level has no items, NodeItem is nil if (NodeItem <> nil) and (NodeItem.text <> Name) then repeat NodeItem := NodeItem.getnextsibling; until (NodeItem = nil) or (NodeItem.text = Name); Result := NodeItem; end; function TreeGetItem(Sender: TTreeView; ItemList: TStrings): TTreeNode; begin Result := TreeAddItem(Sender, Itemlist, nil, false); end; procedure TreeDeleteItem(Sender: TTreeView; ItemList: TStrings; Level: Integer); var Node, Parent: TTreeNode; begin Node := TreeGetItem(Sender, ItemList); while Node.level >= Level do begin Parent := Node.parent; Node.delete; if (Parent = nil) or (Parent.hasChildren) then break; Node := Parent; end; end; end.
实现TreeView结点拖拽的实例
Delphi 实现TreeView结点拖拽的实例(转) 2010-09-16 21:03 Delphi 实现TreeView结点拖拽的实例 2010-06-04 11:15 转载自 BD枫枫 最终编辑 BD枫枫 下面的程序片段演示了如何实现拖拽treeview构件结点的例子 {鼠标按下时执行的语句} procedure TForm1.Treeview1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin {判断左键按下并且鼠标点在一个结点上开始实现拖拽} if ( Button = mbLeft ) and ( htOnItem in Treeview1.GetHitTestInfoAt( X, Y ) ) then begin Treeview1.BeginDrag( False ); end; end; {鼠标拖动执行语句} procedure TForm1.Treeview1DragOver( Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var Node : TTreeNode; begin if Source = Treeview1 then begin Node := Treeview1.GetNodeAt( X, Y ); {取当前结点} if Node <> nil then {当前结点不为空才能实现拖拽,accept:=true} Accept := true; end; end; {鼠标释放时执行的语句} procedure TForm1.Treeview1DragDrop( Sender, Source: TObject; X, Y : Integer ); var TempNode : TTreeNode; AttachMode : TNodeAttachMode; begin if Treeview1.Selected = nil then Exit; AttachMode := naAddChild; {设置结点移动模式,设移动结点为子结点} { 注意在这里存在一个bug,当移动结点时,如果目标结点没有子结点,} { 则加入的新的子结点会失败,所以先在当前目标结点的下面 } { 加入一个临时子结点,移动完毕后,再将临时结点删除 } Treeview1.Items.BeginUpdate; try TempNode := Treeview1.Items.AddChild( Treeview1.DropTarget, 'Temp' ); try { 移动选中的结点到目标结点 } Treeview1.Selected.MoveTo( Treeview1.DropTarget, AttachMode ); finally TempNode.Free; { 不要忘了释放临时结点 } end; finally Treeview1.Items.EndUpdate; end; end; 详情地址: DELPHI中利用TreeView控件建立目录树 2010-06-09 15:54 关于TreeView的使用,还可以参看:联合使用TreeView 组件 TreeView是一个显示树型结构的控件,通过它能够方便地管理和显示具有层次结构的信息,是Windows应用程序的基本控件之一。DELPHI虽然具有比较强大的文件管理功能,提供了多个用于文件管理的标准控件,如DriveComboBox、DirectoryListBox、FileListBox等,通过设置它们的属性,使其建立起联系,甚至不用编写一行程序,我们就可以实现在不同的目录之间进行切换,然而这样的目录切换只适用于进行文件的查找定位,而不能方便地进行目录的浏览,例如我们要从c:windows目录转到c:program files目录,就必须返回到根目录才能进行切换,而不能象Windows资源管理器那样任意地在不同的目录之间进行浏览与切换。 要实现在不同目录之间任意切换和浏览,还是需要使用TreeView控件,以下程序就利用DELPHI的TreeView控件来建立目录树。 在该程序中采用的各部件以及界面设计如下图所示: 各部件的主要属性设置如下: 部件 属性 属性值 form name caption form1 ‘目录浏览’ drivecommbobox name visible drivecommbobox1 false filelistbox name visible filetype filelistbox1 false fddirectory imagelist name imagelist1 treeview name images 该程序利用DriveCommboBox控件来获得系统具有的驱动器,并以此作为目录树的最上层,利用FileListBox控件,通过设置其Filetype属性为fdDirectory,可以获得所需的子目录,在TreeView控件的OnExpanding事件中将得到的子目录加到该控件的某一节点下。 整个程序的源代码如下: unit main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,StdCtrls, FileCtrl, ComCtrls, ImgList; type TForm1 = class(TForm) DirTreeView: TTreeView; FileListBox1: TFileListBox; DriveComboBox1: TDriveComboBox; ImageList1: TImageList; procedure FormCreate(Sender: TObject); procedure DirTreeViewExpanding(Sender: TObject; Node: TTreeNode;var AllowExpansion: Boolean); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); var FirstNode,DirNode : TTreeNode; ItemCount,Index:integer; Itemstr:string; begin ItemCount:= DriveComboBox1.Items.Count; //所有驱动器的个数 FirstNode := DirTreeView.Items.GetFirstNode; for index := 0 to ItemCount -1 do begin ItemStr:= DriveComboBox1.Items[index]; ItemStr:= copy(ItemStr,1,pos(:,ItemStr)) ; //获得驱动器的名称(比如C/D) DirNode := DirTreeView.Items.AddChild(FirstNode, ItemStr ); DirNode.HasChildren := true; DirNode.ImageIndex := 0; DirNode.SelectedIndex := 1; end; end; //响应扩展事件 procedure TForm1.DirTreeViewExpanding(Sender: TObject; Node: TTreeNode;Var AllowExpansion: Boolean); var DirNode : TTreeNode; ItemCount,Index,level,icount:integer; Itemstr,strPath:string; begin if node.Count = 0 then begin icount:=0; level:=node.Level ; dirnode:=node; strPath:=node.Text+ ; while level 0 do begin strPath:=dirnode.Parent.Text++strpath; dirnode:=dirnode.parent; level :=level -1; end; FileListBox1.Clear ; FileListBox1.Directory := strpath; ItemCount:= FileListBox1.Items.Count; for index:=0 to ItemCount -1 do begin itemstr:=filelistbox1.items[index]; itemstr:= copy(ItemStr,2,pos(],ItemStr)-2) ; if (itemstr〈〉.) and (itemstr 〈〉 ..) then begin DirNode := DirTreeView.Items.AddChild(Node,itemstr ); DirNode.HasChildren :=true; DirNode.ImageIndex := 0; DirNode.SelectedIndex := 1; icount:=icount+1; end; if icount = 0 then Node.HasChildren := false; end; end; end; end. 程序的运行效果如图所示:我们可以展开目录树中的任何一个节点,并且可以在任意节点之间切换,就象我们在Windows资源管理器中所作的那样,而不需要逐级回退之后才能进行切换。
递归加载树形(TreeView)列表
Delphi递归加载树形(TreeView)列表 //采用递归方法,D7编译调试通过。 //数据采用ADOQuery读取,并将数据暂存在一个动态数组中,树形列表控件为TreeView。 procedure TForm1.LoadTreeInfo; type TInfo = record ID, //代码 Name, //名称 SuperID //上级代码 : string; //附加字段随需添加 end; var sql: string; i, nCount: Integer; arrInfo: array of TInfo; NewNode: TTreeNode; //加载一个节点 procedure InitOneNode(ANode: TTreeNode; AId: string); var k: Integer; begin for k := 0 to length(arrInfo) - 1 do if arrInfo[k].SuperID = AId then begin NewNode := TreeView1.Items.AddChild(ANode, arrInfo[k].Name); InitOneNode(NewNode, arrInfo[k].ID); end; end; begin TreeView1.Items.BeginUpdate; TreeView1.Items.Clear; sql := 'select ID, Name, SuperID from DictionaryTable order by ID'; ADOQuery1.Close; ADOQuery1.SQL.Text := sql; ADOQuery1.Open; nCount := ADOQuery1.RecordCount; if nCount > 0 then begin SetLength(arrInfo, nCount); for i := 0 to nCount - 1 do with arrInfo[i] do begin ID := Trim(ADOQuery1.FieldByName('ID').AsString); Name := Trim(ADOQuery1.FieldByName('Name').AsString); SuperID := Trim(ADOQuery1.FieldByName('SuperID').AsString); //无没有此字段,可根据上下级编码规则赋值 ADOQuery1.Next; end; end; ADOQuery1.Close; if nCount > 0 then begin InitOneNode(nil, ''); //假设顶级代码为空白 TreeView1.FullExpand; TreeView1.Items.EndUpdate; end; end; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 递归获取treeview选中父节点的所有子节点 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) TreeView1: TTreeView; Button1: TButton; ListBox1: TListBox; procedure Button1Click(Sender: TObject); procedure AddNodeList(NodeFirst:TTreeNode); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var NodeTest:TTreeNode; begin NodeTest:= TreeView1.Selected.getFirstChild; // 可以添加适当的节点过滤条件--genispan ListBox1.Items.Add(NodeTest.Text); AddNodeList(NodeTest); end; procedure TForm1.AddNodeList(NodeFirst:TTreeNode); begin if NodeFirst.getNextSibling<>nil then begin ListBox1.Items.Add(NodeFirst.getNextSibling.Text); AddNodeList(NodeFirst.getNextSibling); end; end; end. ====================================================================== . 实现对TreeView的遍历 function TForm1.AllOverTreeView(node:TTreenode):TTreenode; begin while node<>nil do begin if node.HasChildren then begin node:=node.getFirstChild; allovertreeview(node); node:=node.Parent; end; if node.getNextSibling<>nil then node:=node.getNextSibling else exit; end; end; procedure TForm1.Button1Click(Sender: TObject); var parentnode:TTreenode; begin parentnode:=Mytreeview.Items.GetFirstNode; AllOverTreeView(parentnode); end; ============================================================================== 数据库表TreeView树的快速生成 收藏 根据数据表的内容生成TreeView树状结构,通常的做法就是从顶级开始,然后逐项递归查询遍历生成。这种方法在实现上容易做到,也很容易想到,但是效率比较低,因为数据库的检索(SQL语句需要解释执行,而且是对数据库文件进行操作)还是比较耗时的,尤其是树的层次较多,节点较多的情况。这里我要介绍的方法是以空间换取时间,只进行一次数据库检索,提取出全部数据,然后一次生成TreeView树状结构。通过SQL语句,让返回的记录按照父节点ID、节点ID进行排序,这样保证每次当前要添加的节点记录的父节点都已经添加到了TreeView树中,剩下的工作就是如何在TreeView树中找到父节点。这里我采用了一个排序的TStringList列表,通过排序列表采用二分查找的快速性能,就能够很快地查找到当前要添加节点的父节点,从而插入到 TreeView树的正确位置。 源代码如下(假定数据表名称为FTree,字段有ID, ParentID, Name): procedure MakeTree(Query: TQuery; TreeView: TTreeView); var List: TStringList; Node: TTreeNode; Index: Integer; begin TreeView.Items.BeginUpdate; try TreeView.Items.Clear; List := TStringList.Create; try List.Sorted := True; while not Query.Eof do begin if Query.FieldByName('ParentID').AsInteger = 0 then { ParentID=0,顶层节点 } Node := TreeView.Items.AddChild(nil, Query.FieldByName('Name').AsString) else begin Index := List.IndexOf(Query.FieldByName('ParentID').AsString); Node := TreeView.Items.AddChild(TTreeNode(List.Objects[Index]), Query.FieldByName('Name').AsString); end; List.AddObject(Query.FieldByName('ID').AsString, Node); Query.Next; end; finally List.Free; end; finally TreeView.Items.EndUpdate; end; end; procedure TForm1.Button1Click(Sender: TObject); var T: DWORD; begin T := GetTickCount; Query1.SQL.Text := 'SELECT * FROM FTree ORDER BY ParentID, ID'; Query1.Open; MakeTree(Query1, TreeView1); Label1.Caption := Format('MakeTree所用时间: %d ms', [GetTickCount - T]); end; ============================================================================== unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, ADODB; type PTNode=^TNode; TNode=record id:string; fID,nID:string; hasCreate:boolean; fChild:PTNode; nSibling:PTNode; end; type TArr=array of TNode; type TTree=class(TObject) root,cur:PTNode; constructor create; overload; constructor create(var arr:TArr); overload; function FindNode(s:string;p:PTNode):boolean; end; type TForm1 = class(TForm) Button1: TButton; conn: TADOConnection; Query: TADOQuery; Memo1: TMemo; procedure DisPlayFChild(p:PTNode); procedure LoadData(var arr:Tarr); function GetNode(var arr:Tarr;s:string):PTNode; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.displayFChild(p:PTnode); begin if p.fChild<>nil then begin memo1.Lines.Add(p.fchild.id); end; end; function TTree.FindNode(s:string;p:PTNode):boolean; var pn:PTNode; begin result:=false; if p.id=s then begin result:=true; cur:=p; exit; end; pn:=p.fChild; while ((pn<>nil)and (FindNode(s,pn)=false)) do begin pn:=pn.nSibling; end; end; function TForm1.GetNode(var arr:Tarr;s:string):PTNode; var i,n:integer; begin result:=nil; n:=Length(arr); for i:=0 to n-1 do begin if (arr[i].id=s) then begin result:=@arr[i]; exit; end; end; end; procedure TForm1.LoadData(var arr:TArr); //load data from database var i:integer; begin Query.Close; Query.SQL.Clear; Query.SQL.Add('select * from table2'); Query.Open; Query.First; i:=Query.RecordCount; setLength(arr,i); i:=0; while not Query.Eof do begin arr[i].id:=Query.Fields[0].AsString; arr[i].fID:=Query.Fields[1].AsString; arr[i].nID:=Query.Fields[2].AsString; arr[i].hasCreate:=false; i:=i+1; Query.Next; end; Query.Close; end; constructor TTree.create; begin root:=nil; end; constructor TTree.create(var arr:TArr); //create a tree from arr var i,j,n:integer; p:PTNode; begin i:=0; n:=Length(arr); while i<n do begin j:=0; while j<n do begin if (arr[j].id='root') and (arr[j].hasCreate=false) then begin new(root); root:=@arr[j]; root.fChild:=nil; root.nSibling:=nil; cur:=root; i:=i+1; arr[j].hasCreate:=true; end; if arr[j].hasCreate=true then begin if arr[j].fid<>'none' then begin p:=Form1.GetNode(arr,arr[j].fID); if p.hasCreate=false then begin arr[j].fChild:=p; p.hascreate:=true; p.fchild:=nil; p.nsibling:=nil; i:=i+1; end; end; if arr[j].nid<>'none' then begin p:=Form1.GetNode(arr,arr[j].nID); if p.hascreate=false then begin arr[j].nSibling:=p; p.hascreate:=true; p.fchild:=nil; p.nsibling:=nil; i:=i+1; end; end; end; j:=j+1; end; end; end; procedure TForm1.Button1Click(Sender: TObject); var arr:TArr; aTree:TTree; begin LoadData(arr); aTree:=TTree.create(arr); memo1.Lines.Add(atree.root.fchild.fchild.nsibling.nsibling.id); //test aTree.Free; end; end.
Delphi实现树型结构具体实例
代码如下: 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;
TreeView多节点删除
for i:=0 to form1.TreeView.SelectionCount-1 do begin Form1.TreeView.Items.Item[i].Delete; end;
treeview添加节点
procedure TForm1.Button1Click(Sender: TObject); var MyTreeNode1, MyTreeNode2: TTreeNode; begin with TreeView1.Items do begin Clear; { remove any existing nodes } MyTreeNode1 := Add(nil, 'RootTreeNode1'); { Add a root node } { Add a child node to the node just added } AddChild(MyTreeNode1,'ChildNode1'); {Add another root node} MyTreeNode2 := Add(MyTreeNode1, 'RootTreeNode2'); {Give MyTreeNode2 to a child } AddChild(MyTreeNode2,'ChildNode2'); {Change MyTreeNode2 to ChildNode2 } { and add a child node to it} MyTreeNode2 := TreeView1.Items[3]; AddChild(MyTreeNode2,'ChildNode2a'); {Add another child to ChildNode2, after ChildNode2a } Add(MyTreeNode2,'ChildNode2b'); {add another root node} Add(MyTreeNode1, 'RootTreeNode3'); end;
treeview节点移动函数
//TreeView节点移动函数,支撑同级移动跟任意移动。up代表移动方向,true上移false下移; //同级移动的时候须要传进选中节点的level,免意挪动level=-1 procedure UpDownNode(node:TTreeNode;up:boolean;level:integer); var TargetNode: TTreeNode; begin if (level<>-1) then //同级间移动 begin if (node <>nil)then //检查是否有选中节点 如果有则做上移操作 begin if (up) then //上移 begin if (node.GetPrev <> nil) then begin TargetNode := node.GetPrev; while TargetNode.Level <> level do begin if TargetNode.GetPrev=nil then break else TargetNode := TargetNode.GetPrev; end; if (TargetNode<>nil) then begin if (TargetNode.Level=level) then begin if (node.getPrevSibling <> nil) then //检查是否有同级上一节点如果有做上移操作 begin if (node.getPrevSibling <> nil) then node.MoveTo(TargetNode,naInsert) else node.MoveTo(TargetNode,naAddFirst); end else begin node.MoveTo(TargetNode,naAdd); end; end; end; end; end else //下移 begin if (node.GetNext <> nil) then begin TargetNode := node.getNext; while TargetNode.Level <> level do begin if TargetNode.GetNext=nil then break else TargetNode := TargetNode.getNext; end; if (TargetNode<>nil) then begin if (TargetNode.Level=level) then begin if (node.getNextSibling <> nil) then //检讨是否有同级上一节点假如有作上移操作 begin if (TargetNode.getNextSibling <> nil) then node.MoveTo(TargetNode.getNextSibling,naInsert) else node.MoveTo(TargetNode,naAdd); end else begin node.MoveTo(TargetNode,naInsert); end; end; end; end; end; end; end else //容许跨级移动 begin if (node <> nil)then //检讨是否有选中节点 假如有则做上移操作 begin if (up) then //上移 begin if (node.GetPrev <> nil) then begin TargetNode := node.GetPrev; if (TargetNode<>nil) then begin if (node.GetPrev <> nil) then //检查是否有同级上一节点如果有做上移操作 begin if (node.GetPrev <> nil) then node.MoveTo(TargetNode,naInsert) else node.MoveTo(TargetNode,naAddFirst); end else begin node.MoveTo(TargetNode,naAdd); end; end; end; end else //下移 begin if (node.GetNext <> nil) then begin TargetNode := node.GetNext; while TargetNode.Parent = node do begin if TargetNode.GetNext=nil then break else TargetNode := TargetNode.getNext; end; if (TargetNode<>nil) then begin if (node.GetNext <> nil) then //检讨是否有共级上一节点假如有干上移操息 begin if (TargetNode.getNext <> nil) then node.MoveTo(TargetNode.getNext,naInsert) else node.MoveTo(TargetNode,naAdd); end else begin node.MoveTo(TargetNode,naAdd); end; end; end; end; end; end; end;
遍历treeview某个节点下所有节点的函数
function treeallnode(anode:Ttreenode):string; var i:integer; mystr:string; begin for i:=0 to anode.Count-1 do begin if mystr<>'' then mystr:=mystr+'|'+trim(trim(anode[i].Text)) else xxx:=trim(trim(anode[i].Text)); if anode[i].HasChildren then if mystr<>'' then mystr:=mystr+'|'+trim(treeallnode(anode[i])) else mystr:=trim(treeallnode(anode[i])); end; result:=mystr; end; 上面的函数可以遍历某个节点下的所有节点。
上、下、左、右移动
来自CSDN: (*// 标题:移动树节点 说明:上、下、左、右移动 设计:Zswang 日期:2002-06-08 支持:wjhu111@21cn.com //*) ///////Begin Source function TreeNodeMove(mTreeNode: TTreeNode; mAnchorKind: TAnchorKind; mIsTry: Boolean = False): Boolean; var vTreeNode: TTreeNode; begin Result := Assigned(mTreeNode); if not Result then Exit; case mAnchorKind of akTop: begin vTreeNode := mTreeNode.GetPrev; while Assigned(vTreeNode) do begin if vTreeNode = mTreeNode.GetPrevSibling then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naInsert); Exit; end else if (vTreeNode.Level = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAdd); Exit; end else if (vTreeNode <> mTreeNode.Parent) and (vTreeNode.Level + 1 = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChild); Exit; end; vTreeNode := vTreeNode.GetPrev; end; end; akBottom: begin vTreeNode := mTreeNode.GetNext; while Assigned(vTreeNode) do begin if vTreeNode = mTreeNode.GetNextSibling then begin if not mIsTry then vTreeNode.MoveTo(mTreeNode, naInsert); Exit; end else if (vTreeNode.Level = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddFirst); Exit; end else if vTreeNode.Level + 1 = mTreeNode.Level then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChildFirst); Exit; end; vTreeNode := vTreeNode.GetNext; end; end; akLeft: begin vTreeNode := mTreeNode.Parent; if Assigned(vTreeNode) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naInsert); Exit; end; end; akRight: begin vTreeNode := mTreeNode.GetNextSibling; if Assigned(vTreeNode) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChildFirst); Exit; end; end; end; Result := False; end; { TreeNodeMove } ///////End Source ///////Begin Demo procedure TForm1.TreeView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if not (ssCtrl in Shift) then Exit; case Key of VK_UP: TreeNodeMove(TTreeView(Sender).Selected, akTop); VK_DOWN: TreeNodeMove(TTreeView(Sender).Selected, akBottom); VK_LEFT: TreeNodeMove(TTreeView(Sender).Selected, akLeft); VK_RIGHT: TreeNodeMove(TTreeView(Sender).Selected, akRight); end; end; procedure TForm1.TreeView1GetSelectedIndex(Sender: TObject; Node: TTreeNode); begin CheckBox1.Checked := TreeNodeMove(TTreeView(Sender).Selected, akTop, True); CheckBox2.Checked := TreeNodeMove(TTreeView(Sender).Selected, akBottom, True); CheckBox3.Checked := TreeNodeMove(TTreeView(Sender).Selected, akLeft, True); CheckBox4.Checked := TreeNodeMove(TTreeView(Sender).Selected, akRight, True); end; ///////End Demo 来自: loyalfox, 时间: 2002-09-26 8:47:00, ID: 1346562 可以判断出位置来,但是上下左右移动的代码怎么没有啊? 希望好人做到低吧! 谢谢!等待! 来自: cxz9, 时间: 2002-09-26 20:23:00, ID: 1348156 procedure TForm1.TreeView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if not (ssCtrl in Shift) then Exit; case Key of VK_UP: TreeNodeMove(TTreeView(Sender).Selected, akTop); VK_DOWN: TreeNodeMove(TTreeView(Sender).Selected, akBottom); VK_LEFT: TreeNodeMove(TTreeView(Sender).Selected, akLeft); VK_RIGHT: TreeNodeMove(TTreeView(Sender).Selected, akRight); end; end; 这个就是呀 来自: loyalfox, 时间: 2003-01-13 11:20:00, ID: 1576408 接受答案了. unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TForm1 = class(TForm) TreeView1: TTreeView; Button1: TButton; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; procedure TreeView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} function TreeNodeMove(mTreeNode: TTreeNode; mAnchorKind: TAnchorKind; mIsTry: Boolean = False): Boolean; var vTreeNode: TTreeNode; begin Result := Assigned(mTreeNode); if not Result then Exit; case mAnchorKind of akTop: begin vTreeNode := mTreeNode.GetPrev; while Assigned(vTreeNode) do begin if vTreeNode = mTreeNode.GetPrevSibling then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naInsert); Exit; end else if (vTreeNode.Level = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAdd); Exit; end else if (vTreeNode <> mTreeNode.Parent) and (vTreeNode.Level + 1 = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChild); Exit; end; vTreeNode := vTreeNode.GetPrev; end; end; akBottom: begin vTreeNode := mTreeNode.GetNext; while Assigned(vTreeNode) do begin if vTreeNode = mTreeNode.GetNextSibling then begin if not mIsTry then vTreeNode.MoveTo(mTreeNode, naInsert); Exit; end else if (vTreeNode.Level = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddFirst); Exit; end else if vTreeNode.Level + 1 = mTreeNode.Level then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChildFirst); Exit; end; vTreeNode := vTreeNode.GetNext; end; end; akLeft: begin vTreeNode := mTreeNode.Parent; if Assigned(vTreeNode) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naInsert); Exit; end; end; akRight: begin vTreeNode := mTreeNode.GetNextSibling; if Assigned(vTreeNode) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChildFirst); Exit; end; end; end; Result := False; end; { TreeNodeMove } procedure TForm1.TreeView1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin if not (ssCtrl in Shift) then Exit; case Key of VK_UP: TreeNodeMove(TTreeView(Sender).Selected, akTop); VK_DOWN: TreeNodeMove(TTreeView(Sender).Selected, akBottom); VK_LEFT: TreeNodeMove(TTreeView(Sender).Selected, akLeft); VK_RIGHT: TreeNodeMove(TTreeView(Sender).Selected, akRight); end; end; end.
上下左右移动树TreeView
function TreeNodeMove(mTreeNode: TTreeNode; mAnchorKind: TAnchorKind;& }# J" A& 6 v0 b+ _ mIsTry: Boolean = False): Boolean;9 y. [) D8 r% X1 p! f8 K% ]2 K var vTreeNode: TTreeNode; begin& {0 |$ M5 m# [) z+ h Result := Assigned(mTreeNode); if not Result then Exit;7 a* d/ W/ S; }3 w7 ^ l( o* c3 l, G case mAnchorKind of# D3 O9 B3 f" S; I3 p$ _ akTop: begin vTreeNode := mTreeNode.GetPrev; while Assigned(vTreeNode) do begin if vTreeNode = mTreeNode.GetPrevSibling then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naInsert); Exit;: g. R9 |7 g2 L0 k end else if (vTreeNode.Level = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAdd);! [6 V& T# `" t: q& m Exit;! c4 T" ?4 B) Z3 H, e end else if (vTreeNode <> mTreeNode.Parent) and (vTreeNode.Level + 1 = mTreeNode.Level) then begin! p) j+ W5 D5 t/ P if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChild); Exit; end; vTreeNode := vTreeNode.GetPrev; end;+ Z$ `- Z# V9 L9 ]9 I end;6 H; X; |. E5 f) C akBottom: begin2 @ V0 [0 p5 s5 l1 O1 _ vTreeNode := mTreeNode.GetNext;2 `( ~! n3 |% Z3 k0 y while Assigned(vTreeNode) do begin if vTreeNode = mTreeNode.GetNextSibling then begin# |. D) k4 ]1 I5 r V7 g# w! p" e7 C if not mIsTry then vTreeNode.MoveTo(mTreeNode, naInsert);# I' |( _1 I7 O4 + F Exit;. D9 j" B* T9 `! P end else if (vTreeNode.Level = mTreeNode.Level) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddFirst); Exit; end else if vTreeNode.Level + 1 = mTreeNode.Level then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChildFirst); Exit; end;+ / z" U! 3 y vTreeNode := vTreeNode.GetNext;8 e2 V" P$ U0 { e9 Q5 S, r; `9 D end; end;8 D' x0 y, s, `( ^% A; K- F akLeft: begin) n, I# A, 1 T5 D& `8 g vTreeNode := mTreeNode.Parent; if Assigned(vTreeNode) then begin& l# J8 B+ z2 ~/ N. { if not mIsTry then mTreeNode.MoveTo(vTreeNode, naInsert);2 J% i. q4 r& M) w J+ b3 @9 w! X Exit; end; end;" g( e1 G0 @, l* b- I _! v& b akRight: begin3 e$ C0 {/ Q; a1 a vTreeNode := mTreeNode.GetNextSibling; if Assigned(vTreeNode) then begin if not mIsTry then mTreeNode.MoveTo(vTreeNode, naAddChildFirst);" [ K# P" j/ C% f4 {9 w Exit; end; end;$ O' n4 m$ M0 D) W8 M end; Result := False; end; { TreeNodeMove }. R0 G$ v2 Q2 K
树形控件(TreeView)节点间的拖动
转载自"现实的梦想(http://blog.jiqila.com/)" Delphi树形控件(TreeView)节点间的拖动 很久没写博客了,因为实在没什么东西可写。不过,今天以前的同事问我,关于TreeView的操作,那我就顺便写在博客里,稍微展开一下,说说TreeView吧。 TTreeView是VCL里面一个类,也是我们经常会用到的,而且功能也是很强大的。与TreeView相关的一个极其重要的类就是TTreeNode,我们下面的操作,几乎都是在围绕着它进行的。下面直接切入正题,要实现节点间的拖动,都需要实现哪几个事件呢? 首先,要实现 OnMouseDown ,在其内要写入开启拖动的代码。 然后,要实现 OnDragOver ,其主要作用是在拖动过程中,实现对节点拖动目的(di)的控制。 最后,要实现 OnDragDrop ,其是实现拖动释放的操作。 这三个事件,我们可以简单的理解为,拖动开始、拖动过程、拖动结束(虽然“拖动开始”和“拖动结束”都有他们各自对应的事件,但是也不妨碍我们这样的理解)。首先是 OnMouseDown 事件。 procedure TfrmMain.TreeView1MouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); var node: TTreeNode; begin node := TreeView1.GetNodeAt(X, Y); // 获取鼠标按下位置的节点 if (node <> nil) and (node.Level > 0) and (Button = mbLeft) then TreeView1.BeginDrag(True); // 启动拖动 end; 需要注意的是,TreeView 控件的 DragMode 要设置为 dmManual,才会需要执行 BeginDrag 手工启动拖动。DragMode 的缺省值就是 dmManual。 接下来就是 OnDargOver 事件。 procedure TfrmMain.TreeView1DragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var node: TTreeNode; begin node := TreeView1.GetNodeAt(X, Y); // node.Level 是节点的层级,等于0时,表示是根节点(没有上级节点了) // 本语句控制只能将节点拖动到与父节点平级的其他节点上,Accept表示,是否可释放 if (node <> nil) and (node.Level = 0) and (TreeView1.Selected.Parent <> node) then Accept := True else Accept := False; end; 最后是实现 OnDragDrop 事件,此事件里就要写上与业务相关的代码了。 procedure TfrmMain.TreeView1DragDrop(Sender, Source: TObject; X, Y: Integer); var node: TTreeNode; begin node := TreeView1.GetNodeAt(X, Y); if (node <> nil) and (node.Level = 0) then begin // 此处用到了StateIndex,只是在生成TreeView的时候,将每条记录的主键值储存到了这里,并不代表StateIndex的实际意义 Query1.SQL.Text := 'update sys_config set class_id=' + IntToStr(node.StateIndex) + ' where cid=' + IntToStr(TreeView1.Selected.StateIndex); if Query1.ExecSQL() > 0 then TreeView1.Selected.MoveTo(node, naAddChild); // 将节点移动到目标节点的下一级,也就是使目标节点成为被拖动节点的父节点 end; end; 至此,整个拖动就结束了。代码是不是很简单?又回到了我刚开始说的话,整个过程,都是在围绕着TTreeNode类进行的操作。最后,说明一下,代码中我使用到了TreeNode的StateIndex属性,实际上我这样的做法是不推荐的。我们推荐使用TreeNode的Data属性来保存额外的数据,而且这也是正宗的用法。Data属性可以保存任何数据,因为它是Pointer类型(无类型指针),相当于C++里面的void*。具体Data如何使用,与本次的主题关系不大,以后有时间再来讲吧。
tree找窗体
procedure TMainFrm.RzTreeViewClick(Sender: TObject); var // i: Integer; Child: TChildFrm; begin { for i :=0 to MainFrm.MDIChildCount -1 do //MDIChildCount:总的子窗体个数 if (MainFrm.MDIChildren[i] is TChildFrm) then //判断子窗体是否存在 begin if TChildFrm(MainFrm.MDIChildren[i]).DataBase_Path=TRzTreeView(Sender).Hint then //判断是否是同一个工作组 begin TChildFrm(MainFrm.MDIChildren[i]).UPRich(1); Exit; //退出函数体 end; end; } // if TRzTreeView(Sender).Selected <> nil then // for I := 0 to TRzTreeView(Sender).Owner.ComponentCount-1 do if (TRzTreeView(Sender).Owner.ComponentCount = 2) and (TRzTreeView(Sender).Owner.Components[1] is TChildFrm) then begin TChildFrm(TRzTreeView(Sender).Owner.Components[1]).UPRich(1); end else begin Child := TChildFrm.Create(TRzTreeView(Sender).Parent); With Child do begin Try DataBase_Path := TRzTreeView(Sender).Hint; Caption := ExtractFileName(TRzTreeView(Sender).Hint); WindowState := wsMaximized; // 最大化子窗体 BringToFront; // 把窗体放在最前面 SetFocus; // 窗体获得焦点 UPRich(1); except Free; end; end; end; // FindSubFrm(MainFrm, TRzTreeView(Sender).Hint).UPRich(1); // TChildFrm(Twincontrol(TRzTreeView(Sender).Parent).Owner).UPRich(1); { case treeview1.Selected.Level of 0: 1: 2: end; } // TChildFrm(TComponent(TRzTreeView(Sender).Parent).Owner).UPRich(1); end;
if nil <> TRzTreeView(Sender).Selected then begin for i := 0 to TRzTabSheet(TRzTreeView(Sender).Owner).ComponentCount - 1 do begin if (TRzTabSheet(TRzTreeView(Sender).Owner).Components[i] is TChildFrm) then begin TChildFrm(TRzTabSheet(TRzTreeView(Sender).Owner).Components[i]).UPRich (PNodeData(TRzTreeView(Sender).Selected.Data)^.id); TChildFrm(TRzTabSheet(TRzTreeView(Sender).Owner).Components[i]) .RzPageControl1.ActivePage := TChildFrm (TRzTabSheet(TRzTreeView(Sender).Owner).Components[i]) .RzPageControl1.ActivePageDefault; RzListView1.Hint := TRzTreeView(Sender).Hint; UPList(PNodeData(TRzTreeView(Sender).Selected.Data)^.id); // Break; exit; end; end; // if CheckFrm(NavRzPgCtl, TRzTreeView(Sender).Hint) = false then With TChildFrm.Create(TRzTreeView(Sender).Parent) do begin Try DataBase_Path := TRzTreeView(Sender).Hint; Caption := ExtractFileName(TRzTreeView(Sender).Hint); WindowState := wsMaximized; // 最大化子窗体 BringToFront; // 把窗体放在最前面 SetFocus; // 窗体获得焦点 UPRich(PNodeData(TRzTreeView(Sender).Selected.Data)^.id); UPList(PNodeData(TRzTreeView(Sender).Selected.Data)^.id); except Free; end; end; end;
delphi TreeView 携带数据 使用
TreeView由节点构成,建树通过对TreeView.items属性进行操作。Items是一个TTreeNodes对象,这是一个TTreeNode集。 一、针对TTreeNodes,也就是TreeView.Items,有这些属性: 1、count,节点个数。 2、item[index] ,通过index得到节点。 二、针对TTreeNodes,也就是TreeView.Items,常用的添加节点的操作有: AddFirst添加第一个根节点。由此函数添加的节点总排在前面,除非后来又使用此函数添加了一个节点,则后添加的节点将排在前面。该函数返回新添加的节点。 AddChildFirst添加第一个子节点,要求有父节点作为其参数。返回新添加的节点。 AddChild添加一个子节点,要求有父节点作为其参数。返回新添加的节点。 Add添加一个兄弟节点,要求有兄弟节点作为其参数。返回新添加的节点。 三、针对TTreeNodes,也就是TreeView.Items,常用的得到节点的操作有: GetFirstNode()得到根节点。 然后配合TTreeNode.GetNext(),就可以访问所有的节点。 四、建树举例: var root_node,cur_node:TTreeNode; begin root_node:=AddFirst(nil,根节点1); cur_node:=addChildfirst(root_node,nil,根节点1_child1); add(cur_node,根节点1_child2); root_node:=Add(nil,根节点2); AddChildFirst(root_node,根节点2_child1); end; 五、事件触发: 当从一个节点跳到另一个节点,会触发TTreeView.OnChange事件。该事件中,将传递node,即当前被选中的节点。 当修改一个节点的text时,会触发TTreeView.onEdit事件。 六、将节点和节点所对应的数据联系起来 对于每个TTreeNode,有个Data属性,可以存放一个指针。我们可以利用这个域来存放与节点对应的自己的数据。 1.我们先定义一个数据结构,作为记录我们要记录的数据。如: type PMyData=^TMyData; TMyData=Record sFName:string; sLName:String; nIndex:integer; end; 2.然后,创建数时,将节点和节点数据联系起来: procedure TForm1.Button1Click(Sender: TObject); var myshuju: PMyData cur_node:TTreeNode; begin New(myshuju); //记住,一定要先分配内存。有几个节点,就要分配几次内存。 myshuju^.FName:=Edit1.Text; Myshuju^.LName := Edit2.Text; TreeViewIndex := StrToInt(Edit3.Text); with TreeView1 do begin cur_node:=items.AddFirst(nil,first); cur_node.data:=myshuju; end; end; 3.当我们选中一个节点时,就可以使用我们的数据了。 procedure TForm1.TreeView1Change(Sender:TObject;Node:TTreeNode); begin if node.data<>nil then self.label1.caption:=pmyData(node.data)^.Fname+pmyData(node.data)^.Lname end; 七、一般使用流程: 1、添加全局变量: b_first:boolean; //记录是否是第一次访问节点,因为此时数据还未准备好,而一旦访问节点就会触发OnChange事件,在此事件处理函数中也许会出错。 2、在FormCreate中, a、设置b_first:=true; b.创建数并将节点与数据联系。 3、在FormShow中 设置b_first:=false; 4.在事件OnChange中处理节点被选中事件。 5.在Edit中处理节点被修改Text事件。 并调用OnChange. 6.在TreeView.Destory中 释放Data中指向的内存空间。 if Rev1.Items[I].Level = 0 then begin path := extractfilepath(paramstr(0)) + '平台'; if FindFirst(path + '*.exe', faAnyFile, sr) = 0 then begin repeat if sr.Name[1] = '.' then Continue; trNode := Rev1.Items.AddChild(Rev1.Items[I], sr.Name); New(pstr);//每次都要新开内存 pstr^ := path + sr.Name; trNode.Data := pstr; pstr:=nil;//必须释放,要不每次地址都一样,值也不会改变 until (FindNext(sr) <> 0); end;
delphi TreeView绑定数据库
数据库结构:
ID PID Caption
1 0 aaa
2 1 abb
3 1 abc
4 2 abbc
数据库的表已经建好,但是不会写Delphi部分代码,请高手发布一下代码,最好是完整代码,整个Form全部Copy上,包括结构声明部分,最好是所有功能都写在同一个Form中,稍做修改就能够运行的,急用.
1:是不是要在treenode的结构指里里存放数据库记录的键值 如果是的话就像下面那样了 procedure TBqLeaveFrm.InitTV; var I:Integer; begin for I:=0 to TV.Items.Count-1 do begin Dispose(TV.Items[i].Data); TV.Items[I].Data:=nil; end; TV.Items.Clear; end; procedure TBqLeaveFrm.LoadTV; var ParentNode,Node : TTreeNode; BeInHospital_No:^integer; SickName:string; begin InitTV; if FQuery_TV.Active then FQuery_TV.Close; FQuery_TV.SQL.Clear; FQuery_TV.SQL.Add('select beinhospital_no,sick_name,sex,bq_section_id '); FQuery_TV.SQL.Add('from bq_sick_basic_mis nolock '); FQuery_TV.SQL.Add('where bq_square_indicate=0 and bq_section_id=:Bq_Section_Id'); FQuery_TV.Parameters.ParamByName('Bq_Section_Id').Value:=HisObj.CurrentSectionNo; FQuery_TV.Open; FQuery_TV.First; TV.Items.BeginUpdate; ParentNode:=TV.Items.Add(Nil,HisObj.CurrentSectionName); ParentNode.ImageIndex:=2; ParentNode.SelectedIndex:=2; while not FQuery_TV.Eof do begin New(BeInHospital_No); SickName:=FQuery_TV['sick_name']; BeInHospital_No^:=FQuery_TV['beinhospital_no']; Node:=TV.Items.AddChildObject(ParentNode,SickName,BeInHospital_No); if Trim(FQuery_TV['sex'])='男' then begin Node.ImageIndex:=1; Node.SelectedIndex:=1; end else begin Node.ImageIndex:=0; Node.SelectedIndex:=0; end; FQuery_TV.Next; end; TV.TopItem.Expand(True); TV.Items.EndUpdate; end; 取出来用 procedure TBqLeaveFrm.TVDblClick(Sender: TObject); var TN:TTreeNode; BeInHospital_No:integer; begin if TV.Items.Count=0 then begin Application.MessageBox('此病区未有病人在院!','错误提示',MB_OK); Exit; end; if TV.Selected=Nil then begin Application.MessageBox('请选择要办理病区出院的病人!','错误提示',MB_OK); Exit; end; if TV.Selected.Parent=Nil then Exit; TN:=TV.Selected; BeInHospital_No:=Integer(TN.Data^); Get_MedicineMis(BeInHospital_No); Btn_Preview.Enabled:=True; Btn_Print.Enabled:=True; Btn_ConfirmLeaveBq.Enabled:=True; TV.Enabled:=False; 看看这个吧!
whyno1回复于15日21点05分 treeview不是数据感知控件,不能直接连数据库。 楼主要自己写sql创建结点. Delphi(Pascal) code procedure TForm1.FormShow(Sender: TObject); var q1,q2:tadoquery; i,j:integer; nodeo,nodep:ttreenode; sid:psid; begin treeview1.Items.Clear; q1:=tadoquery.Create(nil); q1.Connection :=adoconnection1; q1.Close; q1.SQL.Add('select dep from dept '); q1.Open; q2:=tadoquery.Create(nil); q2.Connection :=adoconnection1; q1.First; if not q1.Eof then treeview1.AutoExpand :=true; treeview1.ReadOnly :=true; nodeo:=treeview1.items.AddFirst(nil,'测试'); for i := 0 to q1.RecordCount - 1 do begin nodep:=treeview1.Items.AddChild(nodeo,q1.FieldByName('dep').AsString ); q2.Close; q2.SQL.Clear; q2.SQL.Add('select id,stano,name,ename from t_user where cast(dep as varchar(10))='''+q1.fieldbyname('dep').AsString +''' ' ); q2.Open; q2.First ; if not q2.Eof then for j := 0 to q2.RecordCount - 1 do begin treeview1.Items.AddChildObject(nodep,q2.FieldByName('name').AsString,nil); q2.Next; end; q1.Next ; end; q1.Free; q2.Free; end;
使用一个TreeView, 数据库结构要增加字段,记录父结点ID: ID PID Caption FatherID 1 0 aaa 2 1 abb 1 3 1 abc 2 4 2 abbc 2 通过编写一个读取数据库文件的方式,即可实现TreeView与数据库的关联。
大数据高效加载法
{connMenu: TADOConnection; qryMenu : TADOQuery ; //数据查询 TreeViewMenu:TTreeView; lblTime.Caption := FormatDateTime('hh:mm:ss.zz', tEnd - tStart) ; lblCount.Caption := IntToStr(nCount) + ' menu items added.' ; } procedure TForm1.ComplexTypeFour ; type PMenuTreeItem = ^TMenuTreeItem ; TMenuTreeItem = Record ItemID : Integer ; ParentID : Integer ; Caption : String; // Description : String; end; type //指针类型数据 PQryMenuRec = ^TQryMenuRec ; TQryMenuRec = Record ItemID : Integer ; ParentID : Integer ; Caption : String; RecNo : Integer ; end; var qryMenu : TADOQuery ; //数据查询 aRecord : PMenuTreeItem ; aMenuRec : PQryMenuRec ; aMenuList : TList ; //TList是Bennet-Tec公司开发的可以用来显示Tree,Grid,list等形式的结构化数据和数据对象,如用户列表,报表,组织结构等信息,界面。 ParentNode, NextNode , MenuNode : TTreeNode ; tStart, tEnd : TDateTime ; i, nAdded, nRecNo : Integer ; lFindNode : Boolean ; nCount : Integer ; begin tStart := Now ; nCount := 0 ; if Not(connMenu.Connected) then connMenu.Open ; TreeViewMenu.Items.Clear; TreeViewMenu.Items.BeginUpdate ; qryMenu := TADOQuery.Create(nil); qryMenu.CursorLocation := clUseServer ; qryMenu.Connection := connMenu ; qryMenu.SQL.Add('SELECT * FROM menu_tree ' + 'WHERE parent_id = 0 ' + 'ORDER BY menu_id') ; qryMenu.Open ; while Not(qryMenu.Eof) do begin New(aRecord) ; aRecord.ParentID := qryMenu.FieldByName('parent_id').AsInteger ; aRecord.ItemID := qryMenu.FieldByName('menu_id').AsInteger ; aRecord.Caption := qryMenu.FieldByName('menu_caption').AsString ; MenuNode := TreeViewMenu.Items.AddChildObject(NIL,aRecord.Caption,aRecord); Inc(nCount) ; MenuNode.ImageIndex := 0; MenuNode.SelectedIndex := 1; MenuNode.StateIndex := -1; qryMenu.Next; end; qryMenu.Close ; qryMenu.SQL.Clear ; qryMenu.SQL.Add('SELECT * FROM menu_tree ' + 'WHERE parent_id > 0 ' + 'ORDER BY parent_id DESC, menu_id DESC') ; qryMenu.Open ; aMenuList := TList.Create ; while not(qryMenu.Eof) do begin New(aMenuRec) ; aMenuRec.ParentID := qryMenu.FieldByName('parent_id').AsInteger ; aMenuRec.ItemID := qryMenu.FieldByName('menu_id').AsInteger ; aMenuRec.Caption := qryMenu.FieldByName('menu_caption').AsString ; aMenuList.Add(aMenuRec) ; qryMenu.Next; end; while true do begin nAdded := 0 ; ParentNode := nil ; NextNode := nil ; for i := (aMenuList.Count - 1) downto 0 do begin aMenuRec := PQryMenuRec(aMenuList.Items[i]) ; lFindNode := False ; if Not(Assigned(ParentNode)) then lFindNode := True else begin if aMenuRec.ParentID <> PMenuTreeItem(ParentNode.Data).ItemID then begin if Assigned(NextNode) and (aMenuRec.ParentID = PMenuTreeItem(NextNode.Data).ItemID) then begin ParentNode := NextNode ; NextNode := nil ; end else lFindNode := True ; end; end; if lFindNode then begin ParentNode := TreeViewMenu.Items.GetFirstNode ; while Assigned(ParentNode) do begin if aMenuRec.ParentID = PMenuTreeItem(ParentNode.Data).ItemID then begin NextNode := ParentNode.GetNext ; break ; end; ParentNode := ParentNode.GetNext ; end; end; if Assigned(ParentNode) then begin New(aRecord) ; aRecord.ParentID := aMenuRec.ParentID ; aRecord.ItemID := aMenuRec.ItemID ; aRecord.Caption := aMenuRec.Caption ; MenuNode := TreeViewMenu.Items.AddChildObject(ParentNode,aRecord.Caption,aRecord); Inc(nCount) ; MenuNode.ImageIndex := 0; MenuNode.SelectedIndex := 1; MenuNode.StateIndex := -1; Inc(nAdded) ; Dispose(aMenuRec) ; aMenuList.Delete(i); end; end; if nAdded = 0 then break ; end; qryMenu.Close ; qryMenu.Free ; connMenu.Close ; for i := 0 to (aMenuList.Count - 1) do Dispose(PQryMenuRec(aMenuList.Items[i])) ; aMenuList.Clear ; aMenuList.Free ; TreeViewMenu.Items.Item[0].Expand(True); ParentNode := TreeViewMenu.Items.GetFirstNode ; ParentNode.MakeVisible ; TreeViewMenu.Items.EndUpdate ; tEnd := Now ; lblTime.Caption := FormatDateTime('hh:mm:ss.zz', tEnd - tStart) ; lblCount.Caption := IntToStr(nCount) + ' menu items added.' ; end;
导出TreeView到XML. 从XML装载TreeView
Delphi TTreeView 组件 (可以 "Win32" 组件面板中找到) 用于显示一个分级项目的窗口. 每一项 (树节点) 包含一个标签和一个可选的位图 (两个更准确).
XML 使开发者能更好的运用结构数据. XML文档的结构与TreeView的结构非常相似. Delphi中, TXMLDocument (可以 "Internet" 组件面板找到) 可以被用于表示一个XML文档.
本文将教您怎样保存树目录到XML文件及怎样从XML文档重建一个树型目录.
如果您需要一个强大、易传输的格式来保存应用程序的配置, 使用TreeView与XML是一个非常好的选择, 接下来请看......
保存TTreeView项目到XML
首先, 我们新建一个简单的应用程序, 并放置几个组件: 一个TTreeView组件, 两个ImageList组件及一个TXMLDocument组件. 其中的一个图像列表组件用于关联TreeView的Images属性, 另一个用于关联StateImages属性 (请参考: 如何给TreeView 添加复选框和单选按钮 ).
这是程序运行时的一个截屏. Tree2XML 过程用于保存树型目录到一个XML文档. 它只有一个参数, 用于给出要保存到XML的TTreeView组件. XML文档使用与应用程序相同的文件名 (当然, 要用'.XML'作为扩展名)
该过程遍历整个TreeView, 每次都调用子过程 ProcessTreeItem. 子过程ProcessTreeItem在同一级节点中递归调用. 该递归算法确保整个TreeView的节点都被处理.
对于每个目录项, 一个XML节点 (IXMLNode) 被建立, 并带有表示Text的属性值和ImageIndex、StateIndex属性. 节点实际上保存了所有与树节点有关的值.
一旦全部树目录项被处理完毕, XML文件也被保存.
注意: 过程ProcessTreeItem被动态创建和操作 TXMLDocument (XMLDoc)组件.
procedure Tree2XML(tree: TTreeView); var tn : TTreeNode; XMLDoc : TXMLDocument; iNode : IXMLNode; procedure ProcessTreeItem( tn : TTreeNode; iNode : IXMLNode); var cNode : IXMLNode; begin if (tn = nil) then Exit; cNode := iNode.AddChild('item'); cNode.Attributes['text'] := tn.Text; cNode.Attributes['imageIndex'] := tn.ImageIndex; cNode.Attributes['stateIndex'] := tn.StateIndex; //子节点 tn := tn.getFirstChild; while tn <> nil do begin ProcessTreeItem(tn, cNode); tn := tn.getNextSibling; end; end; (*ProcessTreeItem*) begin XMLDoc := TXMLDocument.Create(nil); XMLDoc.Active := True; iNode := XMLDoc.AddChild('tree2xml'); iNode.Attributes['app'] := ParamStr(0); tn := tree.TopItem; while tn <> nil do begin ProcessTreeItem (tn, iNode); tn := tn.getNextSibling; end; XMLDoc.SaveToFile(ChangeFileExt(ParamStr(0),'.XML')); XMLDoc := nil; end; (* Tree2XML *) 实际应用中, 您可以在应用程序关闭时导出TTreeView到XML, 使用Form的OnCloseQuery 事件来询问用户是否保存: // tree是TTreeView 组件的名字 procedure TForm1.FormCloseQuery( Sender: TObject; var CanClose: Boolean); begin case MessageDlg('Save tree items to XML?', mtConfirmation, [mbYes, mbNo, mbCancel], 0) of mrYes: Tree2XML(tree); mrNo: CanClose := True; mrCancel: CanClose := False; end; end; 下面是导出XML文档的结果. 从XML装载TTreeView项目 一旦我们有了表示TreeView的XML文件, 我们就能使用它来填充TreeView了. 当应用程序启动时, XML2Tree 过程被调用来构造树目录. tree 参数表示我们要填充的TTreeView组件; XMLDoc 参数指向一个TXMLDocument组件. procedure XML2Tree( tree : TTreeView; XMLDoc : TXMLDocument); var iNode : IXMLNode; procedure ProcessNode( Node : IXMLNode; tn : TTreeNode); var cNode : IXMLNode; begin if Node = nil then Exit; with Node do begin tn := tree.Items.AddChild(tn, Attributes['text']); tn.ImageIndex := Integer(Attributes['imageIndex']); tn.StateIndex := Integer(Attributes['stateIndex']); end; cNode := Node.ChildNodes.First; while cNode <> nil do begin ProcessNode(cNode, tn); cNode := cNode.NextSibling; end; end; (*ProcessNode*) begin tree.Items.Clear; XMLDoc.FileName := ChangeFileExt(ParamStr(0),'.XML'); XMLDoc.Active := True; iNode := XMLDoc.DocumentElement.ChildNodes.First; while iNode <> nil do begin ProcessNode(iNode,nil); iNode := iNode.NextSibling; end; XMLDoc.Active := False; end; XML2Tree 过程基本上与Tree2XML 操作原理相似. 代码遍历所有的XML节点, 创建 TTreeNode 对象并添加到 TreeView 组件. 就是这样老实说, 在现在的项目中, 我已经很少用 Registry 或 INI files 来保存配置数据了. XML似乎更友好.
读取到treeview
unit index; interface uses Classes,ComCtrls,xmldom, XMLIntf, msxmldom, XMLDoc; procedure XML2Tree(tree : TTreeView; XMLDoc : TXMLDocument;XmlFile:string); procedure Tree2XML(tree: TTreeView;XmlFile:string); procedure XML2TreeStream(tree : TTreeView; XMLDoc : TXMLDocument;ms:TMemoryStream); procedure Tree2XMLStream(tree: TTreeView;des:TMemoryStream); implementation type PMyData = ^TMyData; TMyData = Record ID: string; // 节点ID Fullname: String; // 父节点ID PID: string; Pw:string; mark:string end; procedure XML2Tree(tree : TTreeView; XMLDoc : TXMLDocument;XmlFile:string); {XML生成treeview} var jNode : IXMLNode; procedure ProcessNode(Node : IXMLNode; tn : TTreeNode); var cNode : IXMLNode; myshuju: PMyData; begin if Node = nil then Exit; tn := tree.Items.AddChild(tn, Node.Attributes['text']); tn.ImageIndex := Integer(Node.Attributes['imageIndex']); tn.StateIndex := Integer(Node.Attributes['stateIndex']); New(myshuju); myshuju^.ID :=Node.Attributes['id']; tn.Data:=myshuju; cNode := Node.ChildNodes.First; while cNode <> nil do begin ProcessNode(cNode, tn); cNode := cNode.NextSibling; end; end; (*ProcessNode*) begin tree.Items.Clear; XMLDoc.FileName := xmlFile; XMLDoc.Active := True; if XMLDoc.ChildNodes.First = nil then begin //ShowMessage('nil'); Exit; end; jNode := XMLDoc.DocumentElement.ChildNodes.First; while jNode <> nil do begin ProcessNode(jNode,nil); jNode := jNode.NextSibling; end; XMLDoc.Active := False; end; procedure Tree2XML(tree: TTreeView;XmlFile:string); {treeview导入XML} var tn : TTreeNode; XMLDoc : TXMLDocument; iNode : IXMLNode; procedure ProcessTreeItem(tn : TTreeNode; iNode : IXMLNode); var cNode : IXMLNode; begin if (tn = nil) then Exit; cNode := iNode.AddChild('item'); cNode.Attributes['text'] := tn.Text; cNode.Attributes['imageIndex'] := tn.ImageIndex; cNode.Attributes['stateIndex'] := tn.StateIndex; if PMyData(tn.Data)^.ID<>'' then cNode.Attributes['id'] := PMyData(tn.Data)^.ID; //child nodes tn := tn.getFirstChild; while tn <> nil do begin ProcessTreeItem(tn, cNode); tn := tn.getNextSibling; end; end; (*ProcessTreeItem*) begin XMLDoc := TXMLDocument.Create(nil); XMLDoc.Active := True; iNode := XMLDoc.AddChild('tree2xml'); iNode.Attributes['app'] := ParamStr(0); tn := tree.TopItem; while tn <> nil do begin ProcessTreeItem (tn, iNode); tn := tn.getNextSibling; end; XMLDoc.SaveToFile(XmlFile); end; (* Tree2XML *) procedure XML2TreeStream(tree : TTreeView; XMLDoc : TXMLDocument;ms:TMemoryStream); {XML生成treeview} var jNode : IXMLNode; procedure ProcessNode(Node : IXMLNode; tn : TTreeNode); var cNode : IXMLNode; myshuju: PMyData; begin if Node = nil then Exit; tn := tree.Items.AddChild(tn, Node.Attributes['text']); tn.ImageIndex := Integer(Node.Attributes['imageIndex']); tn.StateIndex := Integer(Node.Attributes['stateIndex']); New(myshuju); myshuju^.ID :=Node.Attributes['id']; tn.Data:=myshuju; cNode := Node.ChildNodes.First; while cNode <> nil do begin ProcessNode(cNode, tn); cNode := cNode.NextSibling; end; end; (*ProcessNode*) begin tree.Items.Clear; // XMLDoc.FileName := xmlFile; XMLDoc.LoadFromStream(ms,xetUTF_8); //XMLDoc.LoadFromStream(ms,); XMLDoc.Active := True; if XMLDoc.ChildNodes.First = nil then begin //ShowMessage('nil'); Exit; end; jNode := XMLDoc.DocumentElement.ChildNodes.First; while jNode <> nil do begin ProcessNode(jNode,nil); jNode := jNode.NextSibling; end; XMLDoc.Active := False; end; procedure Tree2XMLStream(tree: TTreeView;des:TMemoryStream); {treeview导入XML} var tn : TTreeNode; XMLDoc : TXMLDocument; iNode : IXMLNode; procedure ProcessTreeItem(tn : TTreeNode; iNode : IXMLNode); var cNode : IXMLNode; begin if (tn = nil) then Exit; cNode := iNode.AddChild('item'); cNode.Attributes['text'] := tn.Text; cNode.Attributes['imageIndex'] := tn.ImageIndex; cNode.Attributes['stateIndex'] := tn.StateIndex; if PMyData(tn.Data)^.ID<>'' then cNode.Attributes['id'] := PMyData(tn.Data)^.ID; //child nodes tn := tn.getFirstChild; while tn <> nil do begin ProcessTreeItem(tn, cNode); tn := tn.getNextSibling; end; end; (*ProcessTreeItem*) begin XMLDoc := TXMLDocument.Create(nil); XMLDoc.Active := True; iNode := XMLDoc.AddChild('tree2xml'); iNode.Attributes['app'] := ParamStr(0); tn := tree.TopItem; while tn <> nil do begin ProcessTreeItem (tn, iNode); tn := tn.getNextSibling; end; XMLDoc.SaveToStream(Des); //XMLDoc.SaveToFile(XmlFile); end; (* Tree2XML *) end.
treeview的排序
今日开发人事管理系统的时候,遇到一个对组织架构进行排序的问题。 这本身不是什么难的事情,可是要根据部门编号来排,然后,还要保证排了之后能重新保存在数据库。 我的做法是新增一个字段Serial,用来排序。并且在给树递归赋值的时候,选择数据时根据Serial这个字段排序。具体代码如下: 复制代码 复制代码 ALTER proc [dbo].[OS_GetOrganizations] ( @UserID nvarchar(20) ) AS SELECT CASE WHEN A.Parent IS NULL THEN '' ELSE A.Parent END AS Parent, A.NodeID, A.Spec, A.Lft, A.Rgt, A.DepartmentID, B.Specification, B.CenterID, B.Director, B.Complement, B.Responsibility FROM OS_Organizations A LEFT JOIN dbo.OS_Departments B ON A.DepartmentID = B.DepartmentID ORDER BY Serial ASC 复制代码 复制代码 为了保证Serial这个字段的数据完整性(即:保证在同一个父节点下的排序为1,2,3,4,5,6,7,8,9,不能出现断掉的情况),分别为增删改写了三个触发器,触发器的代码如下: ALTER trigger [dbo].[Inster_Org] on [dbo].[OS_Organizations] for insert AS begin declare @Serial int,@Parent nvarchar(50) select @Parent=Parent from inserted select @Serial = isnull(max(Serial),0) from OS_Organizations where Parent=@Parent update OS_Organizations set Serial=@Serial+1 where NodeID=(select NodeID from inserted) print @Serial ALTER trigger [dbo].[delete_Org] on [dbo].[OS_Organizations] for delete as begin declare @Serial int,@Parent nvarchar(50) select @Parent=Parent from deleted select @Serial=Serial from deleted update OS_Organizations Set Serial=Serial-1 where Parent=@Parent and Serial>@Serial end ALTER trigger [dbo].[update_Org] on [dbo].[OS_Organizations] for update as begin if update(Parent) begin declare @Serial int,@SerialSelect int,@Parent nvarchar(50)--,@ParentSelect nvarchar(50) --select @ParentSelect=Parent from deleted ----- select @Parent=Parent from inserted select @SerialSelect=Serial from deleted ----- select @Serial = isNull(Max(Serial),0) from OS_Organizations where Parent=@Parent update OS_Organizations Set Serial=@Serial+1 where NodeID=(select NodeID from inserted) update OS_Organizations set Serial=Serial-1 where Parent=(select Parent from deleted) and Serial>@SerialSelect end end 最后写一个存储过程来实现节点的上移下移功能。代码如下: ALTER PROC [dbo].[_2_OS_SortOrganizations] ( @NodeID nvarchar(50), @SortType nvarchar(10), @Result nvarchar(30) output ) AS DECLARE @TempSerial int,@TempNodeID nvarchar(50),@Parent nvarchar(50) SELECT @Parent=Parent FROM dbo.OS_Organizations WHERE NodeID=@NodeID DECLARE Organizations_Cursor CURSOR SCROLL FOR SELECT NodeID FROM OS_Organizations WHERE Parent=@Parent ORDER BY Serial OPEN Organizations_Cursor FETCH NEXT FROM Organizations_Cursor INTO @TempNodeID WHILE @@FETCH_STATUS = 0 BEGIN if(@TempNodeID=@NodeID) BEGIN if(@SortType='UP') BEGIN FETCH PRIOR FROM Organizations_Cursor INTO @TempNodeID END else BEGIN FETCH NEXT FROM Organizations_Cursor INTO @TempNodeID END SELECT @TempSerial=Serial FROM OS_Organizations WHERE NodeID=@TempNodeID UPDATE OS_Organizations SET Serial=(SELECT Serial FROM OS_Organizations WHERE NodeID=@NodeID) WHERE NodeID=@TempNodeID UPDATE OS_Organizations SET Serial=@TempSerial WHERE NodeID=@NodeID FETCH LAST FROM Organizations_Cursor INTO @TempNodeID END FETCH NEXT FROM Organizations_Cursor INTO @TempNodeID END CLOSE Organizations_Cursor DEALLOCATE Organizations_Cursor if @@ERROR <> 0 BEGIN SET @Result='操作失败' END else BEGIN SET @Result='操作成功' END
添加节点 子节点 数据库调用
procedure TashTreeView(); var Node : TTreeNode; Kehu:String; begin tashForm.TreeView1.Items.Clear ; Node :=TashForm.TreeView1.Items.Add(nil,'a'); TashForm.TreeView1.Items.AddChild(Node ,'a1'); Node := TashForm.TreeView1.Items.AddChild(Node ,'a2'); TashForm.TreeView1.Items.AddChild(Node ,'a21'); Node :=TashForm.TreeView1.Items.Add(Node.parent,'b'); TashForm.TreeView1.Items.AddChild(Node ,'b1'); TashForm.TreeView1.Items.AddChild(Node ,'b2'); Node :=TashForm.TreeView1.Items.Add(nil,'c'); TashForm.TreeView1.Items.AddChild(Node ,'c1'); TashForm.TreeView1.Items.AddChild(Node ,'c2'); Node :=TashForm.TreeView1.Items.Add(nil,'d') end; procedure TashTreeView(); var Node : TTreeNode; //Kehu:String; begin while not Tashform.ADOQuery2.Eof do begin Node :=TashForm.TreeView1.Items.Add(nil,Tashform.ADOQuery2.FieldByName('kehu').AsString); Tashform.ADOQuery3.Close; Tashform.ADOQuery3.SQL.Clear; Tashform.ADOQuery3.SQL.Add('select * from tashform where kehuid='''+Tashform.ADOQuery2.FieldByName('id').AsString+''''); Tashform.ADOQuery3.Open; while not Tashform.ADOQuery3.Eof do begin TashForm.TreeView1.Items.Addchild(node,Tashform.ADOQuery3.FieldByName('danhao').AsString); Tashform.ADOQuery3.Next; end; Tashform.ADOQuery3.Close; Tashform.ADOQuery2.Next; end; Tashform.ADOQuery2.Close; end;
Treeview 选中节点高亮有关问题
问关于Treeview 选中节点高亮问题
Treeview的选中节点,高亮显示。
但在失去焦点的时候,不显示
如果设置了TreeView.HideSelection:=False;
则在失去焦点的时候为灰色显示
现在我希望在失去焦点的时候依然可以普通的高亮显示,如windows默认的蓝色,
请问该如何做。谢谢
procedure TForm1.TreeView1CustomDrawItem(Sender: TCustomTreeView; Node: TTreeNode; State: TCustomDrawState; var DefaultDraw: Boolean); begin if node.Selected then begin TreeView1.Canvas.Brush.Style := bsSolid; TreeView1.Canvas.Brush.Color := clRed; end; end;
数据库表TreeView树的快速生成
根据数据表的内容生成TreeView树状结构,通常的做法就是从顶级开始,然后逐项递归查询遍历生成。这种方法在实现上容易做到,也很容易想到,但是效率比较低,因为数据库的检索(SQL语句需要解释执行,而且是对数据库文件进行操作)还是比较耗时的,尤其是树的层次较多,节点较多的情况。这里我要介绍的方法是以空间换取时间,只进行一次数据库检索,提取出全部数据,然后一次生成TreeView树状结构。通过SQL语句,让返回的记录按照父节点ID、节点ID进行排序,这样保证每次当前要添加的节点记录的父节点都已经添加到了TreeView树中,剩下的工作就是如何在TreeView树中找到父节点。这里我采用了一个排序的TStringList列表,通过排序列表采用二分查找的快速性能,就能够很快地查找到当前要添加节点的父节点,从而插入到TreeView树的正确位置。
源代码如下(假定数据表名称为FTree,字段有ID, ParentID, Name): procedure MakeTree(Query: TQuery; TreeView: TTreeView); var List: TStringList; Node: TTreeNode; Index: Integer; begin TreeView.Items.BeginUpdate; try TreeView.Items.Clear; List := TStringList.Create; try List.Sorted := True; while not Query.Eof do begin if Query.FieldByName('ParentID').AsInteger = 0 then { ParentID=0,顶层节点 } Node := TreeView.Items.AddChild(nil, Query.FieldByName('Name').AsString) else begin Index := List.IndexOf(Query.FieldByName('ParentID').AsString); Node := TreeView.Items.AddChild(TTreeNode(List.Objects[Index]), Query.FieldByName('Name').AsString); end; List.AddObject(Query.FieldByName('ID').AsString, Node); Query.Next; end; finally List.Free; end; finally TreeView.Items.EndUpdate; end; end; procedure TForm1.Button1Click(Sender: TObject); var T: DWORD; begin T := GetTickCount; Query1.SQL.Text := 'SELECT * FROM FTree ORDER BY ParentID, ID'; Query1.Open; MakeTree(Query1, TreeView1); Label1.Caption := Format('MakeTree所用时间: %d ms', [GetTickCount - T]); end;
通过连数据库自动生成treeview树结构
var myTreeNode : TTreeNode; v_id: string; begin TreeView1.Items.Clear; with ADOQuery1 do begin Close; SQL.Clear; SQL.Add('select distinct Z_id '); SQL.Add(' from table1 '); Open; while not EOF do begin myTreeNode := TreeView1.Items.Add(nil, FieldByName('Z_id').AsString); v_id := FieldByName('Z_id').AsString; Close; SQL.Clear; SQL.Add('select Z_jc '); SQL.Add(' from table1 '); SQL.Add(' where Z_id = '''+v_id+''' '); Open; while not EOF do begin TreeView1.Items.AddChild(myTreeNode, FieldByName('Z_jc').AsString); Next; end; Next; end; end; end;
Delphi中根据分类数据生成树形结构的最优方法
一、 引言:
TreeView控件适合于表示具有多层次关系的数据。它以简洁的界面,表现形式清晰、形象,操作简单而深受用户喜爱。而且用它可以实现ListView、ListBox所无法实现的很多功能,因而受到广大程序员的青睐。
树形结构在Windows环境中被普遍应用,但在数据库开发中面对层次多、结构复杂的数据,如何快速构造树形目录并实现导航呢?
二、 实现关键技术:
在Delphi提供的控件中包含了TreeView控件,但树的具体形成还需要用户编写代码。即它的列表项要在程序中动态添加,而这些列表数据通常由用户已录入在数据库表中,并作为数据库维护的一项内容。
许多人用TreeView构造树形目录时,通常都使用多个嵌套循环,或递归算法,将代码“编织”成树。这样不但算法复杂,且运行效率低下,不是最佳选择。这里介绍的是基于编码结构的高效算法。该算法的主要优点是:程序短小精悍,运行效率高,能快速实现数据库的树形结构,可以适应任何复杂的层次数据,实现方法简单,且树的内容有变动时,无需更改程序行。
算法的关键在于代码字典表中的代码字段的设计上一定要符合一定的代码设计要求,数据类型使用字符型。用户新增和修改代码时,必须有严格的约束,否则会导致程序出错。编码表的基本字段包括编码和编码名称,其编码规则是以数字、字母的位数来区分不同层次,同一层编码位数相同,层次按位数递增,程序通过判断编码位数来决定所在层数。
本例程中编码结构是“222”,编码格式为 “XX XX XX”。例如:第一层为10~99两位,第二层为1001~1099四位,用户需要做的是先要设计树的结构和对应编码,并录入相应名称,然后程序在读取这些数据时形成树。本例程不需要用户自己进行编码,程序中自动实现各层编码,保证了代码格式的正确性。
用TreeView导航表时,采用弹出式菜单,通过对话框操作数据表,同步更新树形控件和数据库。在所有操作中,树形控件不用重构,从而避免了重构时TreeView控件出现的闪动,也提高了程序的运行速度。
本示例程序为了使大家看清楚数据表中记录是否同步更新,用TDBGrid控件显示当前数据库表中所有记录。下面给出范例程序和主要代码分析。
三、 范例程序和主要代码分析:
我们以建立一个城市名称的树形结构为例来说明如何快速生成树形并实现导航数据表。
1. 先建立编码表:city_tree(bianma,cityname)。
2. 新建一个项目,按默认保存。
3. 新建一公共单元pubvar,在其中定义以下常量:
Const
cTreeCodeFormat = ‘222’;//编码格式为 XX XX XX
cTreeMaxLevel = 3;//最大编码层次
cTreeRootTxt = ‘城市’;//树根结点名称
这样做为了提高程序的通用性,以后用于其他代码字典的维护时,只需要更改这些特征常量。
4. 程序源代码:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DB, DBTables, ImgList, ComCtrls , PubVar, Grids, DBGrids, Menus , StrUtils, StdCtrls; type TForm1 = class(TForm) Tree: TTreeView; ImageList1: TImageList; Table1: TTable; DataSource1: TDataSource; DBGrid1: TDBGrid; PopupMenu1: TPopupMenu; AddMenu: TMenuItem; DeleteMenu: TMenuItem; RenameMenu: TMenuItem; Query1: TQuery; DataSource2: TDataSource; procedure AddMenuClick(Sender: TObject);//点击增加子项菜单 procedure RenameMenuClick(Sender: TObject);//点击重命名菜单 procedure DeleteMenuClick(Sender: TObject); //点击删除该项菜单 procedure FormCreate(Sender: TObject); procedure TreeClick(Sender: TObject); private { Private declarations } public { Public declarations } procedure LoadTree(treeDB:TDBDataSet);//初始化树 procedure UpdateTree(curNode:TTreenode; nodeTxt:string; state:string);//更新树 function GetNodeLevel(sFormat,sCode:string):integer;//获得节点层数 function GetCurrChildNodeMaxMa(curNode:TTreenode):string; //获得当前节点子节点的最大编码 function GetCurrentNodeBianma(curNode:TTreenode):string;//获得当前节点的编码 procedure UpdateTable(bianma:string; cityname:string ;state:string); //更新数据库 end; var Form1: TForm1; CurrentTreeNode: TTreeNode; // AddChildeTreeNode: TTreeNode; // flag:boolean; //用于标识是否需要在重命名树结点时更新数据 implementation uses AddChildUnit,RenameItemUnit; {$R *.dfm} procedure TForm1.LoadTree(treeDB:TDBDataSet);//初始化树 var curID,nodeTxt:string; level:integer; mynode:array[0..3] of TTreenode; begin //初始化变量 Screen.Cursor:=crHourGlass; tree.Enabled:=True; tree.Items.Clear; level:=0 ; //设置根节点 mynode[level]:=tree.items.add(Tree.Topitem,cTreeRootTxt); mynode[level].ImageIndex:=1; //遍历数据表,利用编码字段记录排序规律,依次添加树节点 with treeDB do begin 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 begin //增加下一节点时,用添加子节点的方法可轻松实现节点间的层次关系 //注意:这里的父节点是用当前节点的上一级节点mynode[level-1] mynode[level]:= tree.items.addchild(mynode[level-1],nodeTxt); mynode[level].ImageIndex:=2; end; next;//下一条记录 end; finally close; End; mynode[0].expand(False); Screen.Cursor:=crDefault; end; end; function TForm1.GetNodeLevel(sFormat,sCode:string):integer;//获得节点层数 var i,level,iLen:integer; begin level:=-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 level:=i; break; end; end; result:=level; end; //以下过程在新增、删除、修改记录时,同步更新树形结构 procedure TForm1.UpdateTree(curNode:TTreenode; nodeTxt:string; state:string); Begin if UpperCase(state)=’ADD’ then begin curNode:=tree.items.addchild(curNode,nodeTxt); curNode.ImageIndex:=2; end; if UpperCase(state)=’DEL’ then begin curNode.DeleteChildren; curNode.delete; end; if UpperCase(state)=’EDI’ then curNode.Text:=nodeTxt; end; procedure TForm1.AddMenuClick(Sender: TObject);//点击增加子项菜单 var AddChildText, AddTableText,maxbianma : string; begin AddChildForm.Label1.Caption:=’为“’+CurrentTreeNode.Text+’“增加子项 ’; if AddChildForm.ShowModal=mrOk then begin AddChildText:=AddChildForm.Edit1.Text; maxbianma:=GetCurrChildNodeMaxMa(CurrentTreeNode); if (CurrentTreeNode.Text=’城市’) and (maxbianma=’1000’) then maxbianma:=’11’//如果当前节点为根节点,且只有一个子节点,使增加节点编码为11 else if CurrentTreeNode.Text=’城市’ then maxbianma:=IntToStr(StrToInt(LeftStr(maxbianma,2))+1) else maxbianma:=IntToStr(StrToInt(maxbianma)+1); //使子项编码自动增1 if maxbianma〈〉’0’ then begin //增加树子层 AddTableText:=maxbianma+’-’+AddChildText; UpdateTree(CurrentTreeNode,AddTableText,’add’); //更新树 UpdateTable(maxbianma,AddChildText,’add’); //更新表 ShowMessage(’添加成功!’); end else ShowMessage(’此层为最低子层,不能在该层增加子层’); AddChildForm.Edit1.Text:=’’; end; end; function TForm1.GetCurrChildNodeMaxMa(curNode:TTreenode):string; //获得当前节点子节点的最大编码 var aSQL,maxbianma:string; li_pos:integer; begin li_pos:=pos(’-’,curNode.Text); if li_pos=7 then begin result:=’-1’; exit; end; if (li_pos=0) and (not(curNode.HasChildren)) then // 如果当前节点为根节点并且没有子节点 begin result:=’9’; //使根节点第一个节点编码为10 exit; end else begin aSQL:=’select bianma from city_tree where bianma like “’ + MidStr(curNode.Text, 1, li_pos-1) + ’%“’; Query1.UnPrepare; Query1.Close; Query1.SQL.Clear; Query1.SQL.Text:=aSQL; Query1.Prepare; Query1.ExecSQL; Query1.Active:=true; Query1.Last; maxbianma:=Query1.fieldbyname(’bianma’).AsString; if Query1.RecordCount=1 then//如果当前项没有子项 maxbianma:=maxbianma+’00’; Query1.Active:=false; end; result:=maxbianma; end; procedure TForm1.RenameMenuClick(Sender: TObject);//点击重命名菜单 var bianma:string; itemtext:string; //用于重命名时保存输入的Edit.text begin RenameItemForm.Label1.Caption:=’将“’+CurrentTreeNode.Text+’“命名为 ’; if RenameItemForm.ShowModal=mrOk then begin itemtext:=RenameItemForm.Edit1.Text; bianma:=GetCurrentNodeBianma(CurrentTreeNode); Table1.Locate(’bianma’,bianma,[]); UpdateTable(’’,itemtext,’edi’); itemtext:=bianma+’-’+itemtext; UpdateTree(CurrentTreeNode,itemtext,’edi’); ShowMessage(’重命名成功!’); end; end; //以下过程在新增、删除、修改记录时,同步更新数据库表 procedure TForm1.UpdateTable(bianma:string; cityname:string ;state:string); //更新数据库 begin if state=’add’ then begin Table1.Active:=True; Table1.Insert; Table1.SetFields([bianma,cityname]); Table1.Post; end; if state=’del’ then begin Table1.Active:=True; Table1.Locate(’bianma’,bianma,[]); Table1.Delete; end; if state=’edi’ then begin Table1.Edit; Table1.FieldByName(’cityname’).AsString:=cityname; Table1.Post; end; end; procedure TForm1.DeleteMenuClick(Sender: TObject); //点击删除该项菜单 var bianma:string; begin CurrentTreeNode.expand(False); if CurrentTreeNode.Text=’城市’ then //如果当前节点为根节点 begin ShowMessage(’不能删除根节点’); exit;//退出该过程 end; if CurrentTreeNode.HasChildren then //如果当前节点具有子项 begin ShowMessage(’请先删除其子项’); exit;//退出该过程 end; if Application.MessageBox(PChar(’真的要删除“’+CurrentTreeNode.Text+’“这项吗?’),’警告’,MB_YESNO)=mrYES then begin bianma:=GetCurrentNodeBianma(CurrentTreeNode); UpdateTree(CurrentTreeNode,’’,’del’); UpdateTable(bianma,’’,’del’); ShowMessage(’删除成功!’); Table1.Refresh;//更新TBGrid控件中的显示 Table1.Active:=true; CurrentTreeNode:=Form1.tree.selected; end; end; function TForm1.GetCurrentNodeBianma(curNode:TTreeNode):string;//获得当前节点的编码 var li_pos:integer; bianma:string; begin li_pos:=pos(’-’,curNode.Text); bianma:=MidStr(curNode.Text,1,li_pos-1); result:=bianma; end; procedure TForm1.FormCreate(Sender: TObject); begin LoadTree(Table1); Table1.Active:=true; end; procedure TForm1.TreeClick(Sender: TObject); begin CurrentTreeNode:=Form1.tree.selected; //获得当前节点 end; end. unit PubVar; interface uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, DbTables, StdCtrls, ExtCtrls, Buttons, Dialogs,Registry,Db, ComCtrls; const cTreeCodeFormat=’222’; //编码格式:xx xx xx cTreeMaxLevel=3; //最大编码(树节点)层次 cTreeRootTxt=’城市’; //树的根节点名称 implementation end. 5. 编译运行结果图:(附后) 四、 小结: 本程序与编码法快速生成树形结构,通过TreeView控件直接操作数据表,实现了对表的数据导航。如果你想通过点击TreeView控件某项直接显示该项的相关信息,可在该程序的基础上进行修改。 原文载于《电脑编程技巧与维护》2003年第一期P27 以上源码在http://www.comprg.com.cn/tit1_rjxz.htm 可以下载得到!需要的各位可以去下载! 程序运行结果图(稍后传上) 2004-6-18 8:20:32 查看评语??? 2004-6-18 8:22:34 续篇 用Delphi实现Windows文件夹管理树 李鹏 薛志东 摘要:本文利用Windows名空间所提供的IShellFolder接口,用Delphi实现了文件夹管理树的生成。 关键字:文件夹 接口 Delphi 一、概述 Windows95/98视觉感观上区别Windows3.1的一个重要方面就是大量采用了树形视图控件,资源管理器左侧的文件夹管理树便是如此,它将本地和网络上的文件夹和文件等资源以层次树的方式罗列出来,为用户集中管理计算机提供了极大便利,同时在外貌上也焕然一新。Delphi为我们提供了大量Windows标准控件,但遗憾的是在目录浏览方面却只提供了一个Windows3.1样式的DirectoryListBox(Delphi5的测试版也是如此),因此,在Delphi中实现Windows文件夹管理树对开发更“地道”的Windows程序有着重大意义。 二、实现原理 Windows文件夹管理树的实现实质上是对Windows名空间(Namespace)的遍历。名空间中每个文件夹都提供了一个IShellFolder接口,遍历名空间的方法是: 1)调用SHGetDesktopFolder函数获得桌面文件夹的IShellFolder接口,桌面文件夹是文件夹管理树的根节点。 2)再调用所获得的IShellFolder接口的EnumObjects成员函数列举出子文件夹。 3)调用IShellFolder的BindToObject成员函数获得子文件夹的IShellFolder接口。 4)重复步骤2)、3)列举出某文件夹下的所有子文件夹,只至所获得的IShellFolder接口为nil为止。 下面解释将要用到的几个主要函数,它们在ShlObj单元中定义: 1)function SHGetDesktopFolder(var ppshf: IShellFolder): HResult; 该函数通过ppshf获得桌面文件夹的IShellFolder接口。 2)function IShellFolder.EnumObjects(hwndOwner: HWND; grfFlags: DWORD; out EnumIDList: IEnumIDList): HResult; 该函数获得一个IEnumIDList接口,通过调用该接口的Next等函数可以列举出IShellFolder接口所对应的文件夹的内容,内容的类型由grfFlags来指定。我们需要列举出子文件夹来,因此grfFlags的值指定为SHCONTF_FOLDERS。HwndOwner是属主窗口的句柄。 3)function IShellFolder.BindToObject(pidl: PItemIDList; pbcReserved: Pointer; const riid: TIID; out ppvOut: Pointer): HResult; 该函数获得某个子文件夹的IShellFolder接口,该接口由ppvOut返回。pidl是一个指向元素标识符列表的指针,Windows95/98中用元素标识符和元素标识符列表来标识名空间中的对象,它们分别类似于文件名和路径。需要特别指出的是:pidl作为参数传递给Shell API函数时,必须是相对于桌面文件夹的绝对路径,而传递给IShellFolder接口的成员函数时,则应是相对于该接口所对应文件夹的相对路径。pbcReserved应指定为nil,riid则应指定为IID_IShellFolder。 其它函数可以查阅Delphi提供的《Win32 Programmer’s Reference》。 三、程序清单 下面的源代码在Windows98中实现,并在Windows2000测试版中测试无误(程序运行结果如图1所示),有兴趣的读者可以将其改写成Delphi组件,以备常用。 unit BrowseTreeView; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ShlObj, ComCtrls; type PTreeViewItem = ^TTreeViewItem; TTreeViewItem = record ParentFolder: IShellFolder; // 接点对应的文件夹的父文件夹的IShellFolder接口 Pidl, FullPidl: PItemIDList; // 接点对应的文件夹的相对和绝对项目标识符列表 HasExpanded: Boolean; // 接点是否展开 end; 图1 程序运行结果 TForm1 = class(TForm) TreeView1: TTreeView; procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); procedure TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); private FItemList: TList; procedure SetTreeViewImageList; procedure FillTreeView(Folder: IShellFolder; FullPIDL: PItemIDList; ParentNode: TTreeNode); end; var Form1: TForm1; implementation {$R *.DFM} uses ActiveX, ComObj, ShellAPI, CommCtrl; // 以下是几个对项目标识符进行操作的函数 procedure DisposePIDL(ID: PItemIDList); var Malloc: IMalloc; begin if ID = nil then Exit; OLECheck(SHGetMalloc(Malloc)); Malloc.Free(ID); end; function CopyItemID(ID: PItemIDList): PItemIDList; var Malloc: IMalloc; begin Result := nil; OLECheck(SHGetMalloc(Malloc)); if Assigned(ID) then begin Result := Malloc.Alloc(ID^.mkid.cb + sizeof(ID^.mkid.cb)); CopyMemory(Result, ID, ID^.mkid.cb + sizeof(ID^.mkid.cb)); end; end; function NextPIDL(ID: PItemIDList): PItemIDList; begin Result := ID; Inc(PChar(Result), ID^.mkid.cb); end; function GetPIDLSize(ID: PItemIDList): Integer; begin Result := 0; if Assigned(ID) then begin Result := sizeof(ID^.mkid.cb); while ID^.mkid.cb 〈〉 0 do begin Inc(Result, ID^.mkid.cb); ID := NextPIDL(ID); end; end; end; function CreatePIDL(Size: Integer): PItemIDList; var Malloc: IMalloc; HR: HResult; begin Result := nil; HR := SHGetMalloc(Malloc); if Failed(HR) then Exit; try Result := Malloc.Alloc(Size); if Assigned(Result) then FillChar(Result^, Size, 0); finally end; end; function ConcatPIDLs(ID1, ID2: PItemIDList): PItemIDList; var cb1, cb2: Integer; begin if Assigned(ID1) then cb1 := GetPIDLSize(ID1) - sizeof(ID1^.mkid.cb) else cb1 := 0; cb2 := GetPIDLSize(ID2); Result := CreatePIDL(cb1 + cb2); if Assigned(Result) then begin if Assigned(ID1) then CopyMemory(Result, ID1, cb1); CopyMemory(PChar(Result) + cb1, ID2, cb2); end; end; // 将二进制表示的项目标识符列表转换成有可识的项目名 function GetDisplayName(Folder: IShellFolder; PIDL: PItemIDList; ForParsing: Boolean): String; var StrRet: TStrRet; P: PChar; Flags: Integer; begin Result := ’’; if ForParsing then Flags := SHGDN_FORPARSING else Flags := SHGDN_NORMAL; Folder.GetDisplayNameOf(PIDL, Flags, StrRet); case StrRet.uType of STRRET_CSTR: SetString(Result, StrRet.cStr, lStrLen(StrRet.cStr)); STRRET_OFFSET: begin P := @PIDL.mkid.abID[StrRet.uOffset - sizeof(PIDL.mkid.cb)]; SetString(Result, P, PIDL.mkid.cb - StrRet.uOffset); end; STRRET_WSTR: Result := StrRet.pOleStr; end; end; function GetIcon(PIDL: PItemIDList; Open: Boolean): Integer; const IconFlag = SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON; var FileInfo: TSHFileInfo; Flags: Integer; begin if Open then Flags := IconFlag or SHGFI_OPENICON else Flags := IconFlag; SHGetFileInfo(PChar(PIDL), 0, FileInfo, sizeof(TSHFileInfo), Flags); Result := FileInfo.iIcon; end; // 获得每个文件夹在系统中的图标 procedure GetItemIcons(FullPIDL: PItemIDList; TreeNode: TTreeNode); begin with TreeNode do begin ImageIndex := GetIcon(FullPIDL, False); SelectedIndex := GetIcon(FullPIDL, True); end; end; // 获得系统的图标列表 procedure TForm1.SetTreeViewImageList; var ImageList: THandle; FileInfo: TSHFileInfo; begin ImageList := SHGetFileInfo(PChar(’C:’), 0, FileInfo, sizeof(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON); if ImageList 〈〉 0 then TreeView_SetImageList(TreeView1.Handle, ImageList, 0); end; // 生成文件夹管理树 procedure TForm1.FillTreeView(Folder: IShellFolder; FullPIDL: PItemIDList; ParentNode: TTreeNode); var TreeViewItem: PTreeViewItem; EnumIDList: IEnumIDList; PIDLs, FullItemPIDL: PItemIDList; NumID: LongWord; ChildNode: TTreeNode; Attr: Cardinal; begin try OLECheck(Folder.EnumObjects(Handle, SHCONTF_FOLDERS, EnumIDList)); while EnumIDList.Next(1, PIDLs, NumID) = S_OK do begin FullItemPIDL := ConcatPIDLs(FullPIDL, PIDLs); TreeViewItem := New(PTreeViewItem); TreeViewItem.ParentFolder := Folder; TreeViewItem.Pidl := CopyItemID(PIDLs); TreeViewItem.FullPidl := FullItemPIDL; TreeViewItem.HasExpanded := False; FItemList.Add(TreeViewItem); ChildNode := TreeView1.Items.AddChildObject(ParentNode, GetDisplayName(Folder, PIDLs, False), TreeViewItem); GetItemIcons(FullItemPIDL, ChildNode); Attr := SFGAO_HASSUBFOLDER or SFGAO_FOLDER; Folder.GetAttributesOf(1, PIDLs, Attr); if Bool(Attr and (SFGAO_HASSUBFOLDER or SFGAO_FOLDER)) then if Bool(Attr and SFGAO_FOLDER) then if Bool(Attr and SFGAO_HASSUBFOLDER) then ChildNode.HasChildren := True; end; except // 你可在此处对异常进行处理 end; end; procedure TForm1.FormDestroy(Sender: TObject); var I: Integer; begin try for I := 0 to FItemList.Count-1 do begin DisposePIDL(PTreeViewItem(FItemList[i]).PIDL); DisposePIDL(PTreeViewItem(FItemList[i]).FullPIDL); end; FItemList.Clear; FItemList.Free; except end; end; procedure TForm1.FormCreate(Sender: TObject); var Folder: IShellFolder; begin SetTreeViewImageList; OLECheck(SHGetDesktopFolder(Folder)); FItemList := TList.Create; FillTreeView(Folder, nil, nil); end; procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var TVItem: PTreeViewItem; SHFolder: IShellFolder; begin TVItem := PTreeViewItem(Node.Data); if TVItem.HasExpanded then Exit; OLECheck(TVItem.ParentFolder.BindToObject(TVItem^.Pidl, nil, IID_IShellFolder, Pointer(SHFolder))); FillTreeView(SHFolder, TVItem^.FullPidl, Node); Node.AlphaSort; TVItem^.HasExpanded := True; end; end. 2004-6-18 8:23:37 再续 仅仅十几行代码实现对TreeView的遍历 摘 要:对TreeView的遍历 关键字:TreeView 类 别:Delphi & IDE E-Mail:iloveyou9595@sina.com function TForm1.AllOverTreeView(node:TTreenode):TTreenode; begin while node〈〉nil do begin if node.HasChildren then begin node:=node.getFirstChild; allovertreeview(node); node:=node.Parent; end; if node.getNextSibling〈〉nil then node:=node.getNextSibling else exit; end; end; procedure TForm1.Button1Click(Sender: TObject); var parentnode:TTreenode; begin parentnode:=Mytreeview.Items.GetFirstNode; AllOverTreeView(parentnode); end; ------------------------------------------------------ 遍历TreeView的方法有很多,我经过反复编程实现,上面是我用最少的代码实现TreeView的遍历。效果还不错。 利用这个对所有节点的遍历,我们可以很方便的对所有节点进行各种操作。例如:统计每层节点的个数、对满足要求的节点进行操作、等等。 投稿人:iloveyou9595 投稿日期:2003-3-26 23:51:00 2004-6-18 8:24:32 再续... TreeView用法参考 unit Main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) SearchBtn: TButton; DirectoryEdt: TMemo; PathEdt: TEdit; Label1: TLabel; Image1: TImage; procedure SearchBtnClick(Sender: TObject); procedure MakeTree; private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.MakeTree; var Sr: TSearchRec; Err: Integer; FilePath: string; begin Err := FindFirst(’*.*’,$37,Sr);//$37为除Volumn ID Files外的所有文件 // 如果找到文件 while (Err = 0) do begin if Sr.Name[1] 〈〉 ’.’ then begin //找到文件 if (Sr.Attr and faDirectory) = 0 then begin end; //找到子目录 if (Sr.Attr and faDirectory) = 16 then begin FilePath := ExpandFileName(Sr.Name); DirectoryEdt.Lines.Add(FilePath); ChDir(Sr.Name); MakeTree; ChDir(’..’); end; end; //结束递归 Err := FindNext(Sr); end; end; procedure TForm1.SearchBtnClick(Sender: TObject); begin DirectoryEdt.Lines.Clear; ChDir(PathEdt.Text); MakeTree; end; end. 这是个递归搜文件的例子, 摘 要:将数据表连接到TreeView中 关键字:数据表 TreeView 类 别:API CoDelphi.com版权所有,未经允许,不得进行任何形式转载 procedure AddDataToTree(TreeView:TTreeView;DataSet:TDataSet) var TreeNodes:TTreeNodes TreeNode:array[0..100] of TTreeNode; i:Integer; begin DataSet.Close; DataSet.Open; TreeNodes=TTreeView.Items; if DataSet.RecordCount〉0 then begin DataSet.First; while not DataSet.Eof do begin TreeNode[0]=TreeNodes.Add(Nil,DataSet.Fields[0].AsString); for i=1 to DataSet.Fields.Count-1 do TreeNode[i]=TreeNodes.AddChild(TreeNode[i-1],DataSet.Fields[i].AsString); DataSet.Next; end; end; end; unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ImgList, StdCtrls, FileCtrl, ComCtrls; type TForm1 = class(TForm) DirTreeView: TTreeView; DriveComboBox1: TDriveComboBox; FileListBox1: TFileListBox; ImageList1: TImageList; procedure FormCreate(Sender: TObject); procedure DirTreeViewExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); var FirstNode,DirNode : TTreeNode; ItemCount,Index:integer; Itemstr:string; begin ItemCount:= DriveComboBox1.Items.Count; //所有驅動器的個數 FirstNode := DirTreeView.Items.GetFirstNode; for index := 0 to ItemCount -1 do begin ItemStr:= DriveComboBox1.Items[index]; ItemStr:= copy(ItemStr,1,pos(’:’,ItemStr)) ; //?得驅動器的名稱(比如C/D) DirNode := DirTreeView.Items.AddChild(FirstNode, ItemStr ); DirNode.HasChildren := true; DirNode.ImageIndex := 0; DirNode.SelectedIndex := 1; end; end; procedure TForm1.DirTreeViewExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var DirNode : TTreeNode; ItemCount,Index,level,icount:integer; Itemstr,strPath:string; begin if node.Count = 0 then begin icount:=0; level:=node.Level ; dirnode:=node; strPath:=node.Text+’’ ; while level〈〉0 do begin strPath:=dirnode.Parent.Text+’’+strpath; dirnode:=dirnode.parent; level :=level -1; end; FileListBox1.Clear ; FileListBox1.Directory := strpath; ItemCount:= FileListBox1.Items.Count; for index:=0 to ItemCount -1 do begin itemstr:=filelistbox1.items[index]; itemstr:= copy(ItemStr,2,pos(’]’,ItemStr)-2) ; if (itemstr〈〉’.’) and (itemstr〈〉’..’) then begin DirNode := DirTreeView.Items.AddChild(Node,itemstr ); DirNode.HasChildren :=true; DirNode.ImageIndex := 0; DirNode.SelectedIndex := 1; icount:=icount+1; end; if icount = 0 then Node.HasChildren := false; end; end; end; end. procedure TMain.CreateTree(QuerySource:TADOQuery;NodeParent:TTreeNode;treeview1:ttreeview); var pstr1, pstr2 : ^string; NodeTemp : TTreeNode; begin pstr1 := NodeParent.Data; with QuerySource do begin close; sql.Clear; sql.Text:=’SELECT key,xcode,xname FROM xzdm WHERE parent = ’ + ’’’’ + pstr1^ + ’’’’; open; if isempty then exit; NodeTemp := nil; while not eof do begin new(pstr2); pstr2^ := FieldByName(’key’).AsString; NodeTemp := TreeView1.Items.AddChildObject(NodeParent, trim(FieldByName(’xname’).AsString)+’(’+fieldbyname(’xcode’).AsString+’)’, pstr2); Next; end; end; while NodeTemp 〈〉 nil do begin CreateTree(QuerySource, NodeTemp,treeview1); NodeTemp := Nodetemp.getPrevSibling; end; end; procedure TMain.RootTree(treeview1:ttreeview); var NodeTemp : TTreeNode; pstr : ^string; Query:TADOQuery; begin Query:=TADOQuery.Create(self); query.Connection:=BgConnection; try Treeview1.Items.BeginUpdate; with query do begin SQL.Text :=’select top 1 * from xzdm ’; open; if isempty then exit; NodeTemp := nil; while not eof do begin new(pstr); pstr^ := FieldByName(’key’).AsString; NodeTemp :=treeview1.Items.AddObject(nil, trim(FieldByName(’xname’).AsString)+’(’+fieldbyname(’xcode’).AsString+’)’, pstr); Next; end; end; while NodeTemp 〈〉 nil do begin CreateTree(Query, NodeTemp,treeview1); NodeTemp:=NodeTemp.getPrevSibling; end; treeview1.Items.EndUpdate; finally Query.Free; end; end; var node1:Ttreenode; begin node1:=TreeView1.items.AddChild(node,’收件箱’);//建一个节点 node1.ImageIndex:=86;//节点图象,要加imagelist控件 node1.SelectedIndex:=92; node1.Data:=pointer(0);//重要,node1.data可以存入你有用ID end; 其实看在线帮助是最好的了!另外就是要多用,Treeview我用的比较多,有什么问题可以问具体点! 楼上的说的不错,要注意的是Node节点中的.data是一个指针,要小心使用! Delphi帮助文件里是这样写的: Set ToolTips to True to specify that items in the tree view control have tooltips (Help Hints 当前的节点为: TreeView1.Selected; 他的字节点:child,父节点:Parent 其中的TreeNode的类型下可以保存一个指针类型的值; var CurItem: TTreeNode; begin CurItem := TreeView1.Items.GetFirstNode; while CurItem 〈〉 nildo begin ListBox1.Items.Add(CurItem.Text); CurItem := CurItem.GetNext; end; end 用GetFirstNode,GetNext会比较快 怎么知道我选中的是几级节点呀? procedure TForm1.Button1Click(Sender: TObject); begin showmessage(vartostr(TreeView1.selected.level)); end; 在节点展开的事件中如下: var sID:string; Node,NewNode;TTreeNode; begin Node := TreeView1.Selected; sID := PCHAR(Node.Data); Query1.Close; Query1.SQL.Clear; Query1.SQL.Add(’select * from mytbl where ParentID=’ + sID); Query1.Open; Node.Items.Clear; //清除以下所有节点 While note Query1.Eof do begin NewNode:= TTreeView1.Items.AddChild(Node,Query1.FieldByName(’mc’).AsString); NewNode.ImageIndex := ***; Node.Data := PChar(Query1.FieldByName(’ID’).AsString); //注意保留索引值 Query1.Next; end; 另外在TreeView的节点清除时注意释放内存。 在节点展开的事件中如下: var sID:string; NewNode;TTreeNode; begin sID := PCHAR(Node.Data); Query1.Close; Query1.SQL.Clear; Query1.SQL.Add(’select * from mytbl where ParentID=’ + sID); Query1.Open; Node.Items.Clear; //清除以下所有节点 While note Query1.Eof do begin NewNode:= TTreeView1.Items.AddChild(Node,Query1.FieldByName(’mc’).AsString); NewNode.ImageIndex := ***; Node.Data := PChar(Query1.FieldByName(’ID’).AsString); //注意保留索引值 Query1.Next; end; 以上程序仅供叁考,没做测试。 PNodeRec = 你定义的record 的变量; procedure loadRootNode var pID:integer; aNode: TTreeNode; Item : PNodeRec; begin Query1.close; Query1.SQL.clear; Query1.SQL.add(’select * from T where parentID=0’); Query1.open; where not Query1.eof do begin pID:=Query1.fieldByName(’id’).asInteger; //(1)加载一个根节点 ... ... //aNode:=TV.Items.AddObject(nil, Item^.Name, Item); //(2)加载此根节点下的所有子节点 loadChilds(pID,aNode); Query1.next; end; end; prodedure loadChilds(pID:integer;pNode:TTreeNode); var cpID:integer; aNode: TTreeNode; Item : PNodeRec; begin //(1)加载子节点 Query2.close; Query2.SQL.clear; Query2.SQL.add(’select * from T where parentID=’+intToStr(pID)); Query2.open; where not Query2.eof do begin //载入子节点 ... ... //TV.Items.AddChildobject(pNode,Item^.Name,item); Query2.next; end; //(2)递归载入子节点的子节点 for i:=0 to pNode.Count -1 do begin LoadChild(PNodeRec(pNode.Item[i].data)^.parentID,pNode.item[i]); end; end; 大家是从算法上来说,我来从GUI方面来说。 TreeList1.Items.BeginUpdate; ....执行添加代码 TreeList1.Items.EndUpdate; 哎呀,来晚了! Eastunfail(恶鱼杀手)对!浪费时间的部分主要实在绘制上,不用BeginUpdate和用BeginUpdate在数据量较大时,差着“十万八千里”呢!算法当然也很重要,但要是从最快的角度将,影响最大的还是BeginUpdate和EndUpdate(就是等数据全部加载完毕再进行绘制)。我有亲身体会... //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //无限层数:数据严格按照(层(在TreeView上),ParentID,ID,Text,图标序号)顺序排序************ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ procedure DataSetToTreeView(DataSet: TDataSet; var TreeView: TTreeView; var TreeList: TStringList); overload; var IsAvtive: Boolean; TempIndex: integer; TempNode: TTreeNode; begin DataSet.DisableControls; IsAvtive := DataSet.Active; if not IsAvtive then DataSet.Open; //~~~~~~~~~~~~
项目高效加载树节点
procedure ComplexTypeFour ; {connMenu: TADOConnection; qryMenu : TADOQuery ; //数据查询 TreeViewMenu:TTreeView; lblTime.Caption := FormatDateTime('hh:mm:ss.zz', tEnd - tStart) ; lblCount.Caption := IntToStr(nCount) + ' menu items added.' ; } procedure TMainFrm_U.ComplexTypeFour ; type PMenuTreeItem = ^TMenuTreeItem ; TMenuTreeItem = Record ItemID : String ; ParentID : String ; Caption : String; // Description : String; end; type //指针类型数据 PQryMenuRec = ^TQryMenuRec ; TQryMenuRec = Record ItemID : String ; ParentID : String ; Caption : String; RecNo : Integer ; end; var qryMenu : TUniQuery ; //数据查询 aRecord : PMenuTreeItem ; aMenuRec : PQryMenuRec ; aMenuList : TList ; //TList是Bennet-Tec公司开发的可以用来显示Tree,Grid,list等形式的结构化数据和数据对象,如用户列表,报表,组织结构等信息,界面。 ParentNode, NextNode , MenuNode : TTreeNode ; tStart, tEnd : TDateTime ; i, nAdded, nRecNo : Integer ; lFindNode : Boolean ; nCount : Integer ; connMenu:TUniConnection; begin connMenu:=UniConnection1; tStart := Now ; nCount := 0 ; if Not(connMenu.Connected) then connMenu.Open ; TreeView1.Items.Clear; TreeView1.Items.BeginUpdate ; qryMenu := TUniQuery.Create(nil); //qryMenu.CursorLocation := clUseServer ; qryMenu.Connection := connMenu ; qryMenu.SQL.Add('SELECT * FROM type_tree ' + 'WHERE parent_id = 0 ' + 'ORDER BY type_id') ; qryMenu.Open ; while Not(qryMenu.Eof) do begin New(aRecord) ; aRecord.ParentID := qryMenu.FieldByName('parent_id').AsString ; aRecord.ItemID := qryMenu.FieldByName('type_id').AsString ; aRecord.Caption := qryMenu.FieldByName('type_name').AsString ; MenuNode := TreeView1.Items.AddChildObject(NIL,aRecord.Caption,aRecord); Inc(nCount) ; MenuNode.ImageIndex := 0; MenuNode.SelectedIndex := 1; MenuNode.StateIndex := -1; qryMenu.Next; end; qryMenu.Close ; qryMenu.SQL.Clear ; qryMenu.SQL.Add('SELECT * FROM type_tree ' + 'WHERE parent_id > 0 ' + 'ORDER BY parent_id DESC, type_id DESC') ; qryMenu.Open ; aMenuList := TList.Create ; while not(qryMenu.Eof) do begin New(aMenuRec) ; aMenuRec.ParentID := qryMenu.FieldByName('parent_id').AsString ; aMenuRec.ItemID := qryMenu.FieldByName('type_id').AsString ; aMenuRec.Caption := qryMenu.FieldByName('type_name').AsString ; aMenuList.Add(aMenuRec) ; qryMenu.Next; end; while true do begin nAdded := 0 ; ParentNode := nil ; NextNode := nil ; for i := (aMenuList.Count - 1) downto 0 do begin aMenuRec := PQryMenuRec(aMenuList.Items[i]) ; lFindNode := False ; if Not(Assigned(ParentNode)) then lFindNode := True else begin if aMenuRec.ParentID <> PMenuTreeItem(ParentNode.Data).ItemID then begin if Assigned(NextNode) and (aMenuRec.ParentID = PMenuTreeItem(NextNode.Data).ItemID) then begin ParentNode := NextNode ; NextNode := nil ; end else lFindNode := True ; end; end; if lFindNode then begin ParentNode := TreeView1.Items.GetFirstNode ; while Assigned(ParentNode) do begin if aMenuRec.ParentID = PMenuTreeItem(ParentNode.Data).ItemID then begin NextNode := ParentNode.GetNext ; break ; end; ParentNode := ParentNode.GetNext ; end; end; if Assigned(ParentNode) then begin New(aRecord) ; aRecord.ParentID := aMenuRec.ParentID ; aRecord.ItemID := aMenuRec.ItemID ; aRecord.Caption := aMenuRec.Caption ; MenuNode := TreeView1.Items.AddChildObject(ParentNode,aRecord.Caption,aRecord); Inc(nCount) ; MenuNode.ImageIndex := 0; MenuNode.SelectedIndex := 1; MenuNode.StateIndex := -1; Inc(nAdded) ; Dispose(aMenuRec) ; aMenuList.Delete(i); end; end; if nAdded = 0 then break ; end; qryMenu.Close ; qryMenu.Free ; connMenu.Close ; for i := 0 to (aMenuList.Count - 1) do Dispose(PQryMenuRec(aMenuList.Items[i])) ; aMenuList.Clear ; aMenuList.Free ; TreeView1.Items.Item[0].Expand(True); ParentNode := TreeView1.Items.GetFirstNode ; ParentNode.MakeVisible ; TreeView1.Items.EndUpdate ; tEnd := Now ; StatusBar1.Panels[1].Text := FormatDateTime('hh:mm:ss.zz', tEnd - tStart) ; StatusBar1.Panels[3].Text := IntToStr(nCount) + ' menu items added.' ; end;
用TreeView控件从数据库中动态装载信息
1.PInfo表结构 ID VARCHAR(50) FullName VARCHAR(50) ParentID VARCHAR(50) 2.Unit文件 unit Info; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, DB, ADODB; type PNodeInfo=^TNodeInfo; TNodeInfo=record ID:string; FullName:string; end; TfmInfo = class(TForm) TreeView1: TTreeView; btnShowInfo: TButton; ADOQuery1: TADOQuery; ADOConnection1: TADOConnection; procedure CreateChildTree(ParentNode: TTreeNode); procedure btnShowInfoClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var fmInfo: TfmInfo; implementation {$R *.dfm} //创建子树 procedure TfmInfo.CreateChildTree(ParentNode: TTreeNode); var Query:TADOQuery; ChildNode:TTreeNode; //孩子结点 ChildNodeInfo:PNodeInfo; //孩子结点信息 begin Query:=TADOQuery.Create(nil); with Query do begin Connection:=ADOConnection1; SQL.Add('SELECT ID,FullName FROM PInfo WHERE ParentID = '''+PNodeInfo(ParentNode.Data)^.ID+''''); //获取孩子结点信息 Open; while not Eof do begin New(ChildNodeInfo); ChildNodeInfo^.ID:=FieldByName('ID').AsString; ChildNodeInfo^.FullName:=FieldByName('FullName').AsString; ChildNode:=TreeView1.Items.AddChildObject(ParentNode,(ChildNodeInfo^.ID+ChildNodeInfo^.FullName),ChildNodeInfo); //添加孩子结点,并关联孩子结点信息 CreateChildTree(ChildNode); //进行递归 Next; end; Close; end; end; procedure TfmInfo.btnShowInfoClick(Sender: TObject); var BootNode:TTreeNode; //根结点 BootNodeInfo:PNodeInfo; //根结点信息 begin with ADOQuery1 do begin SQL.Clear; SQL.Add('SELECT ID,FullName FROM PInfo WHERE ParentID IS NULL'); //获取根结点信息 Open; New(BootNodeInfo); BootNodeInfo^.ID:=FieldByName('ID').AsString; BootNodeInfo^.FullName:=FieldByName('FullName').AsString; TreeView1.Items.Clear; BootNode:=TreeView1.Items.AddChildObject(nil,(BootNodeInfo^.ID+BootNodeInfo^.FullName),BootNodeInfo); //添加根结点,并关联根结点信息 Close; end; CreateChildTree(BootNode); //创建子树 TreeView1.FullExpand; //展开所有树结点 end; end.
TreeView是Delphi中使用频率比较高的一个控件,虽然使用次数很多,但总结不够。借着这次做GDW原型的机会总结一下,写的过程中也会参考网上的博文。
TTreeView、TTreeNodes和TTreeNode
TTreeView由节点构成,建树通过对TreeView.items属性进行操作。Items是一个TTreeNodes对象,这是一个TTreeNode集。
常用的属性
Count,结点个数;
Item[index],通过index得到结点;
TTreeNode.Data,指向一个指针,可以存对象,存指针,也可以存整数;
TTreeNode.Text,树结点的文本;
TTreeNode.ImageIndex,TTreeNode.SelectedIndex,分别是树结点图标序号,树结点选中时图标序号,用于设置树结点的图标;
TTreeNode.Expanded属性表明是否所有的子项都全部展开;
TTreeNode.HasChildren属性表明一个项是否有子项;
TTreeNode.Focused属性确定焦点是否落在此节点上,被Focus时会一个标准的方框围住,只能有一个节点会被聚焦。
TTreeNode.Selected属性表明一个节点是否被选中,同样只有一个节点会被选中。
常用的方法
GetFirstNode 得到根结点;
TTreeNode.GetNext 得到本节点的下一个结点,配合GetFirstNode可以遍历整个树;
AddFirst 添加第一个根节点,此函数添加的节点总排在前面,除非后来又使用此函数添加了一个节点,则后添加的节点将排在前面。返回新添加的节点。
AddChild添加一个子节点,要求有父节点作为其参数。返回新添加的节点。
Add添加一个兄弟节点,要求有兄弟节点作为其参数。返回新添加的节点。
TTreeNode的一些结点关系方法:GetFirstChild, GetLastChild, GetPrevChild, and GetNextChild分别返回当前项子项的第一个、最后一个和前一个、后一个项。GetNextSibling、 GetPrevSibling则返回在同一Level下的下一个和上一个项。
常用的事件
当从一个节点跳到另一个节点,会触发TTreeView.OnChange事件。该事件中,将传递node,即当前被选中的节点。
当修改一个节点的text时,会触发TTreeView.OnEdit事件。
TreeView的常见使用方法
添加、删除和编辑树结点
用AddFirst, AddFirstChild, AddChild等先添加根节点,如Treeview.Items.AddFirst( nil, 'Root');然后以此为基础,添加此项的子节点。
删除节点:Treeview.Selected.Delete
编辑节点内容:Treeview.Selected.EditText
为了提升效率,避免界面大幅闪动,最好使用TreeView.Items.BeginUpdate 和 TreeView.Items.EndUpdate 方法;
设置树结点图标
ImageIndex:在常态时选用的图的序号;
SelectedIndex:当节点被选中时在TimageList 中选什么样的图象;
一段代码
1 Items.BeginUpdate;
2 Items.Clear;
3 // 建立第一层节点
4 AddElemntType2Tree(AElementTypeID, AAddNoChildNode);
5 // 建立第二层节点
6 if Items.GetFirstNode <> nil then
7 begin
8 oNode := Items[0]; // 选择第一个构件类型结点
9 while oNode <> nil do
10 begin
11 UpdateTreeNode(oNode, ASelectedElementID);
12 oNode := oNode.getNextSibling;
13 end;
14 end;
15 // 展开
16 if Selected <> nil then
17 Selected.Expanded := True
18 else if Items.Count > 0 then
19 begin
20 oNode := Items.GetFirstNode;
21 Assert(oNode <> nil);
22 // 选中第一个节点的第一个子节点(若存在则为第一个构件类型下的第一个构件)。
23 Selected := oNode.getFirstChild();
24 // 若没有选中构件,则选中第一个构件类型节点,否则展开节点。
25 if Selected = nil then
26 Selected := oNode
27 else
28 Selected.Expanded := True;
29 end;
30 Items.EndUpdate;