我的表为
--------------------
pid | id | name
0 | 001 | 原辅料
001 | 002 | 布料
001 | 003 | 辅料
002 | 004 | 纯棉
002 | 005 | TC布
003 | 006 | 扣子
003 | 007 | 绣线
--------------------
function TF_FenLei.AddChildNode(node: TTreeNode; typecode: string; stype: string): TTreeNode;
var i:integer;
begin
Result := TreeView1.Items.AddChild(node, stype); with adoquery1 do begin
Close;
SQL.Clear;
SQL.Text := 'SELECT * FROM B_Lei WHERE pid='+typecode;
Open;
while not eof do begin
TreeView1.Items.AddChild(result,FieldByName('Name').AsString);
//AddChildNode(Result,FieldByName('id').AsString,FieldByName('Name').AsString);
next;
end;
end;
end;procedure TF_FenLei.FormCreate(Sender: TObject);
begin
AddChildNode(nil,'001','原辅料');
end;想得到
原辅料
|--布料
| |--纯棉
| |--TC布
|--辅料
|--扣子
|--绣线但得到的却是
原辅料
|--布料
| |--纯棉请各位改一下,或给我一个更好的方法生成数据树,谢谢!!!!
--------------------
pid | id | name
0 | 001 | 原辅料
001 | 002 | 布料
001 | 003 | 辅料
002 | 004 | 纯棉
002 | 005 | TC布
003 | 006 | 扣子
003 | 007 | 绣线
--------------------
function TF_FenLei.AddChildNode(node: TTreeNode; typecode: string; stype: string): TTreeNode;
var i:integer;
begin
Result := TreeView1.Items.AddChild(node, stype); with adoquery1 do begin
Close;
SQL.Clear;
SQL.Text := 'SELECT * FROM B_Lei WHERE pid='+typecode;
Open;
while not eof do begin
TreeView1.Items.AddChild(result,FieldByName('Name').AsString);
//AddChildNode(Result,FieldByName('id').AsString,FieldByName('Name').AsString);
next;
end;
end;
end;procedure TF_FenLei.FormCreate(Sender: TObject);
begin
AddChildNode(nil,'001','原辅料');
end;想得到
原辅料
|--布料
| |--纯棉
| |--TC布
|--辅料
|--扣子
|--绣线但得到的却是
原辅料
|--布料
| |--纯棉请各位改一下,或给我一个更好的方法生成数据树,谢谢!!!!
解决方案 »
- 关于线程的基础代码
- 如何实现两个Listbox同步.高分悬赏
- 请问在DELPHI中用什么可以设计以下的 多行 铅笔、钢笔、及数量及金额来让用户输入?
- 关于SQL语句JOIN语法的问题???????????????????????????????????
- 该用什么控件?(绝食等待中...)
- 各位高手!监控键盘可以不用Dll就能做全局钩子。不知道监控鼠标能不能也不放在Dll里面!
- 用table进行查询,怎么样让它更快???
- 怎样实现这种功能?
- delphi有没有对日期进行减价运算的函数?
- delphi中的窗体问题。
- 动态创建表----大家帮忙呀!谢谢!
- 如果把Access数据库中的某个数据表结构和数据复制到另个Access数据库中?
procedure AddClass(AId: string; FatherNode: TTreeNode; temp_treeview: Ttreeview);
var
QryTmp: TADOQuery;
myNode: TTreeNode;
myLabel: TLabel;
begin
QryTmp := TADOQuery.Create(application);
qrytmp.connection := form1.ADOConnection1;
QryTmp.SQL.Add('select * from pro_bom');
QryTmp.SQL.Add('where cp_peij_no=:AId');
qrytmp.Parameters.ParamByName('aid').value := aid;
QryTmp.Open;
while not QryTmp.Eof do
begin
myNode := temp_Treeview.Items.AddChild(FatherNode, QryTmp.fieldbyname('wl_peij_no').AsString);
//创建标签,caption存放各分支的AutoId表识
myLabel := TLabel.Create(application);
myLabel.Visible := false;
myLabel.Caption := QryTmp.fieldbyname('wl_peij_no').AsString;
myNode.Data := myLabel;
AddClass(QryTmp.fieldbyname('wl_peij_no').Asstring, myNode, temp_treeview); //递归用过程
QryTmp.Next;
end;
QryTmp.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
AddClass('all',nil,bosstreeview1);
end;
ID(编号)Name(名称) PID(父类编号)
1 日用品 0
2 维护品 0
3 电气产品 1
4 洗漱用品 1
5 登高品 2
6 电气品 2
7 牙膏 4要生成一个日用品
电气产品
洗漱用品
牙膏
维护品
登高品
电气品这里一般大家会用TreeView,至于TreeView的一般用法,这里就不多说了
最常见的方法就是递归了,procedure ParaseCls(_TV: TTreeView;NodeID:Integer;ParentNode:TTreeNode);
var
LocalQuery:TAdoQuery;
TN:TTreeNode;
PNI:PNodeInfo;
begin
LocalQuery:=TAdoQuery.Create(nil);
LocalQuery.Connection:=ADOConnection1;
LocalQuery.SQL.Add('SELECT * FROM CLASS WHERE PID='+IntToStr(NodeID));
LocalQuery.Open;
While Not LocalQuery.Eof do
begin
Application.ProcessMessages;
TN:=_TV.Items.AddChild(ParentNode,LocalQuery.Fields[1].AsString);
ParaseCls(_TV,LocalQuery.FieldByName('ID').AsInteger,TN);
LocalQuery.Next;
end;
LocalQuery.Free;
end;
针对上面的,可以这样调用
ParaseCls(TreeView1,0,nil);
简单方便,但是速度比较慢,频繁访问数据库,如果记录数多的话,就是一场噩梦有没有其它方法?有位PlainSong有如下见解“不要用递归,这样会多次访问数据库,加重数据服务器的负担(用存储过程同理)。
一条SQL把条例要求的数据全读出来,然后:
“对每一条数据处理,如果ParentID为NULL则加入到树的根结点下并把它的ID和相应的树结点联系在一起放到一个查找表中并从待处理数据中删除。如果ParentID不为空则在查找表中用ParentID查找树结点,如找到则加入为子结点并把自己也加入查找表并从待处理数据中删除。
“重复以上处理直到待处理数据为空或某次迭代无数据加入查找表(这时有非法数据,把它们抛弃不要)。
“这样处理不一定比用递归快,但减清了数据服务的负担,当用多个应用服务访问一个数据服务器时会明显提高效率。”
思路很好,可惜这位老兄只说了这么一段就不见了,他的具体实现未能得知。本来的根据这段话是这样想的
1、取出数据集
2、将ID、PID放到一个List之类的东西中,这个List对应于PlainSong说的待处理数据
3、根据PlainSong 的办法反复遍历这个List,直到List被一条一条删除,或者有一次的遍历没有合适的记录好办法,实现起来不难,速度估计也不错
有没有再好一点的,以下是我的办法,思路是这样的
1、取出数据集
2、将数据集中所有记录均以根节点的方式加到TreeView中去
3、遍历整个TreeView,将每一个节点移到适当的位置
针对上面的结构实现方法是这样的type
PNodeInfo= ^TNodeInfo;
TNodeInfo = record
NodeID:Integer;
PNodeID:Integer;
end;
//先定义一个结构,记录每一个节点的信息
//你可以扩充这个结构,使它更适用//下面是实现的函数
//其中TVClass是我的TreeView;
//ADOQuery1的SQL是SELECT * From Class
procedure ParaCls;
var
PNI:PNodeInfo;
I,J:Integer;
begin
LockWindowUpdate(Self.Handle);
TVClass.Items.BeginUpdate; for I:=0 to TVClass.Items.Count-1 do
begin
if TVClass.Items.Data<>nil then
Dispose(PNodeInfo(TVClass.Items.Data));
end;//如果已经有了,就清了它 TVClass.Items.Clear;
ADOQuery1.Open;
ADOQuery1.First;
while not ADOQuery1.Eof do
begin
New(PNI);
PNI.NodeID:=ADOQuery1.FieldByName('CLSID').AsInteger;
PNI.PNodeID:=ADOQuery1.FieldByName('ParentID').AsInteger;
TVClass.Items.AddObject(nil,ADOQuery1.FieldByName('Name').AsString,PNI);
ADOQuery1.Next;
end; TVClass.Items.EndUpdate;
TVClass.Items.BeginUpdate;
//这里我刷了一更新了一次,因为我发现如果没有上面这两句
//常会出现TreeView显示不正常,难道是D的BUG,
//上午同DeathCat说的就是这个问题 I:=0;
while I<=TVClass.Items.Count-1 do
begin
if PNodeInfo(TVClass.Items.Data).PNodeID<>0 then
//这里父节为0的代表就是要节点
begin
for J:=0 to TVClass.Items.Count-1 do
begin
if PNodeInfo(TVClass.Items[J].Data).NodeID=PNodeInfo(TVClass.Items.Data).PNodeID then
begin
if I<>J then
TVClass.Items.MoveTo(TVClass.Items[j],naAddChild);
if I<J then Dec(I);
//当前节点要是向上移没什么问题,向下移的话就要处理一下了
break;
end;
end;
end;
I:=I+1;
end;
TVClass.Items.EndUpdate;
LockWindowUpdate(0);
end;
这中间用到了TTreeNode.Data,对每一个结点生成了一个结构指针,所以在退出时要释放了
用这样的句子
for I:=0 to TVClass.Items.Count-1 do
begin
if TVClass.Items.Data<>nil then
Dispose(PNodeInfo(TVClass.Items.Data));
end;
Data值真要好好利用,试想一个单纯的树形结构有什么用处,包含丰富信息的树(Data)才是有用的树,节点的增加删除修改(要同数据库联动哟)如果充分利用Data则会是很方便的测试了一下,当总记录数在50条以内时,以上方法速度快于递归,但当数据多于100条时,递归方法的速度则远高于上述方法,所以如果记录数不多可行,多了的话还是用递归为好