ผู้ปกครองของ RootNode ของ TVirtualStringTree
ตามเอกสารประกอบคุณสมบัติ RootNode ของ TVirtualStringTree คือโหนดที่ซ่อนอยู่ซึ่งเป็นพาเรนต์ของโหนดที่ผู้ใช้สร้างขึ้นทั้งหมด แต่มีการตั้งค่าพาเรนต์ของ RootNode ฉันกำลังประสบปัญหาต่อไปนี้:
บางครั้งเมื่อปิดแบบฟอร์มที่มี TVirtualStringTree อยู่ฉันได้รับข้อผิดพลาดในการละเมิดการเข้าถึง การดีบักข้อผิดพลาดเกิดขึ้นเมื่อพยายามเข้าถึงคุณสมบัติ Name ของคอมโพเนนต์ (virtualstringtree) หากฉันพยายามประเมินคุณสมบัติ 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 (ซ่อนอยู่) และจากนั้นเป็นพาเรนต์ของรูท พาเรนต์ของโหนดรูทพาเรนต์เท่านั้นที่เป็นศูนย์ ในเมธอด GetNodeData ได้รับการตรวจสอบว่าเป็นโหนดรูทหรือไม่ให้ส่งคืนค่า nil แต่สำหรับพาเรนต์ของรูทนี้เนื่องจากไม่ใช่รูทให้ป้อนโค้ดอื่น นี่คือปัญหา:
ที่อยู่ของคุณสมบัติ Name ของ virtualstrintree คือ $ 141B9768
แอดเดรสของคุณสมบัติ States ของพาเรนต์ของโหนดรูทคือ $ 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;
เพียงแค่ใส่ต้นไม้และปุ่มในแบบฟอร์มฉันใส่ห่วงในการคลิกปุ่ม
คำตอบ
ขั้นแรกเอกสารประกอบ: (เช่น) https://documentation.help/VirtualTreeview/TBaseVirtualTree_RootNode.html
พูดว่า (เน้นของฉัน)
คุณสมบัติ RootNode: PVirtualNode;
คำอธิบาย
สำหรับการยึดลำดับชั้นของต้นไม้จะมีการบำรุงรักษาโหนดต้นไม้ภายในซึ่งส่วนใหญ่จะเหมือนกับโหนดต้นไม้อื่น ๆ แต่บางครั้งก็มีการจัดการที่แตกต่างกัน โหนดรูทจะถูกขยายและเริ่มต้นเสมอ จุดที่สมาชิกผู้ปกครองในการ TreeView ที่โหนดเป็นสมาชิกและ 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
จะเหมาะกับคุณ
ดังนั้นต้องขอบคุณคำตอบของ Tom และเอกสารของส่วนประกอบพาเรนต์ของ RootNode จึงเป็นต้นไม้นั้นเอง ดังนั้นเมื่อสร้างลูปด้วยParent = nilas point of stop เมื่อไปถึงรูทของพาเรนต์ (ทรี) โค้ดIncludeในGetNodeDataจะถูกเรียกใช้งานและในกรณีของฉันเมื่อเข้าถึงStateคุณสมบัติแอดเดรสของมันอยู่ใกล้กับที่อยู่ชื่อของทรีมาก เนื่องจากที่อยู่ของ "โหนด" คือต้นไม้
ดังนั้นฉันจึงแก้ไขลูปเพื่อตรวจสอบNode <> Tree.RootNodeและแก้ไขGetNodeDataเพื่อตรวจสอบด้วยว่าโหนดเป็นตัวเองor (Node = Pointer(Self)หรือไม่ในกรณีที่รหัสอื่นในแอปพลิเคชันของฉัน (รหัสเดิม 2 ล้านรหัส) ใช้การวนซ้ำที่ไม่ถูกต้อง :
ก่อน:
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;