先把表 添加到treeview中 然后判断点击的 节点 做相应处理引passos(古月春秋) :表结构可以采用两种方式: 1、编码方式,采用001001001的字符串,然后按照每级的长度切开,然后建树。这种方法最慢,而且树的层次数有限。 2、链式存储方式。结构是 节点编号、父节点编号、名称、编码... 这种方法是最好的解决方法。我详细说一下: 关键是两个编号字段,顶级接点的编号可以是-1或者其他特殊值(字段类型用数字或者字符串都可以)。比如树形是 A----B |--C |--D----E |--F那么数据库内容是: 节点编号 父节点编号 名称 1 -1 A 2 1 B 3 1 C 4 1 D 5 4 E 6 4 F 建立树的操作可以使用一个先根顺序建立各个节点(实际上是一个递归函数,广度优先),算法如下://建立一个记录,存储树节点的信息 PNodeData = ^TNodeData; TNodeData = record ID, ParentID: LongInt; Text: ShortString; end;//ParentTreeNode TreeView父节点 //ParentID 父节点编号 //TreeDataSet 存储该树的数据表、之前要打开 procedure BuildTree(ParentNode: TTreeNode; ParentID: Integer); var I: Integer; ANodeData: PNodeData; NewNode: TTreeNode; begin //过滤出该父节点下的子节点 TreeDataSet.Filter := Format('父节点编号 = %d', [ParentID]); TreeDataSet.Filtered := True;
for I := 0 to TreeDataSet.RecordCount - 1 do begin ANodeData := New(PNodeData); ANodeData^.ID := TreeDataSet.FieldValues['节点编号']; ANodeData^.ParentID := TreeDataSet.FieldValues['父节点编号']; ANodeData^.Text := TreeDataSet.FieldValues['名称']; NewNode := TreeView.AddChild(ParentNode, ANodeData^.Text); NewNode.Data := ANodeData; Next; end; //递归调用本函数,为各个子节点生成树 //很明显,如果当前节点没有子节点,那么就是递归调用的结束条件了 //这里的判断是检查第一次调用传进来的参数,如果是nil就用TreeView的属性来访问 if Assigned(ParentNode) then begin for I := 0 to ParentNode.Count - 1 do BuildTree(ParentNode.Item[I], PNodeData(ParentNode.Item[I].Data)^.ID); else begin for I := 0 to TreeView.Items.Count - 1 do BuildTree(TreeView.Items[I], PNodeData(TreeView.Items[I].Data)^.ID); end; end; 初始的调用方法是 TreeDataSet.Open;BuildTree(TreeView.Add(nil, '树根'), -1);//或者 BuildTree(nil, -1);TreeDataSet.Close; TreeDataSet.Filter := ''; TreeDataSet.Filtered := False; 上面那段代码在程序退出的时候要记得释放每个树节点的data指针占用的内存,DELPHI不会负责释放那些空间的。如果不做,将导致内存泄露。 再多加一点注释: //把数据库中查询出来的每个节点加入到ParentNode下 for I := 0 to TreeDataSet.RecordCount - 1 do begin //为节点分配一个记录,记录节点的ID等信息 ANodeData := New(PNodeData); //记录该节点的各个属性,后面要用到 ANodeData^.ID := TreeDataSet.FieldValues['节点编号']; ANodeData^.ParentID := TreeDataSet.FieldValues['父节点编号']; ANodeData^.Text := TreeDataSet.FieldValues['名称']; NewNode := TreeView.AddChild(ParentNode, ANodeData^.Text); //把该节点的DATA指针指向这个记录 NewNode.Data := ANodeData; //继续添加下一个节点 Next; end; 如果不执行下面的代码,那么就只生成一级的树,否则就生成全部的树。按照你的问题的要求,去掉下面的代码,在每次点一个+号的时候调用BuildTree就可以了。 if Assigned(ParentNode) then begin for I := 0 to ParentNode.Count - 1 do BuildTree(ParentNode.Item[I], PNodeData(ParentNode.Item[I].Data)^.ID); else begin for I := 0 to TreeView.Items.Count - 1 do BuildTree(TreeView.Items[I], PNodeData(TreeView.Items[I].Data)^.ID); end;
对了,我说详细一些,比如主表示ADOQuery1,从表是ADOQuery2,设置ADOQuery2的datasource为datasource1,SQL语句为'select * from table_name where key=:key',首先将ADOQuery1的值添加到treeview1中,当treeview1.onchange的时候,用ADOQuery1.locate来定位主表的记录,这样从表的值会自动筛选。然后显示到DBGrid中就可以了。
我有一个物料类别表MaterielType(no,parentno,name)用于treeview之数据
物料名称表Materiel(m_no,no…),作为Dbgrid的数据表
其中物料名称表中的no与物料类别表的no有对应关系
1.如何使物料名称表和物料类别表联接起来,比喻选中物料类型表中的某一条记录,则可以看到相应类别的物料名称
2.如何实现treeview与dbgrid联接,如选中treeview中的某节点物料类别时,则Dbgrid中显示相应类别的物料名称请帮手,TKS!
做相应处理引passos(古月春秋) :表结构可以采用两种方式:
1、编码方式,采用001001001的字符串,然后按照每级的长度切开,然后建树。这种方法最慢,而且树的层次数有限。
2、链式存储方式。结构是 节点编号、父节点编号、名称、编码... 这种方法是最好的解决方法。我详细说一下:
关键是两个编号字段,顶级接点的编号可以是-1或者其他特殊值(字段类型用数字或者字符串都可以)。比如树形是
A----B
|--C
|--D----E
|--F那么数据库内容是:
节点编号 父节点编号 名称
1 -1 A
2 1 B
3 1 C
4 1 D
5 4 E
6 4 F
建立树的操作可以使用一个先根顺序建立各个节点(实际上是一个递归函数,广度优先),算法如下://建立一个记录,存储树节点的信息
PNodeData = ^TNodeData;
TNodeData = record
ID, ParentID: LongInt;
Text: ShortString;
end;//ParentTreeNode TreeView父节点
//ParentID 父节点编号
//TreeDataSet 存储该树的数据表、之前要打开
procedure BuildTree(ParentNode: TTreeNode; ParentID: Integer);
var
I: Integer;
ANodeData: PNodeData;
NewNode: TTreeNode;
begin
//过滤出该父节点下的子节点
TreeDataSet.Filter := Format('父节点编号 = %d', [ParentID]);
TreeDataSet.Filtered := True;
for I := 0 to TreeDataSet.RecordCount - 1 do
begin
ANodeData := New(PNodeData);
ANodeData^.ID := TreeDataSet.FieldValues['节点编号'];
ANodeData^.ParentID := TreeDataSet.FieldValues['父节点编号'];
ANodeData^.Text := TreeDataSet.FieldValues['名称'];
NewNode := TreeView.AddChild(ParentNode, ANodeData^.Text);
NewNode.Data := ANodeData;
Next;
end; //递归调用本函数,为各个子节点生成树
//很明显,如果当前节点没有子节点,那么就是递归调用的结束条件了
//这里的判断是检查第一次调用传进来的参数,如果是nil就用TreeView的属性来访问
if Assigned(ParentNode) then
begin
for I := 0 to ParentNode.Count - 1 do
BuildTree(ParentNode.Item[I], PNodeData(ParentNode.Item[I].Data)^.ID);
else begin
for I := 0 to TreeView.Items.Count - 1 do
BuildTree(TreeView.Items[I], PNodeData(TreeView.Items[I].Data)^.ID);
end;
end;
初始的调用方法是
TreeDataSet.Open;BuildTree(TreeView.Add(nil, '树根'), -1);//或者 BuildTree(nil, -1);TreeDataSet.Close;
TreeDataSet.Filter := '';
TreeDataSet.Filtered := False;
上面那段代码在程序退出的时候要记得释放每个树节点的data指针占用的内存,DELPHI不会负责释放那些空间的。如果不做,将导致内存泄露。
再多加一点注释:
//把数据库中查询出来的每个节点加入到ParentNode下
for I := 0 to TreeDataSet.RecordCount - 1 do
begin
//为节点分配一个记录,记录节点的ID等信息
ANodeData := New(PNodeData);
//记录该节点的各个属性,后面要用到
ANodeData^.ID := TreeDataSet.FieldValues['节点编号'];
ANodeData^.ParentID := TreeDataSet.FieldValues['父节点编号'];
ANodeData^.Text := TreeDataSet.FieldValues['名称'];
NewNode := TreeView.AddChild(ParentNode, ANodeData^.Text); //把该节点的DATA指针指向这个记录
NewNode.Data := ANodeData; //继续添加下一个节点
Next;
end;
如果不执行下面的代码,那么就只生成一级的树,否则就生成全部的树。按照你的问题的要求,去掉下面的代码,在每次点一个+号的时候调用BuildTree就可以了。 if Assigned(ParentNode) then
begin
for I := 0 to ParentNode.Count - 1 do
BuildTree(ParentNode.Item[I], PNodeData(ParentNode.Item[I].Data)^.ID);
else begin
for I := 0 to TreeView.Items.Count - 1 do
BuildTree(TreeView.Items[I], PNodeData(TreeView.Items[I].Data)^.ID);
end;
试着用它,等你做完了这个难题,你会发现
你对指针有更多的了解。你也会从更深的角度来
看delphi。恭喜你,你对delphi的了解又精进了
一层。