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:[email protected][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:[email protected][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.
---------------------
作者:甜而不腻
来源:CSDN
原文:https://blog.csdn.net/ingener/article/details/78363562
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/tc310/p/10080786.html

时间: 2024-09-13 12:19:21

delphi TreeView 从数据库添加节点的四种方法的相关文章

jquery 添加节点的几种方法介绍

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>jQuery插入,复制.替换和删除节点</title> <script type="text/javascript" src="jquery-1.3.2.js"></scri

Jmeter添加变量的四种方法

一.在样本中添加同请求一起发送的参数.根据服务器设置的数据类型,来添加不同类型的参数 二.用户定义的变量 1.创建:添加->配置元件->用户定义的变量 2.作用:当前的线程组内所有Sampler都可以引用变量,方便脚本更新:当参数发生变化时,只要在[用户定义的变量]中更新对应变量的参数即可,不需要逐个修改每个http中的参数 3.变量定义:可以是具体的值,也可以是函数(函数可自动生成:选项->函数助手对话框) 4.变量引用:Sample中引用变量的格式为${变量名} 三.函数助手对话框-

数据库两表连接四种方法

一般我们连接两张表时,都是select xxx,xx from x1,x2 where x1.a=x2.a.其实数据库还有4中join操作. 例如:表1: student s_id name sex 1  张三  男 2  李四  女 4  王五  男 表2: class c_id  id  name 1 1 11111 2 2 22222 3 3 33333 4 2 44444 1.连接查询 select s.name s_name,s.sex s_sex,c.name c_name from

delphi TreeView修改选中的节点的颜色和背景

TreeView修改选中的节点的颜色和背景 TCustomDrawTarget = (dtControl, dtItem, dtSubItem); TCustomDrawStage = (cdPrePaint, cdPostPaint, cdPreErase, cdPostErase); TCustomDrawState = set of (cdsSelected, cdsGrayed, cdsDisabled, cdsChecked, cdsFocused, cdsDefault, cdsHo

oracle数据库的停止(四种模式)

数据库的停止(四种模式)1.正常停库:shutdown normal = shutdown普通用户的连接不允许建立等待查询结束等待事务结束产生检查点(数据同步)关闭联机日志和数据文件关闭控制文件关闭实例 2.事务级停库: shutdown transactional普通用户的连接不允许建立查询被终止等待事务结束产生检查点(数据同步)关闭联机日志和数据文件关闭控制文件关闭实例 3.立即停库: shutdown immediate (生产库最常用的停库方式)普通用户的连接不允许建立查询被终止事务被回

C#连接Oracle数据库的四种方法

C#连接数据库的四种方法 在进行以下连接数据库之前,请先在本地安装好Oracle Client,同时本次测试System.Data的版本为:2.0.0.0. 在安装Oracle Client上请注意,如果OS是3-BIT请安装32-BIT的Oracle Client,但OS是64-BIT,需要根据实际情况安装,如果你的APP是按32-BIT运行的,则需要安装32-BIT客户端,如果是按64位运行的,则安装64位客户端,而不是OS是64-BIT的,就要安装64-BIT的ORACLE客户端.   本

Java解析XML文件的四种方法

[摘要] 可扩展标志语言(XML)在实现信息标准化.信息的交流与共享上有其独特的技术优势,因此受到了广泛的重视.本文先简单的介绍了XML基本知识,然后从XML应用入手总结了四种现今最常见的XML的解析方法,介绍了这四种方法的特点,其中包括优点与不足之处.最后给出了一个简单的案例来对这四种解析进行代码介绍. [关键字] XML文件,DOM,SAX,JDOM,DOM4J [引言] XML即可扩展标记语言(EXtensible Markup Language),是标准通用标记语言的子集,是一种用于标记

Spring Security3的四种方法概述

使用Spring Security3的四种方法概述 那么在Spring Security3的使用中,有4种方法: 一种是全部利用配置文件,将用户.权限.资源(url)硬编码在xml文件中,已经实现过,并经过验证: 二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置,目前这种方式已经实现,并经过验证. 三种是细分角色和权限,并将用户.角色.权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器,     并分别实现Ac

控件绘制的四种方法

OWNER?DRAW实现自绘按钮 一准备工作 在您决定开发 Windows 提供的常规免费自定义控件范围之外的控件之后,您必需确定自己的控件将有多少独到之处 - 在功能和外观两方面.例如,我们假定您正在创建一个类似于计速表的控件.由于公共控件库 (ComCtrl32.dll) 中没有类似的控件,您完全需要自己进行以下操作:编写所有控件功能需要的代码,进行绘制,默认终端用户的交互,以及控件与其父窗口之间需要的任意消息处理. (#add 两方面,公共控件库中没有类似的 完全重写;? 只想调整公共控件