2009-12-03 2 views
2

도킹 패널에서 TTreeView를 사용하는 delphi 2009 앱을 작성 중입니다.델파이의 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

답장을 보내 주셔서 감사합니다.

나는 10 년 동안 TTreeNode의 데이터 속성을 사용하여 트리 뷰를 사용 해왔다. 내가 없어야하고 싶었 : 내가 데이터 속성을 사용했다

  • 이 데이터 속성을 설정
  • 만들기/더 메모리가없는 있도록하는 방식으로 "데이터"개체를 파괴하는 것은 누수 과거의 ID 번호.

    오늘, 내 노드는 데이터베이스에서 데이터를 찾을 수있는 GUID를 가지고 있으므로 더 이상 데이터 속성에 "적합하지"않습니다.TTreeNode의 후손을 사용

    멋지게 내 소원을 해결 것으로 보인다하지만 정중하게 그 일을하기 위해 나는 몇 가지해야 할 일을했을 :

    • 핸들 TTreeView.OnCreateNodeClass 이벤트
    • 핸들 TTreeView.OnDeletion을 이벤트가 파괴되기 전에 노드에서 최신 데이터를 검색합니다.
    • handleTreeView.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)))); 
    
3

IIRC, 각 TTreeNode 인스턴스의 Tag Data 속성은 핸들 재 작성을 통해 유지됩니다.

이 정보를 추가 정보가있는 객체가 들어있는 목록의 색인으로 사용하거나 유형 참조를 저장하여 객체 참조에 직접 액세스 할 수 있습니다.

+0

그 클래스가 더'Tag' 속성이 없습니다 :

내가 할 필요는 트리에 노드를 추가 할 수 있습니다. 어쩌면 당신은'Data' 속성을 대신 생각할 것입니다. –

+0

아, 그렇습니다. 델파이 물건을 몇 마디한지 꽤 오랜만입니다. 내 기억이 완전히 실패 할 가능성이 아주 큽니다! – Bevan

+0

+1, 좋은 답변입니다. IMO는 데이터 구조가 다른 곳에서 분명히 존재해야하며, 트리는 객체 소유권을 명확하게하고 데이터를 프리젠 테이션에서 적절하게 분리하기 위해 트리를 참조 만해야합니다 (포인터 또는 핸들을 통해 목록 인덱스를 사용하는지 여부는 중요하지 않음). – mghie

0

문제는 사용자 지정 TreeNode의 잘못된 구현으로 인해 발생합니다. TreeView의 부모 창이 hodden 후에 다시 만들어지면 해당 정보가 보존되지 않습니다. 해결책으로 TTreeView 자손을 만들고 DestroyWnd 메서드를 재정 의하여 사용자 지정 값을 유지합니다. 예를 들어, TCustomTreeView.DestroyWnd 메서드가 구현 된 방법을 살펴보십시오.

+0

TTreeNode에는 DestroyWnd 메서드가 없습니다. 모든 노드가 포함 된 TTreeView 컨트롤의 메서드입니다. –

0

Joe Meyer와 같이 TCustomTreeView.DestroyWnd를 보았을 때 TreeNode.Data 속성을 사용하고 TObject에서 직접 상속 한 새 클래스의 개체에 대한 참조를 저장하는 것이 좋습니다. TreeView의 OnDeletion 이벤트는 "TMyObject (Node.Data) .Free;"라는 파괴 코드를 넣을 수있는 좋은 기회를 제공합니다.

"TMyNode (Node)"대신 "TMyObject (Node.Data)"를 사용해야하는 경우를 제외하고 사용법은 꽤 유사합니다. 경고 : 경험에 따르면 "TMyObject (Node)"는 컴파일 오류를 발생시키지 않고 런타임에 액세스 위반을 발생시키지 않으므로 ".Data"부분을 잊지 않도록주의해야합니다.