TVirtualStringTree의 RootNode의 부모

Sep 18 2020

문서에 따르면 TVirtualStringTree의 속성 RootNode는 모든 사용자가 생성 한 노드의 부모 인 숨겨진 노드입니다. 그러나 RootNode의 부모가 설정되었습니다. 다음 문제에 직면 해 있습니다.

때때로, TVirtualStringTree가있는 양식을 닫을 때 액세스 위반 오류가 발생합니다. 디버깅 중 구성 요소 (virtualstringtree)의 Name 속성에 액세스하려고하면 오류가 발생합니다. Name 속성을 평가하려고하면 "액세스 할 수없는 값"이 표시됩니다. 문제를 파헤쳐 보면 구성 요소의 Name 속성을 런타임에서 변경할 수 없다는 것을 알았습니다 .

경고 : 런타임에 이름을 변경하면 이전 이름에 대한 모든 참조가 정의되지 않게됩니다. 이전 이름을 사용하는 모든 후속 코드는 예외를 발생시킵니다.

내 코드에서 이것을 변경하지 않았습니다. Name 속성의 데이터 중단 점을 사용하여 다음과 같이 virtualstringtree 루프에서 변경되는 것을 확인했습니다 (문제를 테스트하기 위해 샘플 앱에서 사용하는 예제 코드이지만 프로덕션 코드는 비슷합니다).

var
  N: PNode;
  P: PVirtualNode;
begin
  P := tree.FocusedNode;
  while Assigned(P) do
  begin
    N := tree.GetNodeData(P);
    P := P.Parent;
  end;

GetNodeData는 다음과 같습니다.

 if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
    Result := nil
  else begin
    Result := PByte(@Node.Data) + FTotalInternalDataSize;
    Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323
  end;

다음과 같은 트리가 있습니다.

Node 1
  Node 2
    Node 3

노드 3을 선택하면 루프가 5 번 실행됩니다. 3 내 생성 된 노드, RootNode (숨김), 그리고 루트의 부모. 부모 루트 노드의 부모 만 nil입니다. GetNodeData 메서드에서 루트 노드인지 확인하고 nil을 반환합니다. 그러나이 루트의 부모의 경우 루트가 아니기 때문에 else 코드를 입력합니다. 이제 여기에 문제가 있습니다.

virtualstrintree의 Name 속성 주소는 $ 141B9768입니다.

루트 노드 부모의 상태 속성 주소는 $ 141B976A입니다.

데이터 중단 점을 Name 속성에 넣으면이 시점에서 변경됩니다.

Include(Node.States, vsOnFreeNodeCallRequired);

및 AV 생성.

루트 노드를 올바르게 확인하기 위해 루프를 변경할 수 있다는 것을 알고 있지만 이것을 이해하고 싶으므로 코드 대신 구성 요소 (구성 요소 버그 인 경우)를 수정할 수 있습니다.

암호:

type
  TNode = record
    Text: string;
  end;
  PNode = ^TNode;
...
procedure TForm1.FormCreate(Sender: TObject);
var
  N1, N2, N3: TNode;
  TreeNode: PVirtualNode;
begin
  N1.Text := 'Node 1';
  TreeNode := tree.AddChild(nil, PNode(N1));

  N2.Text := 'Node 2';
  TreeNode := tree.AddChild(TreeNode, PNode(N2));

  N3.Text := 'Node 3';
  TreeNode := tree.AddChild(TreeNode, PNode(N3));
end;

폼에 트리와 버튼을 넣고 버튼을 클릭하면 루프를 넣습니다.

답변

2 TomBrunberg Sep 18 2020 at 14:19

첫째, 문서 : (예) https://documentation.help/VirtualTreeview/TBaseVirtualTree_RootNode.html

말한다 (내 강조)

속성 RootNode : PVirtualNode;

기술

트리 계층 구조를 고정하기 위해 내부 트리 노드가 유지되며 이는 대부분 다른 트리 노드와 비슷하지만 때로는 다르게 처리됩니다. 루트 노드는 항상 확장되고 초기화됩니다. 부모 멤버는 노드가 속한 트 리뷰를 가리키고, PreviousSibling 및 NextSibling 멤버는 실제로이 노드를 인식 할 수 있도록 루트 노드 자체를 가리 킵니다 .

메모

루트 노드를 사용하여 트리를 반복하면 안됩니다. 모든 최상위 노드의 부모이므로 공개적으로 만 액세스 할 수 있으며 최상위 노드인지 여부를 테스트하는 데 사용할 수 있습니다.

둘째

코드에서

var
  N: PNode;
  P: PVirtualNode;
begin
  P := tree.FocusedNode;
  while Assigned(P) do
  begin
    N := tree.GetNodeData(P);
    P := P.Parent;
  end;

P와 비교 tree.RootNode( 또는와 비교)하여 내부 루트인지 확인하지 않기 때문에 무조건 내부 루트 노드를지나 위로 이동합니다 P.NextSibling.

아마도 변경

while Assigned(P) do

조건

while P <> tree.RootNode do

너에게 어울릴거야

1 RodrigoCaetano Sep 18 2020 at 19:57

따라서 Tom의 답변과 구성 요소의 문서 덕분에 RootNode의 부모는 실제로 트리 자체입니다. 와 루프 만들 때, Parent = nil부모의 루트 (트리)에 도달 할 때 정지 점으로, 코드 Include에서이 GetNodeData내 경우, 실행되고를 액세스 할 때, State속성을, 그 주소는 treee의 이름 주소로 매우 가까이 , "노드"의 주소가 트리 였기 때문입니다.

그래서, 내가 확인하기 위해 루프를 해결 Node <> Tree.RootNode하고 또한이 수정 GetNodeData노드가 자체 인 경우도 확인 : or (Node = Pointer(Self), (2,000,000 + 코드의 유산을) 단지의 경우 다른 코드 내 응용 프로그램에서이 같은 잘못된 루프를 사용 :

전에:

function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
begin
  Assert(FNodeDataSize > 0, 'NodeDataSize not initialized.');
  if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
    Result := nil
  else begin
    Result := PByte(@Node.Data) + FTotalInternalDataSize;
    Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323
  end;
end;

후:

function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
begin
  Assert(FNodeDataSize > 0, 'NodeDataSize not initialized.');
  if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) or (Node = Pointer(Self)) then
    Result := nil
  else begin
    Result := PByte(@Node.Data) + FTotalInternalDataSize;
    Include(Node.States, vsOnFreeNodeCallRequired); // We now need to call OnFreeNode, see bug #323
  end;
end;