看看Visual Foxpro的solution ActiveX控件示例。

解决方案 »

  1.   

    你在treeview的onexpand事件中进行处理其下一层的展开
      

  2.   

    看来Delphi无法方便地实现了,不是要遍历树,就是要对Query作Filter处理。
    VB可以很方便地实现,它的集合有字符串型的关键字。这是多么重要的一个功能。
      

  3.   

    我的方法挺土的
    先给treeview1.items[n].data:=pointer(唯一的标记:integer)
    当然需要用到一个链表
    它用来保存标题信息的
    然后存为文件
    数据库中记录内容
    使用唯一的标记同树相对应下回装入时只须装入树结构恢复以前的唯一标记
    只有点击树叶时才利用标记查找相应的内容可能叙述不太清楚,我有源程要的话Mailto:[email protected]
      

  4.   

    这么麻烦呀,还要用链表,文件。不如用个结构数组来保存对应关系(比操作Items,ADOQuery更快)。可能以前用VB习惯了,我现在对Delphi自称数据库方面的快速开发产生了很大的怀疑。
      

  5.   

    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
      

  6.   

    不知是否可以这样解决:
    1.在FormShow事件中,添加上级id=null的节点.
    2.在TreeView1Expanding事件中,根据所选取得node判断是否有children,如果就动态添加好了。(注:这种方法的效率较高,尤其在树结构较为复杂的情形)。
    有问题:[email protected]
      

  7.   

    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列就不支持了,因此还得用其它办法。就是上面各位所说的递归,我还没弄明白?
      

  8.   

    ffossil(吴下阿蒙):
      你的代码什么意思,我看不大懂,可以指点一下吗(不是指语法)?比某个结点小一级的结点不一定就是它的父结点呀?
      

  9.   

    taohongjun(安意):
      可以说说你的递归方法吗?
      

  10.   

    “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
      

  11.   

    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. 
      

  12.   

    生成treeview时,首先生成父ID为null的子节点,再生成孙节点,......
      

  13.   

    这个问题没那么复杂
    我刚刚做了财务软件的科目的ttreeview 结构
    刚开始我也用了递归啊什么的算法
    实际上,根本就很简单
    只要建立一个代码字段(字符串),并且排序
    把字段读出来就可以了!
    如:
    100
    100001
    100002
    100002001

     读出字段后
    判断字段的长度是3就是一级节点,是六就是二级。
    呵呵,只要读一次数据库就可以了
           
      

  14.   

    lengxue的方法是最简单的,我也用过了,以前有用递归,实际上递归并不可取,(能不用则不用),会增加系统资源的负担。
      

  15.   

    参考速达软件(SupperData 2000+ Pro)的地区资料。
    只要在TreeView的OnExpanding中去产生下一级的子结点就行了,还有在使用的过程中多注意一下TreeView的AddChildObject()中Data属性的用法,用好指针问题就解决了。
      

  16.   

    wyb_star(孤星):
      你的代码是用循环查找,没有意思。ffossil(吴下阿蒙),lengxue(冷血),hwy():
      我也有点笨,你们的方法我还是不明白。结点的深度不一定是一直增加的,并且Level属性是只读的。
      比如前面的给出的数据,当我加到"LED显示器"或"刻录光驱"时,用TTreeView.Items.AddChild(Node,'XXXX'),其中Node参数如何确定?
      

  17.   

    LEVEL肯定是连续的,由ORACLE递归算出来的(动态的),
    只不过是ORACLE内部处理不用你操心而已(ORACLE 毕竟一分价钱一分货,呵呵)
    其实最好设置LEVEL字段,可以减轻数据库负荷、更换数据库也方便一点“TTreeView.Items.AddChild(Node,'XXXX')”   看我伪代码啊
    数据定位:用SEELECTED属性啊,然后从TEXT中分离出ID1,读出PARENTID1
    同级增加   PARENTID.NEW  =PARENTID1
    子级增加   PARENTID.NEW  =ID1 就这样了,如果还有问题在大富翁找我的联系方法 --- fossil
      

  18.   

    刚才忘说了 如果加LEVEL字段,就完全不用递归了
      

  19.   

    ffossil(吴下阿蒙):
    我还是看不懂也。
    当然这里根本不用考虑数据分级的问题,问题是现在已经有了分级的数据,如何一次循环直接就增加到TTreeView上。假设Item.Text:=<名称>;Item.Data:=<ID>.
    你不要写伪码,按Pascal写一个好吗?
      

  20.   

    如果是后台,这里有一段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;  
      

  21.   

    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的:)
      

  22.   

    你这个情况,最简单的办法(确定排过序,如你举例所示,不会有父节点未建立的情况):
    从第一条开始
    while(记录集有效)
    {
       if(上级ID的节点已建立)then
          createnode(上级ID的节点,flag子节点);
       else
          if(上级ID的节点 is NULL)then
              createnode(NULL,flag子节点);  //根节点
          else
              报错   下条记录
    }
       
      

  23.   

    如果数据量不大的话用递归是不错的选择,这代码我用得很爽,看看吧!
    '====================================================================================
    '描   述:根据父节点加载子节点,
    '        先加载领导节点再加载单位节点,若当前加载为单位节点则递归调用加载下属节点。
    '编码/修改: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
      

  24.   

    get10(天子脚下一根葱):
    你的代码确实有点用,但如果你改变一下数据结构(加入层次字段,并按此排序),则代码就很简单,返正我是不会用递归的。
    ------------------------------------------------------------------------------------------------------------
    Public Sub MakeTree(tvw as Object,rsData as ADODB.RecordSet)
    Dim objNode as Object Set objNode = tvw.Nodes.Add(, , "Root", "所有结点", 1)
    objNode.Expanded = True
    objNode.Selected = True Do 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]的内容都正好是它的父结点。
      

  25.   

    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]的内容都正好是它的父结点。 
      

  26.   

    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;
    完成啦!!!