2009-12-03 26 views
2

我正在編寫一個在對接面板上使用TTreeView的delphi 2009應用程序。問題在delphi中繼承TTreeNode

我看到我能做出重大簡化在我的應用程序,如果我子類的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

謝謝大家的回覆!

我已經使用樹視圖使用TTreeNode的數據屬性10年。我想成爲自由的:

  • 在設置數據屬性
  • 創建/的方式破壞的「數據」對象,所以沒有內存泄漏

我已經使用的數據屬性也是過去的ID號碼。今天

,我的節點具有GUID來在數據庫中找到它們的數據,使他們不「適合」的數據屬性了。

使用TTreeNode的後代似乎已經很好地解決了我的願望,但爲了使這項工作很好,我不得不做的幾件事情:

  • 手柄TTreeView.OnCreateNodeClass事件
  • 手柄TTreeView.OnDeletion事件從它們被破壞
  • 手柄TTreeView.OnAddition事件前的節點檢索最新數據:1)保持節點的簡單列表2)設置節點的數據屬性,所以我們可以用它來在列表中找到的地方分配用於存儲它的數據。

下面的代碼:

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

IIRC, Tag Data通過句柄重建保留每個TTreeNode實例上的屬性。

你可以選擇使用此作爲一個指數到含有的其他信息,或使用的類型轉換成存儲一個對象的引用並直接訪問對象的對象列表。

+0

那類沒有'Tag'財產。也許你正在想'Data'屬性。 – 2009-12-03 02:32:27

+0

是啊,嗯,這是因爲我做了一些東西,德爾福已經有一段時間。我的記憶完全可能會讓我失望! – Bevan 2009-12-03 05:10:49

+0

+1,很好的答案。國際海事組織的數據結構一定要存在於其他地方,和樹應該只引用它(不要緊,無論通過指針或手柄,像列表索引),使對象所有權更清晰,並能夠正確呈現脫鉤的數據。 – mghie 2009-12-03 07:09:12

0

問題是由錯誤的實現您的自定義樹節點造成的 - 當它已經hodden後TreeView的父窗口被重新它不保留其信息。作爲解決方案,創建一個TTreeView後代並重寫其DestroyWnd方法,以保留您的自定義值。例如,看看如何實現TCustomTreeView.DestroyWnd方法。

+0

TTreeNode沒有DestroyWnd方法。這是包含所有節點的TTreeView控件的一種方法。 – 2009-12-03 14:25:03

0

看過Joe Meyer提出的TCustomTreeView.DestroyWnd之後,我建議您恢復使用TreeNode.Data屬性,並且直接存儲對從TObject繼承的新類的對象的引用。 TreeView的OnDeletion事件提供了一個放置銷燬代碼的好地方:「TMyObject(Node.Data).Free;」

用法很類似,只是你需要使用的,而不是「TMyNode(節點)」「TMyObject(Node.Data)」。儘管有一個警告:經驗告訴我要密切注意不要忘記「.Data」部分,因爲「TMyObject(Node)」不會在運行時拋出編譯錯誤並引發訪問衝突。