zoukankan      html  css  js  c++  java
  • delphi TreeView 从数据库添加节点的四种方法

    方法一:delphi中递归算法构建treeView

    过程:
    通过读取数据库中table1的数据,来构建一颗树。table1有两个字段:ID,preID,即当前结点标志和父结点标志。所以整个树的表示为父母表示法。本递归算法不难写,但是要注意:程序内部的变量都应使用局部变量!比如当Query是外部变量(函数外定义或者直接通过控件拖拽得来)时就会得到错误的结果。代码如下:


    unit Unit1;

    interface

    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, DB, ADODB, ComCtrls, StdCtrls;

    type
      TForm1 = class(TForm)
        Button1: TButton;
        tree: TTreeView;
        Query1: TADOQuery;
        procedure Button1Click(Sender: TObject);
        procedure CreateTree;
      private
        { Private declarations }
      public
        { Public declarations }
      end;
      PTNodeInfo=^TNodeInfo;
      TNodeInfo=record
       id,preId:string;
       end;

    var
      Form1: TForm1;

    implementation

    {$R *.dfm}
    procedure TForm1.CreateTree;

      procedure CreateTree(Pre:string;preNode:TTreeNode);
      var
        pInfo:PTNodeInfo;
        node:TTreeNode;
        sql:string;
        Query:TADOQUery;
      begin
        Query:=TADOQuery.Create(nil);
        Query.ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:/实验/递归父母表示/新建 Microsoft Office Access 应用程序.mdb;Persist Security Info=False';
        sql:= Format('select * from table1 where preId=%s', [QuotedStr(pre)]);
        Query.Close;
        Query.SQL.Clear;
        Query.SQL.Add(sql);
        Query.Open;
        Query.First;
        while not Query.Eof do
        begin
          new(pInfo);
          pInfo.id:=Query.Fields[0].AsString;
          pInfo.preId:=Query.Fields[1].AsString;
          node:=tree.Items.AddChild(preNode,pInfo.id);
          node.Data:=pInfo;
          CreateTree(pInfo.id,node);
          Query.Next;
        end;
        Query.Close;
        Query.Free;
      end;
    begin
      createTree('000',nil);
      tree.FullExpand;
    end;
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      createtree;
    end;

    end.

    createTree函数也可以这么写:
    procedure TForm1.CreateTree;
    var
      p:pTNodeInfo;
      procedure CreateTree(Pre:PTNodeInfo;preNode:TTreeNode);
      var
        pInfo:pTNodeInfo;
        node:TTreeNode;
        sql:string;
        Query:TADOQuery;
      begin
        Query:=TADOQuery.Create(nil);
        Query.ConnectionString:='Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:/实验/递归父母表示/新建 Microsoft Office Access 应用程序.mdb;Persist Security Info=False';
        sql:= 'select * from table1';
        Query.Close;
        Query.SQL.Clear;
        Query.SQL.Add(sql);
        Query.Open;
        Query.First;
        while not Query.Eof do
        begin
          new(pInfo);
          pInfo.id:=Query.Fields[0].AsString;
          pInfo.preId:=Query.Fields[1].AsString;
          if pInfo.preId=pre.id then
          begin
            node:=tree.Items.AddChild(preNode,pInfo.id);
            node.Data:=pInfo;
            CreateTree(pInfo,node);
          end;
          Query.Next;
        end;
      end;
    begin
      new(p);
      p.id:='000';
      createTree(p,nil);
      tree.FullExpand;
    end;


    方法二://数据采用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; 

    方法三: 此方法是第一种方法的变种,凡是有已经添加节点的,删除。

    在数据库中建一张表,包含:NodeName,NodeId,ParentId 3个字段,具体什么含义,一看就知了。

    过程如下:

    procedure TFRM_channel.formtreenode(parentid:string;TreeView1: TTreeView;parentnode:TTreeNode;ADOQuery1:TADOQuery);
    var
        i:integer;
        treenode:TTreeNode;
    begin
        i:=0;
        ADOQuery1.First;
        while not ADOQuery1.Eof do
          begin
             if ADOQuery1.FieldByName('ParentId').AsString=parentid then
               begin
                  treenode:=TreeView1.Items.AddChild(parentnode,ADOQuery1.fieldbyname('NodeName').AsString);
                  formtreenode(ADOQuery1.fieldbyname('NodeId').AsString ,TreeView1,treenode,ADOQuery1);
               end;
              inc(i);
              ADOQuery1.First;
              ADOQuery1.MoveBy(i);
          end;


    end;

    在调用如下,如在窗体的create事件中:

    procedure TFRM_channel.FormCreate(Sender: TObject);
    var
      TreeNode1:TTreeNode;
      i:integer;
      nodename:string;
      nodeid:string;
      parentid:string;
      cmdstr:string;

    begin
    //初始化树形节点
       ADOQuery1.ConnectionString:=mypublic.datastr;

       cmdstr:='select * from ChanTreeNode order by ParentId ,NodeId asc';
       ADOQuery1.Close;
       ADOQuery1.SQL.Clear;
       ADOQuery1.SQL.Add(cmdstr);
       ADOQuery1.Open;

           TreeNode1:=nil;
         formtreenode('000',TreeView1,TreeNode1,ADOQuery1);
    end;

    方法四:用一个排序的TStringList列表,通过排序列表采用二分查找的快速性能,就能够很快地查找到当前要添加节点的父节点,从而插入到 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.

  • 相关阅读:
    毕设2020.02.02
    架构师眼中的高并发架构
    云时代架构读后感二
    以《淘宝网》为例,描述质量属性六个常见场景
    《架构漫谈》读后感
    云时代架构读后感一
    暑假周总结报告08
    暑假周总结报告07
    暑假周总结报告06
    假期周总结报告05
  • 原文地址:https://www.cnblogs.com/jijm123/p/12305901.html
Copyright © 2011-2022 走看看