问题继承delphi中的TTreeNode

时间:2009-12-03 01:03:54

标签: delphi treeview

我正在编写一个在对接面板上使用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)?

谢谢你!

4 个答案:

答案 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的后代似乎很好地满足了我的愿望,但为了使这项工作很好,我必须做一些事情:

  • 处理TTreeView.OnCreateNodeClass事件
  • 处理TTreeView.OnDeletion事件以在节点被销毁之前从节点检索最新数据
  • 处理TTreeView.OnAddition事件:1)维护一个简单的节点列表2)设置节点的Data属性,这样我们就可以用它来查找分配给存储数据的列表中的位置。

这是代码:

  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))));