方法一: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.
---------------------
作者:甜而不腻
来源:CSDN
原文:https://blog.csdn.net/ingener/article/details/78363562
版权声明:本文为博主原创文章,转载请附上博文链接!
delphi TreeView 从数据库添加节点的四种方法的更多相关文章
-
react添加样式的四种方法
React给添加元素增加样式 第一种方法: <!DOCTYPE html> <html lang="en"> <head> <meta c ...
-
jquery 添加节点的几种方法介绍
<html> <head> <meta http-equiv="Content-Type" content="text/html; char ...
-
引:Jmeter添加变量的四种方法
一.在样本中添加同请求一起发送的参数.根据服务器设置的数据类型,来添加不同类型的参数 二.用户定义的变量 1.创建:添加->配置元件->用户定义的变量 2.作用:当前的线程组内所有Samp ...
-
Jmeter添加变量的四种方法
一.在样本中添加同请求一起发送的参数.根据服务器设置的数据类型,来添加不同类型的参数 二.用户定义的变量 1.创建:添加->配置元件->用户定义的变量 2.作用:当前的线程组内所有Samp ...
-
Delphi中弹出提示框的四种方法
参考:http://blog.itpub.net/8432156/viewspace-924843/ 更为详细的内容请参见:http://blog.csdn.net/akof1314/article/ ...
-
数据库事务 ACID属性、数据库并发问题和四种隔离级别
数据库事务 ACID属性.数据库并发问题和四种隔离级别 数据库事务 数据库事务是一组逻辑操作单元,使数据从一种状态变换到另一种状态 一组逻辑操作单元:一个或多个DML操作 事务处理原则 保证所有事务都 ...
-
C#连接Oracle数据库的四种方法
C#连接数据库的四种方法 在进行以下连接数据库之前,请先在本地安装好Oracle Client,同时本次测试System.Data的版本为:2.0.0.0. 在安装Oracle Client上请注意, ...
-
解析Xml四种方法
关键字:Java解析xml.解析xml四种方法.DOM.SAX.JDOM.DOM4j.XPath [引言] 目前在Java中用于解析XML的技术很多,主流的有DOM.SAX.JDOM.DOM4j,下文 ...
-
mysql insert插入时实现如果数据表中主键重复则更新,没有重复则插入的四种方法
[CSDN下载] Powerdesigner 设计主键code不能重复等问题 [CSDN博客] Oracle中用一个序列给两个表创建主键自增功能的后果 [CSDN博客] MySQL自增主键删除后重复问 ...
随机推荐
-
SQL SERVER四舍五入你除了用ROUND还有其他方法吗?
引言 今天和测试沟通一个百分比计算方式时遇到一个问题, 我在存储过程里用到了强转CAST(32.678 AS DECIMAL(5,1)) 我认为该方式只会保留一位小数,我给测试的回复是我并没有用到四 ...
-
bzoj4238 电压
首先先直接对图进行二染色,dfs染完色后,有的边为搜索树边,有的为非树边,当非树边连接的两头的点为异色的时候,那么很明显这条非树边和树边构成的环上的边必然不可能成为答案:如果非树边的两端的点同色,那么 ...
-
Time vs Story Points Estimation [转]
One of the most common questions we get is whether to estimate in time or points. It seems like poin ...
-
Mondriaan&#39;s Dream - POJ 2411(状态压缩)
题目大意:有一些1*2的矩形,现在用这些小矩形覆盖M*N的大矩形,不能重复覆盖,并且要覆盖完全,求有多少种覆盖方式. 分析:可以使用1和0两种状态来表示这个位置有没有放置,1表示放置,0表示没有放置, ...
-
web开发与设计--js数据类型,js运营商
1. js数据类型划分:号码值类型,布尔,串 由typeof能够看到什么类型的数据被详述. 举例: <span style="font-family:Microsoft YaHei;f ...
-
【Maven】构建war包时排除web.xml
在使用maven构建项目的war包时,有时并不需要src/webapp/WEB-INF/下的一些文件. 这时可以通过maven-war-plugin创建配置来排除这些文件.下面贴出我平时使用的pom. ...
-
Thymeleaf入门基础
一.简介 1.thymeleaf优点 ①是一个支持html原型的自然引擎,它在html标签增加额外的属性来达到模板+数据的展示方式,由于浏览器解释html时,忽略未定义的标签属性,因此thymelea ...
-
vue-cli搭建项目的目录结构及说明
vue-cli基于webpack搭建项目的目录结构 build文件夹 ├── build // 项目构建的(webpack)相关代码 │ ├── build.js ...
-
----关于position的四个标签----
从[ two1-4 ]分别为absolute,fixed,static,relative标签 四个标签下位移值相同,[ two2 ]和[ two1 ]都出现在左上角,[ two2 ] 盖住了[ two ...
-
Android UI布局之LinearLayout
LinearLayout是Android中最经常使用的布局之中的一个.它将自己包括的子元素依照一个方向进行排列.方向有两种,水平或者竖直.这个方向能够通过设置android:orientation=& ...