TreeView

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
View Code

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按钮,来将你的桌面设置保存起来供下次使用。
View Code


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;
View Code

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;
View Code

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;
View Code

4.内存释放

procedure TForm1.ListView1Deletion(Sender: TObject; Item: TListItem);
begin
Dispose(Item.Data);
end;
View Code

七、一般使用流程:

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 的用法结束******************//
View Code

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.
View Code

实现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资源管理器中所作的那样,而不需要逐级回退之后才能进行切换。
View Code

递归加载树形(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.
View Code

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;
View Code

TreeView多节点删除

for i:=0 to form1.TreeView.SelectionCount-1 do
begin
     Form1.TreeView.Items.Item[i].Delete;
end;
View Code

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;  
View Code

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;
 
View Code

遍历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;

上面的函数可以遍历某个节点下的所有节点。
View Code

上、下、左、右移动

来自CSDN: 

(*// 
标题:移动树节点 
说明:上、下、左、右移动 
设计:Zswang 
日期:2002-06-08 
支持:[email protected] 
//*) 
///////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.
View Code

上下左右移动树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 &lt;&gt; 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
View Code

树形控件(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如何使用,与本次的主题关系不大,以后有时间再来讲吧。
View Code

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;
View Code
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;
View Code

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;
View Code

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; 
看看这个吧! 
View Code
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;
View Code
使用一个TreeView, 
数据库结构要增加字段,记录父结点ID: 
ID PID Caption FatherID 
1  0  aaa      
2  1  abb    1 
3  1  abc    2 
4  2  abbc    2 
通过编写一个读取数据库文件的方式,即可实现TreeView与数据库的关联。
View Code

大数据高效加载法

{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;
View Code

导出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似乎更友好. 
View Code

读取到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.
View Code

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
View Code

添加节点 子节点 数据库调用

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;
View Code

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;
View Code

数据库表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; 
View Code

通过连数据库自动生成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;
View Code

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〈〉’0then
    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单元中定义:
    1function SHGetDesktopFolder(var ppshf: IShellFolder): HResult;
    该函数通过ppshf获得桌面文件夹的IShellFolder接口。
    2function IShellFolder.EnumObjects(hwndOwner: HWND; grfFlags: DWORD;
    out EnumIDList: IEnumIDList): HResult;
    该函数获得一个IEnumIDList接口,通过调用该接口的Next等函数可以列举出IShellFolder接口所对应的文件夹的内容,内容的类型由grfFlags来指定。我们需要列举出子文件夹来,因此grfFlags的值指定为SHCONTF_FOLDERS。HwndOwner是属主窗口的句柄。
    3function 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:[email protected]

    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;
    //~~~~~~~~~~~~
View Code

项目高效加载树节点

   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;
View Code

用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.  
View Code

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;

猜你喜欢

转载自www.cnblogs.com/blogpro/p/11453055.html