访问treenode.data 的问题,高手请进!(100分)

F

fenian

Unregistered / Unconfirmed
GUEST, unregistred user!
我用一个treeview显示所有的部门,由于显示的是部门名,我想通过单击某部门取得部门号。我是这么作的。

type
PDeptNo = ^TDeptNo;
TDeptNo = record
DeptNo:string; //部门号
end;

procedure Tform1.FormCreate(Sender: TObject);
var
p_no:pDeptNo;
begin
new(p_no);
p_no^.DeptNo:=query1.fieldbyname('deptno').asstring;
.
.
treeview1.items.addchildobject(root,query1.fieldbyname('deptname').asstring, P_NO);
.
.
dispose(P_NO);
end;

直到这儿都没有问题,树建好了,能分层显示出出各部门。

procedure Tform1.treeview1Click(Sender: TObject);
begin
showmessage(PDeptNo(treeview1.selected.Data)^.DeptNo);
end;
正常情况下单击某部门名,应显示相应的 部门号。
可现在要么显示的 乱码,要么报错:access violation at address 004D973D in module 'ntdll.dll' ~~~~~~~~~~

请问各位是怎么回事啊?
还有,是不是用了new()后就一定要用dispose() 啊?我不用dispose好像也可以啊。
 
A

app2001

Unregistered / Unconfirmed
GUEST, unregistred user!
不用就不释放内存,当然你的内存够大就可以呗,但是这样不严谨,就好象你不把拉圾扔到拉圾箱一样
 
F

fenian

Unregistered / Unconfirmed
GUEST, unregistred user!
to app2001:
我好像记得如果p是空,那么dispose(p)会出错,有这么回事吧。
关键是前一个问题,你帮过我不少忙了,再帮我研究研究如何?
 
J

jlutt-sadan

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure Tform1.FormCreate(Sender: TObject);
var
p_no:pDeptNo;
begin
new(p_no);
p_no^.DeptNo:=query1.fieldbyname('deptno').asstring;
.
.
treeview1.items.addchildobject(root,query1.fieldbyname('deptname').asstring, P_NO);
.
.
dispose(P_NO);//你在这里创建的时候就释放掉了,当然该地址无法访问了
end;
应该在合适的地方释放掉这些指针,比如FormClose的时候
 
F

fenian

Unregistered / Unconfirmed
GUEST, unregistred user!
to jlutt-sadan:
you are right。一会给分。
看来我对 treeview1.selected.Data 还是不了解,能给我解释解释吗?为什么我释放了p_no,treeview1.selected.data也被释放了?
还有,由于部门关系比较复杂,在建 部门树 的时候我用了递归。按你的说法,在递归中new()的变量就不可以当时dispose了。可在formclose中如何释放递归函数中new()的变量啊?
 
A

app2001

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure FreeNodeData(T: TTreeView);
var
i: integer;
begin
for i := 0 to T.items.count - 1 do
begin
dispose(T.items.data);
T.items.data := nil;
end;
end;
我就专门建一个过程,在CLOSE事件里调用FreeNodeData(TreeView1);就可以了
 
M

Motar Liu

Unregistered / Unconfirmed
GUEST, unregistred user!
以下是Show 子结构的OnClick,可参考看看
var NewItem : TListItem;
Node : TTreeNode;
begin
Node := DBTreeView1.Selected.GetFirstChild;
ListView1.Items.Clear;
if Node <> nil then
begin
while Node <> nil do
begin
NewItem := ListView1.Items.Add;
NewItem.Caption := Node.Text;
NewItem.ImageIndex := Node.ImageIndex;
if tbForm.Locate('????', TNodeObject(Node.Data).CurrentString, []) then
NewItem.ImageIndex := tbFormImageType.AsInteger;
NewItem.Data := Node.Data;
Node := DBTreeView1.Selected.GetNextChild(Node);
end;
end;
 
J

jlutt-sadan

Unregistered / Unconfirmed
GUEST, unregistred user!
我对指针不是很了解:((没学过c)
TreeNode.Data和你声明的p_no是一个指针,它们指向的是你分配结构TDeptNo的地址,你释放了p_no,等同与TreeNode.Data所指的地址也为空了,当然你后面引用这个地址的时候无法读取了。(这只是我的理解,写的不是很明白,具体的你可以请教高手解释一下指针的概念)

无论你用什么算法,按照你程序的情况,你每增加一个节点,你就得给生成一个指针变量,如果后面程序中需要引用的话,这里是不能释放的。
释放的方法是对TreeView的节点做循环
var
ANode: TTreeNode;
begin
if TreeView.Items.Count>0 then
ANode := TreeView.Items[0];
while ANode<>nil do begin
if Assigned(ANode.Data) then begin
dispose(PDeptNo(ANode.Data);
end;
ANode := ANode.GetNext;
end;
end;
 
F

fenian

Unregistered / Unconfirmed
GUEST, unregistred user!
to jlutt-sadan:
别的都可以了。但用你的释放方法报错,估计是最后一句 ANode := ANode.GetNext;的问题。当到了最后一个时,再 ANode.GetNext 指针可能溢出了。
怎么改进那?

to app2001:
你的方法没有对T.items.data是否不为空进行判断,如果T.items.data为空,那么再dispose就会出错,还有别的方法吗?
 
G

gear1023

Unregistered / Unconfirmed
GUEST, unregistred user!
同意[blue]app2001[/blue]

procedure FreeNodeData(T: TTreeView);
var
i: integer;
begin
for i := 0 to T.items.count - 1 do
try //防 Access Violation as ..... 并保证所有DATA都能FREE掉!
dispose(T.items.data);
T.items.data := nil;
except
end;
end;
 
A

app2001

Unregistered / Unconfirmed
GUEST, unregistred user!
我不需要判断是否不为空,因为我的程序中,只有这一个地方释放节点,在别的地方没有这样的功能,包括创建树节点时。因此,只要我的树创建出来,节点就不会为空
 
F

fenian

Unregistered / Unconfirmed
GUEST, unregistred user!
to app2001:
procedure TfrmDeptinfo.FormDestroy(Sender: TObject);
begin
FreeNodeData(tv_deptinfo);
end;
报错:invalid pointer operation.
把这句屏蔽掉后就好了。

不过及有可能是我程序的问题,因为在treeview的onclick事件中:
procedure Tform1.treeview1Click(Sender: TObject);
begin
showmessage(PDeptNo(treeview1.selected.Data)^.DeptNo);
end;
点击某一部门名时,应该显示的是该部门的部门号。但在我这,无论点哪一个部门,显示的都是一个部门号:该级节点的最后一个兄弟节点的部门号。
回去好好推敲推敲。各位兄弟们如果有兴趣也帮我看看好吗?为什么treeview1.selected.Data 选择的并不是当前节点的data,而是最后一个兄弟节点的data?
不胜感激之至!
 
A

app2001

Unregistered / Unconfirmed
GUEST, unregistred user!
我是在CLOSE的事件中
 
J

jlutt-sadan

Unregistered / Unconfirmed
GUEST, unregistred user!
把你的代码贴出来,特别是生成TreeView的
 

耗子_super

Unregistered / Unconfirmed
GUEST, unregistred user!
你的声明是这样的:
type
PDeptNo = ^TDeptNo;
TDeptNo = record
DeptNo:string; //部门号
end;

然后每次 addchildobject(root,query1.fieldbyname('deptname').asstring, P_NO)
由于你的p_no是个指针,你create tree的时候,肯定有个循环。估计是每次循环结束,指针指向的都是最后一个节点。然后你在tree的onclick事件中,取的应该就是最后一个兄弟节点。

提供你另外一种思路,希望能对你有所帮助:
建树的循环里头用: addchildobject(root,query1.fieldbyname('deptname').asstring, query1.getbookmark).
树的onchange事件中:query1.GotoBookmark(node.Data)
 
C

cnaoszh

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure Tform1.FormCreate(Sender: TObject);
var
p_no:pDeptNo;
begin
new(p_no);
p_no^.DeptNo:=query1.fieldbyname('deptno').asstring;
.
.
treeview1.items.addchildobject(root,query1.fieldbyname('deptname').asstring, P_NO);
.
.
[red]dispose(P_NO);//这一句删掉[/red]end;


在form de的destory中

procedure FreeNodeData(T: TTreeView);
var
i: integer;
p_no:pDeptNo;
begin
for i := 0 to T.items.count - 1 do
begin
p_no:=T.items.data;
dispose(p_no);
end;
end;

procedure Tform1.treeview1Click(Sender: TObject);
var
p_no:pDeptNo;
begin
if treeview1.selected=nil then
exit;
p_no:=treeview1.selected.Data;
showmessage(p_no^.DeptNo);
end;
不过一般应该在treeview1.onselectedchange中写




 
F

fenian

Unregistered / Unconfirmed
GUEST, unregistred user!
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
559
import
I
S
回复
0
查看
811
SUNSTONE的Delphi笔记
S
L
回复
4
查看
468
xuxiaohan
X
顶部