自己制作的一个TDBTreeView中的一点问题(TreeNode的释放) ( 积分: 100 )

  • 主题发起人 主题发起人 atoi
  • 开始时间 开始时间
A

atoi

Unregistered / Unconfirmed
GUEST, unregistred user!
(请关注问题本身,不要建议我用现成的TDBTreeview等等)
我自己制作了一个TDBTreeview控件,在模式对话框中使用完全达到了设计目标。我从TTreeView和TTreeNode派生了TDBTreeView和TDBTreeNode,TDBTreeNode只是增加了几个属性,其他没变化。
现在的问题是,当我在一个TFrame中使用这个控件,则在TFrame加载的时候,我调用如下所示的TDBTreeview.Load方法添加TDBTreeNode到TDBTreeView,当Load方法结束的时候,TDBTreeNode的Destroy方法立刻就会被调用了,也就是说添加的节点被过早的释放了。在使用的时候TDBTreeView所扩充的几个属性的值全部丢失了。请问我的代码存在什么问题吗?或者该如何避免TDBTreeNode被释放这一点?
procedure TDBTreeview.Load(DataSet: TDataSet);
var
AddingNode: TDBTreeNode;
CurrentNode: TDBTreeNode;
AddingParent: Integer;
AddingTitle: string;
begin
if Assigned(DataSet) then
FdataSet := DataSet;

if not Assigned(FDataSet) then
Exit;

if (Length(FDepthField) = 0) or (Length(FTitleField) = 0) or (Length(FParentIdField) = 0) then
Exit;

Items.Clear;
CurrentNode := nil;

with FDataSet do
try
while not EOF do
begin
// ÅжÏÒªÌí¼ÓµÄ½ÚµãÓ뵱ǰ½ÚµãµÄÏà¶Ô¹Øϵ

AddingParent := FieldByName(FParentIdField).AsInteger;
AddingTitle := FieldByName(FTitleField).AsString;

CurrentNode := FindNode(CurrentNode, AddingParent);
AddingNode := TDBTreeNode.Create(Items);
AddingNode.FDBTreeView := self;

if nil = inherited Items.AddNode(AddingNode, CurrentNode, AddingTitle, nil, naAddChild) then
Exit;

AddingNode.NodeID := FieldByName(FIdField).AsInteger;
AddingNode.OrderInBrothers := FieldByName(FOrderInBrothersField).AsInteger;
AddingNode.Depth := FieldByName(FDepthField).AsInteger;
AddingNode.ParentID := FieldByName(FParentIDField).AsInteger;

CurrentNode := AddingNode;
Next;
end;
except
end;
end;
 
(请关注问题本身,不要建议我用现成的TDBTreeview等等)
我自己制作了一个TDBTreeview控件,在模式对话框中使用完全达到了设计目标。我从TTreeView和TTreeNode派生了TDBTreeView和TDBTreeNode,TDBTreeNode只是增加了几个属性,其他没变化。
现在的问题是,当我在一个TFrame中使用这个控件,则在TFrame加载的时候,我调用如下所示的TDBTreeview.Load方法添加TDBTreeNode到TDBTreeView,当Load方法结束的时候,TDBTreeNode的Destroy方法立刻就会被调用了,也就是说添加的节点被过早的释放了。在使用的时候TDBTreeView所扩充的几个属性的值全部丢失了。请问我的代码存在什么问题吗?或者该如何避免TDBTreeNode被释放这一点?
procedure TDBTreeview.Load(DataSet: TDataSet);
var
AddingNode: TDBTreeNode;
CurrentNode: TDBTreeNode;
AddingParent: Integer;
AddingTitle: string;
begin
if Assigned(DataSet) then
FdataSet := DataSet;

if not Assigned(FDataSet) then
Exit;

if (Length(FDepthField) = 0) or (Length(FTitleField) = 0) or (Length(FParentIdField) = 0) then
Exit;

Items.Clear;
CurrentNode := nil;

with FDataSet do
try
while not EOF do
begin
// ÅжÏÒªÌí¼ÓµÄ½ÚµãÓ뵱ǰ½ÚµãµÄÏà¶Ô¹Øϵ

AddingParent := FieldByName(FParentIdField).AsInteger;
AddingTitle := FieldByName(FTitleField).AsString;

CurrentNode := FindNode(CurrentNode, AddingParent);
AddingNode := TDBTreeNode.Create(Items);
AddingNode.FDBTreeView := self;

if nil = inherited Items.AddNode(AddingNode, CurrentNode, AddingTitle, nil, naAddChild) then
Exit;

AddingNode.NodeID := FieldByName(FIdField).AsInteger;
AddingNode.OrderInBrothers := FieldByName(FOrderInBrothersField).AsInteger;
AddingNode.Depth := FieldByName(FDepthField).AsInteger;
AddingNode.ParentID := FieldByName(FParentIDField).AsInteger;

CurrentNode := AddingNode;
Next;
end;
except
end;
end;
 
TTreeNode本身有一个data属性,可以是结构数据,你没有必要自己写TDBTreeNode
type
PMyRec = ^TMyRec;
TMyRec = record
id: Longint; //目录id
Pid: Longint; //目录pid
Name:String; //对应的名称
end;
Nodetemp: TTreeNode;
MyRecPtr: PMyRec;

New(MyRecPtr); //分配内存
MyRecPtr^.id := 0;
MyRecPtr^.pid := -1;
MyRecPtr^.Name:='sdfs';
Nodetemp.Data := MyRecPtr;
 
你的是生命期的问题,你定义的结点变量在这个函数结束时就完了。
解决的办法显然是让你的结点数据和你的TreeView有相同的生命期,
有两种办法都可以实现,一是在使用动态分配内存,但很多人写的这个组件都会犯一个同样的问题,就是动态分配的这些内存在释放TreeView时并没有一起被释放,造成内存遗留。这可以在析构函数里加个循环来释放。
还有一种办法就是定义一个在TreeView中全局的结点数组,就不用在析构函数里那么麻烦了。
 
问题我自己已经解决了。谢谢两位热心。

To yanghai0437: 我之所以不使用Data属性是因为我要做一个控件给多个项目用,所以如果我把Data属性占用了,别人就没法使用这个属性了。

To yiemyn: 你的说法是错误的,因为我在问题中已经明确说了“在模式对话框中使用完全达到了设计目标”,只有在TFrame中使用的时候才有这个问题。

出现这个问题是由于我忘了设置TFrame的parent属性造成的。
再次感谢两位热心。
 
后退
顶部