转载 我也没有看过,你慢慢看吧
主  题:TTreeView怎么构建层次树形结构(数据库相关)?
作  者:Jiker
所属论坛:Delphi
问题点数:100
回复次数:42
发表时间:2001-9-16 2:08:36
 
  
  Oracle PL/SQL:
Select Level as 级数,ID,上级ID,名称 From 材料表
Start With 上级ID is NULL Connect by ID=上级ID
------------------------------------------------
级数      ID      上级ID        名称
-------  --------  --------  ---------------
1          23      NULL      电脑配件
2          42      23        显示器
3          45      42        LED显示器
3          46      42        CRT显示器
4          52      46        纯平显示器
4          53      46        球面显示器
2          11      23        光盘驱动器
3          12      11        普通光驱
3          13      11        刻录光驱
3          67      11        DVD光驱
1          14      NULL      电脑耗材
....
------------------------------------------------
树形结构如下:
电脑配件
  显示器
    LED显示器
    CRT显示器
      纯平显示器
      球面显示器
  光盘驱动器
    普通光驱
    刻录光驱
    DVD光驱
电脑耗材
------------------------------------------------
我想根据这些数据将它们装入TTreeview,但不能根据一个记录的上级ID直接获取其父结点,我又不想用遍历查找的低效方法,请教各位有什么好办法?TListview中也存在这个问题,如何直接得到一个Item(不用Index,不用Find...)。
还有ListView中的FindData方法如何利用,我只知道要查找的Data指针指向的内容是什么,但它又要我提供对应的地址,这个时候怎么知道,指针变量已重复分配了多次内存,且已在局部方法结束时释放掉了。 
回复贴子: 
回复人: first_lover(冷雨夜) (2001-9-16 2:12:50)  得0分 
up  
回复人: jiangsheng(蒋晟) (2001-9-16 4:53:15)  得0分 
看看Visual Foxpro的solution ActiveX控件示例。  
回复人: zzutlink(来溜达) (2001-9-16 9:18:17)  得0分 
你在treeview的onexpand事件中进行处理其下一层的展开  
回复人: zzutlink(来溜达) (2001-9-16 9:20:29)  得0分 
建议你修改数据库的结构,将可以得到更高效的解决方法  
回复人: wyb_star(孤星) (2001-9-16 9:38:16)  得0分 
用递归不就可以了!  
回复人: Jiker(程序员是什么?) (2001-9-16 17:28:56)  得0分 
看来Delphi无法方便地实现了,不是要遍历树,就是要对Query作Filter处理。
VB可以很方便地实现,它的集合有字符串型的关键字。这是多么重要的一个功能。  
回复人: kuchong(苦虫) (2001-9-16 18:42:55)  得0分 
我的方法挺土的
先给treeview1.items[n].data:=pointer(唯一的标记:integer)
当然需要用到一个链表
它用来保存标题信息的
然后存为文件
数据库中记录内容
使用唯一的标记同树相对应下回装入时只须装入树结构恢复以前的唯一标记
只有点击树叶时才利用标记查找相应的内容可能叙述不太清楚,我有源程要的话Mailto:[email protected] 
回复人: Jiker(程序员是什么?) (2001-9-16 23:45:16)  得0分 
这么麻烦呀,还要用链表,文件。不如用个结构数组来保存对应关系(比操作Items,ADOQuery更快)。可能以前用VB习惯了,我现在对Delphi自称数据库方面的快速开发产生了很大的怀疑。  
回复人: ffossil(吴下阿蒙) (2001-9-17 0:32:23)  得0分 
node[] :array of ttreenode;select max(level)  ...imax
setlength(node,imax);select level.....
open;ttreeview.items.clear;
node[0] := ttreeview.getfirstnode;while not eof do begin
    i := fieldbyname('level').asinteger;
    node[i]  := ttreeview.items.add(node[i-1],[...id+mingcheng...]);
    next;
end;good luck  
回复人: taohongjun(安意) (2001-9-17 10:00:03)  得0分 
表结构的定义不正确,自已想想吧!  
回复人: taohongjun(安意) (2001-9-17 10:01:33)  得0分 
需用到递归算法!  
回复人: davidTIm(david) (2001-9-17 10:47:06)  得0分 
用递归啊,这些我早就用过了  
回复人: cszhz(丑小鸭) (2001-9-17 10:52:25)  得0分 
不知是否可以这样解决:
1.在FormShow事件中,添加上级id=null的节点.
2.在TreeView1Expanding事件中,根据所选取得node判断是否有children,如果就动态添加好了。(注:这种方法的效率较高,尤其在树结构较为复杂的情形)。
有问题:[email protected]  
回复人: Jiker(程序员是什么?) (2001-9-17 22:30:23)  得0分 
taohongjun(安意):
  这是一种比较简洁有效的表结构,哪里不正确,我都用了几年了。
  所谓递归算法是否指在哪里?SQL中还是Treeview中?cszhz(丑小鸭):
  数据量很大时肯定应该用你说的方法,而且这是一个很好的方法,实现起来也很简单。
  但数据量较小时就需要一次性全部显示出来。对这个问题,基于Oracle,我想出来了一个比较好的解决办法:
将SQL语句改为:
Select A.ID,A.名称,A.idx,B.idx As ParentIdx,A.级数
From (Select Rownum As Idx,Level As 级数,ID,上级ID,名称 
From 材料表
Start With 上级ID Is Null Connect By Prior ID=上级ID) A,
(Select Rownum As Idx,ID 
From 材料表 
Start With 上级ID Is Null Connect By Prior ID=上级ID) B
Where A.上级ID=B.ID(+) 
Order By A.idx
这样将ID及上级ID组织成对应的Index及ParentIndex,在Treeview中依顺序Add,并可以直接通过Index获得其父结点作为Add的参数。当数据量不大时(千条记录级),这个办法很好,查询速度基本不受影响,而且一次循环就完成了树形显示。但数据量大了可能SQL就有点慢。对于SQLServer及其它数据库,级数(Level)虚列可以用数据结构实现,但Rownum列就不支持了,因此还得用其它办法。就是上面各位所说的递归,我还没弄明白?  
回复人: Jiker(程序员是什么?) (2001-9-17 22:38:35)  得0分 
ffossil(吴下阿蒙):
  你的代码什么意思,我看不大懂,可以指点一下吗(不是指语法)?比某个结点小一级的结点不一定就是它的父结点呀?
 
回复人: Jiker(程序员是什么?) (2001-9-17 22:39:20)  得0分 
taohongjun(安意):
  可以说说你的递归方法吗?  
回复人: ffossil(吴下阿蒙) (2001-9-18 11:11:55)  得0分 
“Select Level as 级数,ID,上级ID,名称 From 材料表
Start With 上级ID is NULL Connect by ID=上级ID”是按深度优先的,你可以浏览数据中的LEVEL的顺序,没有问题的具体可找一篇关于财务分级编码处理的一篇文章,我就不多说了,可以在计算机世界网找找
http://www.computerworld.com.cn/search/theme/themefile.asp?ThemeID=323
 
回复人: cuiyxy(沧海鲨鱼) (2001-9-18 11:56:41)  得0分 
建议用递归,我常这么做
用递归的感觉很爽~~~~啊  
回复人: wyb_star(孤星) (2001-9-18 12:01:00)  得0分 
dbtreeview的代码,你看看吧unit DBTrees;interfaceuses ComCtrls, DB, Classes, Sysutils;typeTDBTreeView=class(TTreeView)
private
fTable:TDataSet;
fId,fParentId,fName:string;
function AddItem(var ParentIDList:TStringList):TTreeNode;
procedure GetInitParentID(var parentIdList:TStringList);
public
function GetId:integer;
procedure BuildTree;
procedure ClearTree;
function FindItem(Id:integer):TTreeNode;
function SelectItem(Id:integer):TTreeNode;
published
property FieldID:string Read fID Write fID;
property FieldParentID:string Read fParentId Write fParentId;
property FieldName:string Read fName Write fName;
property DataSource:TDataSet Read fTable Write fTable;
property ActiveId:integer Read GetId;
end;procedure Register;implementationfunction TDBTreeView.FindItem(Id:integer):TTreeNode;
var
i:integer;
begin
Result:=nil;
For i:=0 to Items.Count-1 do
begin
if integer(Items[i].Data) = Id then
begin
Result:=Items[i];
Exit;
end;
end;
end;function TDBTreeView.GetId:integer;
begin
If Selected=nil then Result:= -1
else Result:=integer(Selected.Data);
end;procedure TDBTreeView.GetInitParentID(var parentIdList:TStringList);
var
childIdList:TStringList;
i:integer;
begin
childIDList := TStringList.Create;
try
parentIDList.Clear;fTable.Filtered := false;
fTable.First;While not fTable.Eof do
begin
parentIdList.add(fTable.FieldByName(fParentId).AsString);if fTable.FieldByName(fParentId).AsString <> fTable.FieldByName(fId).AsString then
childIdList.add(fTable.FieldByName(fId).AsString);fTable.next;
end;for i := parentIdList.Count - 1 downto 0 do
begin
if childIdList.IndexOf(parentIdList[i]) <> -1 then
parentIdList.Delete(i);
end;finally
childIdList.Free;
end;
end;function TDBTreeView.AddItem(var ParentIDList:TStringList):TTreeNode;
var
CurrentItem:TTreeNode;
Name:string;
CurId:integer;
begin
CurrentItem:=FindItem(fTable.FieldByName(fId).AsInteger);//已经安装
If CurrentItem <> nil then
begin
Result := nil;
Exit;
end;//父节点还未安装
if ParentIdList.Indexof(fTable.FieldByName(fParentID).AsString) = -1 then
begin
Result := nil;
exit;
end;Name:=fTable.FieldByName(fName).AsString;
CurId :=fTable.FieldByName(fID).Asinteger;//找到父节点,装在父节点下面
CurrentItem := FindItem(fTable.FieldByName(fParentID).Asinteger);
Result:=Items.AddChildObject(CurrentItem, Name, Pointer(CurID));ParentIdList.Add(IntToStr(CurID));
end;procedure TDBTreeView.BuildTree;
var
ChangeEvent:TTVChangedEvent;
initParentIdList:TStringList;
HasChanged:boolean;
begin
If (fTable=nil) or (not fTable.Active) then Exit;
ChangeEvent:=OnChange;
OnChange:=nil;
Items.BeginUpdate;
try
initParentIdList := TStringList.Create;
try
GetInitParentID(initParentIdList);
ClearTree;
HasChanged := true;while HasChanged do
begin
HasChanged := false;
fTable.Filtered := false;
fTable.First;
While not fTable.Eof do
begin
if AddItem(initParentIdList) <> nil then
HasChanged := true;
fTable.Next;
end;
end;
finally
initParentIdList.Free;
end;
finally
Items.EndUpdate;
OnChange:=ChangeEvent;
end;
end;procedure TDBTreeView.ClearTree;
begin
Items.BeginUpdate;
While Items.Count>0 do Items.Delete(Items[0]);
Items.EndUpdate;
end;procedure Register;
begin
RegisterComponents('QuickMail',[TDBTreeView]);
end;function TDBTreeView.SelectItem(Id: integer): TTreeNode;
var
aNode:TTreeNode;
begin
result := FindItem(Id);
if result = nil then exit;
aNode := result.parent;
while aNode <> nil do
begin
aNode.Expand(false);
aNode := aNode.Parent;
end;
result.Selected := true;
end;end. 
回复人: lldwolf(wolf) (2001-9-18 12:16:35)  得0分 
up  
回复人: feifeitutu(网上飞飞) (2001-9-18 12:27:17)  得0分 
up  
回复人: kerplayer(含玉) (2001-9-18 12:37:33)  得0分 
生成treeview时,首先生成父ID为null的子节点,再生成孙节点,......  
回复人: lengxue(冷血) (2001-9-18 16:21:46)  得0分 
这个问题没那么复杂
我刚刚做了财务软件的科目的ttreeview 结构
刚开始我也用了递归啊什么的算法
实际上,根本就很简单
只要建立一个代码字段(字符串),并且排序
把字段读出来就可以了!
如:
100
100001
100002
100002001

读出字段后
判断字段的长度是3就是一级节点,是六就是二级。
呵呵,只要读一次数据库就可以了
        
回复人: hwy() (2001-9-18 16:34:58)  得0分 
lengxue的方法是最简单的,我也用过了,以前有用递归,实际上递归并不可取,(能不用则不用),会增加系统资源的负担。  
回复人: Water_E(老大~~~俺真的很笨) (2001-9-18 16:38:42)  得0分 
gz
 
回复人: ylk_pop(贪食蛇) (2001-9-18 16:47:46)  得0分 
参考速达软件(SupperData 2000+ Pro)的地区资料。
只要在TreeView的OnExpanding中去产生下一级的子结点就行了,还有在使用的过程中多注意一下TreeView的AddChildObject()中Data属性的用法,用好指针问题就解决了。  
回复人: Jiker(程序员是什么?) (2001-9-18 18:44:33)  得0分 
wyb_star(孤星):
  你的代码是用循环查找,没有意思。ffossil(吴下阿蒙),lengxue(冷血),hwy():
  我也有点笨,你们的方法我还是不明白。结点的深度不一定是一直增加的,并且Level属性是只读的。
  比如前面的给出的数据,当我加到"LED显示器"或"刻录光驱"时,用TTreeView.Items.AddChild(Node,'XXXX'),其中Node参数如何确定? 
回复人: tide_liu(龙飞九天) (2001-9-18 18:56:52)  得0分 
gz  
回复人: ffossil(吴下阿蒙) (2001-9-18 20:33:39)  得0分 
LEVEL肯定是连续的,由ORACLE递归算出来的(动态的),
只不过是ORACLE内部处理不用你操心而已(ORACLE 毕竟一分价钱一分货,呵呵)
其实最好设置LEVEL字段,可以减轻数据库负荷、更换数据库也方便一点“TTreeView.Items.AddChild(Node,'XXXX')”  看我伪代码啊
数据定位:用SEELECTED属性啊,然后从TEXT中分离出ID1,读出PARENTID1
同级增加  PARENTID.NEW  =PARENTID1
子级增加  PARENTID.NEW  =ID1 就这样了,如果还有问题在大富翁找我的联系方法 --- fossil  
回复人: ffossil(吴下阿蒙) (2001-9-18 20:39:14)  得0分 
刚才忘说了 如果加LEVEL字段,就完全不用递归了  
回复人: phil911(海风) (2001-9-18 20:59:40)  得0分 
:)  
回复人: Jiker(程序员是什么?) (2001-9-18 22:16:24)  得0分 
ffossil(吴下阿蒙):
我还是看不懂也。
当然这里根本不用考虑数据分级的问题,问题是现在已经有了分级的数据,如何一次循环直接就增加到TTreeView上。假设Item.Text:=<名称>;Item.Data:=<ID>.
你不要写伪码,按Pascal写一个好吗?  
回复人: Jiker(程序员是什么?) (2001-9-18 22:20:05)  得0分 
不要点击某个结点再动态装下级的结点,要一次性全部装入,因为我的数据并不多。  
回复人: ljy(ljy) (2001-9-19 11:59:17)  得0分 
如果是后台,这里有一段Ms sqlserver的例子
Parent                            Child                            ---------------------------------- ----------------------------------World                              Europe                            World                              North America                    Europe                            France                            France                            Paris                            North America                      United States                    North America                      Canada                            United States                      New York                          United States                      Washington                        New York                          New York City                    Washington                        Redmond                            This example is easier to interpret:World    North America        Canada        United States            Washington                Redmond            New York                New York City    Europe        France            Paris
--存储过程
CREATE PROCEDURE expand (@current char(20)) asSET NOCOUNT ONDECLARE @level int, @line char(20)CREATE TABLE #stack (item char(20), level int)INSERT INTO #stack VALUES (@current, 1)SELECT @level = 1  WHILE @level > 0BEGIN    IF EXISTS (SELECT * FROM #stack WHERE level = @level)        BEGIN            SELECT @current = item            FROM #stack            WHERE level = @level            SELECT @line = space(@level - 1) + @current            PRINT @line            DELETE FROM #stack            WHERE level = @level                AND item = @current            INSERT #stack                SELECT child, @level + 1                FROM hierarchy                WHERE parent = @current            IF @@ROWCOUNT > 0                SELECT @level = @level + 1        END    ELSE        SELECT @level = @level - 1END -- WHILE
下面是一段前台的写法
procedure Init;
var
tvParent: TTreeNode;
  sParent, sCurNode: String;
  iIndex: Integer;
begin
lsbColumn.ItemIndex := 0;
with qryReadData do
  begin
  Close;
    SQL.Clear;
    SQL.Add( ' Select Parent, Category from PersonCategory ' );
    Open;
  while Not Eof do
  begin
sParent := FieldByName( 'Parent' ).AsString;
    sCurNode := FieldByName( 'Category' ).AsString;
    tvParent := nil;
for iIndex := 0 to tvCategory.Items.Count - 1 do
    begin
    if tvCategory.Items[iIndex].Text = sParent then
      begin
        tvParent := tvCategory.Items[iIndex];
        Break;
      end;
    end;
    if tvParent = nil then
    begin
    tvParent := tvCategory.Items.Add( nil, sParent );
    end;
tvCategory.Items.AddChild( tvParent, sCurNode );
    Next;
  end;    for iIndex := 0 to tvCategory.Items.Count - 1 do
    begin
    if tvCategory.items[iIndex].HasChildren = True then
      begin
tvCategory.items[iIndex].ImageIndex := 0;
        tvCategory.items[iIndex].SelectedIndex := 0;
      end
      else
      begin
tvCategory.items[iIndex].ImageIndex := 1;
        tvCategory.items[iIndex].SelectedIndex := 1;
      end;
    end
  end;
end;  
 
回复人: ffossil(吴下阿蒙) (2001-9-19 14:03:06)  得0分 
function  TDMPUB.treeAdd(var Atree :TTreeView) :boolean;
var  node :array of TTreenode;
    rootnode : TTreenode;
    str0,str :string;
    Alevel :integer;
begin
    str0 :=  'select max(level)'
        + ' from epgroup start with groupupid =''-1'''
        + ' connect by groupupid = prior groupid ';    str := 'select level,groupid,groupupid,groupname,groupdesc'
        + ' from epgroup start with groupupid =''-1'''
        + ' connect by groupupid = prior groupid ';
    with qrypub do begin
        close;
        sql.Clear;
        sql.Add(str0);
        open;        if fields[0].AsInteger > 0 then
            setlength(node,fields[0].AsInteger+1)
        else
            exit;        node[0] := Atree.Items.GetFirstNode;        close;
        sql.Clear;
        sql.Add(str);
        open;
        while not eof do begin
            alevel := fieldbyname('level').asinteger;
            node[alevel] := atree.Items.AddChild(node[alevel-1],
            fieldbyname('groupid').AsString+'_'+ fieldbyname('groupname').AsString );
            next;
        end;
    result := true;
    end;
end;注意:要避免分割符写入ID(这里用下划线 '_'),,速达ERP有这个BUG的:)
 
回复人: mid_land() (2001-9-19 14:20:36)  得0分 
你这个情况,最简单的办法(确定排过序,如你举例所示,不会有父节点未建立的情况):
从第一条开始
while(记录集有效)
{
  if(上级ID的节点已建立)then
      createnode(上级ID的节点,flag子节点);
  else
      if(上级ID的节点 is NULL)then
          createnode(NULL,flag子节点);  //根节点
      else
          报错  下条记录
}
   
回复人: okook(我ok) (2001-9-19 18:39:56)  得0分 
在我的问题上面, 有详细的回复,你可以看见的。  
回复人: get10(天子脚下一根葱) (2001-9-19 18:52:37)  得0分 
如果数据量不大的话用递归是不错的选择,这代码我用得很爽,看看吧!
'====================================================================================
'描  述:根据父节点加载子节点,
'        先加载领导节点再加载单位节点,若当前加载为单位节点则递归调用加载下属节点。
'编码/修改:1  XXX        2001/08/16  创建
'====================================================================================
Public Sub AddNodeByParent(vParentNode As Node, _
                                vRecOrg As ADODB.Recordset, _
                                vRecEmp As ADODB.Recordset)
    Dim i As Integer
    Dim dicCurrentLeavelNode As New Dictionary      '''当前级节点的集合(用于递归)
    Dim nNode As Node                              '''节点
    vRecEmp.Filter = "emp_belongorg='" & vParentNode.Key & "' and emp_type='领导'"  '''属于该单位的领导
    vRecOrg.Filter = "org_belong='" & vParentNode.Key & "'"                        '''属于该单位的下属单位
    
    If vRecEmp.RecordCount > 0 Then
        For i = 1 To vRecEmp.RecordCount        '''加载领导节点
            Set nNode = mTreeView.Nodes.Add(vParentNode.Key, tvwChild, _
                        vRecEmp!emp_recid, vRecEmp!emp_name & vRecEmp!emp_title, gCNTLeader)
            vRecEmp.MoveNext
        Next
    End If
    
    If vRecOrg.RecordCount > 0 Then
        For i = 1 To vRecOrg.RecordCount        '''加载载单位节点
            Set nNode = mTreeView.Nodes.Add(vParentNode.Key, tvwChild, _
                        vRecOrg!org_recid, vRecOrg!org_simname, gCNTOrg)
            dicCurrentLeavelNode.Add i, nNode
            vRecOrg.MoveNext
        Next
    End If
    
    For i = 1 To vRecOrg.RecordCount            '''递归加载所有子节点
        AddNodeByParent dicCurrentLeavelNode.Item(i), vRecOrg, vRecEmp
    Next
    
    vRecEmp.Filter = ""
    vRecOrg.Filter = ""
    Set dicCurrentLeavelNode = Nothing
End Sub  
回复人: Jiker(程序员是什么?) (2001-9-19 22:21:40)  得0分 
get10(天子脚下一根葱):
你的代码确实有点用,但如果你改变一下数据结构(加入层次字段,并按此排序),则代码就很简单,返正我是不会用递归的。
------------------------------------------------------------------------------------------------------------
Public Sub MakeTree(tvw as Object,rsData as ADODB.RecordSet)
Dim objNode as ObjectSet objNode = tvw.Nodes.Add(, , "Root", "所有结点", 1)
objNode.Expanded = True
objNode.Selected = TrueDo While Not rsSel.EOF
If IsNull(rsData!UpID) Then
Set objNode = tvw.Nodes.Add("Root", 4, "_" & rsData!ID, rsData!Name, 1)
Else
Set objNode = tvw_s.Nodes.Add("_" & rsData!UpID, 4, "_" & rsData!Name, 1)
End If
objNode.Parent.Expanded = True
rsSel.MoveNext
Loop
End Sub
------------------------------------------------------------------------------------------------------------ffossil(吴下阿蒙):
    你的方法简直太棒了,这才我想要的,非常感谢,这下好了,不用两个表联起来查了,一次就OK。
让我再分析一下吧,前提是数据按树形顺序依次排列,当处理到某个下级结点时,
它的上级结点必已存放在Item[当前级-1]这个结点中(假设级数都从0开始)。比如有这样一组数据,
X
A
A1
A2
B
B1
B2
B21
C
共有4级,可以定义数据Node:Array [0..3] of TTreeNode,循环时各数据如下:
Node[0] Node[1] Node[2] Node[3]
X X
A A
A1 A1
A2 A2
B B
B1 B1
B2 B2
B12 B12 
C C可以看到,无论处理到哪个结点,Node[当前级-1]的内容都正好是它的父结点。  
回复人: Jiker(程序员是什么?) (2001-9-19 22:58:26)  得0分 
ffossil(吴下阿蒙):
    你的方法简直太棒了,这才我想要的,非常感谢,这下好了,不用两个表联起来查了,一次就OK。
让我再分析一下吧,前提是数据按树形顺序依次排列,当处理到某个下级结点时,
它的上级结点必已存放在Item[当前级-1]这个结点中(假设级数都从0开始)。比如有这样一组数据,
X
  A
    A1
    A2
B
    B1
    B2
      B21
C
共有4级,可以定义数据Node:Array [0..3] of TTreeNode,循环时各数据如下:
Node[0] Node[1] Node[2] Node[3]
X    X
A        A
A1                A1
A2                A2
B        B
B1                B1
B2                B2
B12                    B12 
C        C可以看到,无论处理到哪个结点,Node[当前级-1]的内容都正好是它的父结点。  
回复人: DelphiProgrammer(zx) (2001-9-19 23:39:52)  得0分 
1. 定义结构记录
  r=Record
    js:integer;      //其实此项无用
    id:String[5];
    sjid:String[5];  //因为有空值,故定义成字符串比较好 
    mc:String[20];
  end;
2. 定义记录数组
  ra=Array[0..RecordCount-1] of r;
3. 定义递归过程:
procedure TForm1.dggc(id:string;tn:TTreeNode);
var
  mynode: TTreeNode;
  i:integer;
begin
  for i:=0 to RecordCount-1 do
    begin
      if ra[i].sjid=id  then
      begin
        mynode:=dxTreeView1.Items.AddChild(tn,ra[i].mc);
        dggc(ra[i].id,mynode);
      end;
    end;
end;
3. 假定数组已赋值,RecordCount为数组长度
procedure TForm1.Button1Click(Sender: TObject);
var
  i:Integer;
  node:TTreeNode;
begin
  for i:=0 to RecordCount-1 do
    if ra[i].sjid='' then
    begin
    node:=dxTreeView1.Items.AddChild(nil,ra[i].id);
    dggc(ra[i].id,node);
    end;
end;
完成啦!!!  

解决方案 »

  1.   

    To:zswang(伴水)(需要充充电)
    多谢你!
    不好意思!我是新手加菜鸟对上面的的程序有好多都看不懂,所以才希望先做个简单一点的.
    如果分不够的话,我一定再加.
      

  2.   

    function StrLeft(const mStr: string; mDelimiter: string): string;
    begin
      Result := Copy(mStr, 1, Pos(mDelimiter, mStr) - 1);
    end; { StrLeft }function StrRight(const mStr: string; mDelimiter: string): string;
    begin
      if Pos(mDelimiter, mStr) <= 0 then
        Result := ''
      else Result := Copy(mStr, Pos(mDelimiter, mStr) + Length(mDelimiter), MaxInt);
    end; { StrRight }procedure TForm1.Button1Click(Sender: TObject);
    var
      I: Integer;
      vTreeNode: TTreeNode;
    begin
      Table1.First;
      TreeView1.Items.Clear;  while not Table1.Eof do begin
        vTreeNode := nil;
        for I := 0 to TreeView1.Items.Count - 1 do begin
          if (Pos(StrLeft(TreeView1.Items[I].Text, #32),
            Table1.FieldByName('号码').AsString) = 1) then begin
            vTreeNode := TreeView1.Items[I];
          end;
        end;
        TreeView1.Items.AddChild(vTreeNode,
          Table1.FieldByName('号码').AsString + #32 +
          Table1.FieldByName('名称').AsString);
        Table1.Next;
      end;  ///////Begin 排除编号
      for I := 0 to TreeView1.Items.Count - 1 do
        TreeView1.Items[I].Text := StrRight(TreeView1.Items[I].Text, #32);
      ///////End 排除编号
    end;
      

  3.   

    [建议.1]
    使用Query1通过排序后才比较安全
    select 号码,名称
    from TreeTable
    order by 号码[建议.2]
    其实直接存取树的文本更方便一些
      TreeView1.SaveToStream();
      TreeView1.LoadFromFile();
    也有DBTreeView控件
      

  4.   

    TO:zswang(伴水)(需要充充电) 
    多谢你的回答!上面的程序中
    function StrLeft(const mStr: string; mDelimiter: string): string;
    begin
      Result := Copy(mStr, 1, Pos(mDelimiter, mStr) - 1);
    end; { StrLeft }function StrRight(const mStr: string; mDelimiter: string): string;
    begin
      if Pos(mDelimiter, mStr) <= 0 then
        Result := ''
      else Result := Copy(mStr, Pos(mDelimiter, mStr) + Length(mDelimiter), MaxInt);
    end; { StrRight }
    表示什么意思呀!我不懂,能讲解一下吗!
      

  5.   

    //我自己写了个控件请参考
    http://www.csdn.net/Expert/topic/492/492794.shtmStrLeft('[email protected]', '@') = 'wjhu111'
    StrRight('[email protected]', '@') = '21cn.com'StrLeft('[email protected]', '.') = 'wjhu111@21cn'
    StrRight('[email protected]', '.') = 'com'返回左右分隔字符串
      

  6.   

    //屏蔽掉你就可看出来了  ///////Begin 排除编号
    //  for I := 0 to TreeView1.Items.Count - 1 do
    //    TreeView1.Items[I].Text := StrRight(TreeView1.Items[I].Text, #32);
      ///////End 排除编号
      

  7.   

    to:zswang(伴水)(需要充充电)
     StrLeft和StrRight的意思我明白了,但
    Result := Copy(mStr, 1, Pos(mDelimiter, mStr) - 1);是取出什么东西呀!
      

  8.   

    是的,你自己做做试验吧
    Caption := Copy('1234.56', Pos('.', '1234.56') - 1)
      

  9.   

    strleft和strright的意思我明白了,我想再问一下,strleft和strright是不是取表中的字段号码和名称的值的,如果是的话,那字段有3个是不是这种方法就没有用了,那位高手或者知道的,也帮我解答一下,小弟在这不胜感激了.
      

  10.   

    to :zswang(伴水)(需要充充电)
    昨天你说     #32 = Chr(32) = ' ' = 空格 
    我把   if (Pos(StrLeft(TreeView1.Items[I].Text, #32),
    中的#32改成,' '或者#31,树形的排列都变成同一级别了
           公司名字
           公司部门
           公司业务
           开
    这个样子!
    在TreeView1.Items[I].Text := StrRight(TreeView1.Items[I].Text, #32);中
    #32改成,' '或者#31树形条是有,节点也对,就是没有字显示了,这是不是说#32是一个固定的参数呀!