我正在编写一个在对接面板上使用TTreeView的delphi 2009应用程序。
我看到如果我将TTreeNode子类化,我可以在我的应用程序中进行大幅度的简化。它所在的树状视图放在对接面板上。
TInfoTreeNode=class(TTreeNode)
private
// remember some stuff
public
end;
procedure TfraInfoTree.tvInfoCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
begin
NodeClass:=TInfoTreeNode;
end;
我想我已经碰壁了......每个“TInfoTreeNode”实例都需要记住自己的事情。因为当包含TTreeView的面板自动隐藏时释放句柄,所以类被销毁。
这是一个问题,因为那时所有类知道的东西都会被遗忘。
有没有办法解决这个问题(除了再次从数据库重新加载每个TInfoTreeNode)?
谢谢你!答案 0 :(得分:3)
IIRC,每个TTreeNode实例上的 Tag
Data
属性通过句柄重建保留。
您可以将此作为包含附加信息的对象的List的索引,或使用类型转换来存储对象引用并直接访问对象。
答案 1 :(得分:0)
问题是由您的自定义TreeNode的错误实现引起的 - 当TreeView的父窗口被hodden重新创建后,它不会保留其信息。作为解决方案,创建一个TTreeView后代并覆盖其DestroyWnd方法,以保留您的自定义值。例如,看看如何实现TCustomTreeView.DestroyWnd方法。
答案 2 :(得分:0)
看过像Joe Meyer建议的TCustomTreeView.DestroyWnd,我建议你恢复使用TreeNode.Data属性,并存储对直接继承自TObject的新类的对象的引用。 TreeView的OnDeletion事件提供了一个放置破坏代码的好地方:“TMyObject(Node.Data).Free;”
除了你需要使用“TMyObject(Node.Data)”而不是“TMyNode(Node)”之外,用法非常相似。但是有一个警告:经验告诉我要注意不要忘记“.Data”部分,因为“TMyObject(Node)”不会抛出编译错误并在运行时引发访问冲突。
答案 3 :(得分:0)
谢谢大家的回复!
我已经使用TTreeNode的数据属性使用树视图已有10年了。我想要摆脱:
我过去也使用了Data属性作为ID号。
今天,我的节点有GUID在数据库中查找数据,因此它们不再适合Data属性。
使用TTreeNode的后代似乎很好地满足了我的愿望,但为了使这项工作很好,我必须做一些事情:
这是代码:
TInfoTreeNodeMemory=record
...
end;
TInfoTreeNode=class(TTreeNode)
private
m_rInfoTreeNodeMemory:TInfoTreeNodeMemory;
public
property InfoTreeNodeMemory:TInfoTreeNodeMemory read m_rInfoTreeNodeMemory write m_rInfoTreeNodeMemory;
end;
TInfoTreeNodeMemoryItemList=class
private
m_List:TList<TInfoTreeNodeMemory>;
public
constructor Create;
destructor Destroy; override;
procedure HandleOnDeletion(Node: TInfoTreeNode);
procedure HandleOnAddition(Node: TInfoTreeNode);
end;
TfraInfoTree = class(TFrame)
tvInfo: TTreeView;
procedure tvInfoCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
procedure tvInfoDeletion(Sender: TObject; Node: TTreeNode);
procedure tvInfoAddition(Sender: TObject; Node: TTreeNode);
private
m_NodeMemory:TInfoTreeNodeMemoryItemList;
...
procedure TfraInfoTree.tvInfoCreateNodeClass(Sender: TCustomTreeView;
var NodeClass: TTreeNodeClass);
begin
// THIS IS VITAL!
NodeClass:=TInfoTreeNode;
end;
procedure TfraInfoTree.tvInfoDeletion(Sender: TObject; Node: TTreeNode);
begin
m_NodeMemory.HandleOnDeletion(TInfoTreeNode(Node));
end;
procedure TfraInfoTree.tvInfoAddition(Sender: TObject; Node: TTreeNode);
begin
m_NodeMemory.HandleOnAddition(TInfoTreeNode(Node));
end;
g_icTreeNodeNotInList=MAXINT;
procedure TInfoTreeNodeMemoryItemList.HandleOnDeletion(Node: TInfoTreeNode);
var
iPosition:integer;
begin
iPosition:=integer(Node.Data);
if iPosition=g_icTreeNodeNotInList then
raise Exception.Create('Node memory not found!')
else
// we recognize this node; store his data so we can give it back to him later
m_List[iPosition]:=Node.InfoTreeNodeMemory;
end;
procedure TInfoTreeNodeMemoryItemList.HandleOnAddition(Node: TInfoTreeNode);
var
iPosition:integer;
begin
// "coat check" for getting back node data later
iPosition:=integer(Node.Data);
if iPosition=g_icTreeNodeNotInList then
begin
// Node.Data = index of it's data
// can't set Node.Data in OnDeletion so we must assign it in OnAddition instead
Node.Data:=pointer(m_List.Count);
// this data may very well be blank; it mostly occupies space; we harvest the real data in OnDeletion
m_List.Add(Node.InfoTreeNodeMemory);
end
else
// we recognize this node; give him his data back
Node.InfoTreeNodeMemory:=m_List[iPosition];
end;
非常酷......它符合我的所有目标!
要向树中添加节点,我需要做的就是:
// g_icTreeNodeNotInList important so the "coat check" (TInfoTreeNodeMemoryItemList)
// can recognize this as something that's not in it's list yet.
MyInfoTreeNode:=TInfoTreeNode(tvInfo.Items.AddChildObject(nParent, sText, pointer(g_icTreeNodeNotInList))));