Induk RootNode dari TVirtualStringTree
Menurut dokumentasi, properti RootNode dari TVirtualStringTree adalah node tersembunyi, induk dari semua node yang dibuat pengguna. Tetapi Parent of the RootNode telah diatur. Saya menghadapi masalah berikut:
Terkadang, saat menutup formulir yang memiliki TVirtualStringTree di dalamnya, saya mendapatkan kesalahan pelanggaran akses. Debugging, kesalahan terjadi ketika mencoba mengakses properti Name dari componente (virtualstringtree). Jika saya mencoba mengevaluasi properti Nama, saya mendapatkan "Nilai tidak dapat diakses". Menggali masalah, saya menemukan bahwa properti Nama komponen tidak dapat diubah dalam runtime :
Peringatan: Mengubah Nama saat runtime menyebabkan referensi apa pun ke nama lama menjadi tidak terdefinisi. Kode berikutnya yang menggunakan nama lama akan menyebabkan pengecualian.
Itu terjadi bahwa saya tidak mengubah ini dalam kode saya. Menggunakan breakpoint data di properti Name, saya melihat bahwa itu berubah dalam loop virtualstringtree, sesuatu seperti ini (contoh kode yang saya gunakan dalam aplikasi sampel untuk menguji masalah, tetapi kode produksi serupa):
var
N: PNode;
P: PVirtualNode;
begin
P := tree.FocusedNode;
while Assigned(P) do
begin
N := tree.GetNodeData(P);
P := P.Parent;
end;
GetNodeData adalah sebagai berikut:
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;
Memberikan bahwa saya memiliki pohon berikut:
Node 1
Node 2
Node 3
Ketika Node 3 dipilih, loop dijalankan 5 kali. 3 di node yang saya buat, lalu RootNode (tersembunyi), dan kemudian Induk dari root. Hanya Induk dari simpul akar orang tua yang nihil. Dalam metode GetNodeData diverifikasi jika itu adalah simpul akar, mengembalikan nihil. Tetapi untuk induk dari root ini, karena ini bukan root, masukkan kode lain. Sekarang inilah masalahnya:
Alamat dari properti Name dari pohon virtual adalah $ 141B9768
Alamat properti Serikat dari induk dari simpul akar adalah $ 141B976A
Saat meletakkan breakpoint data di properti Name, itu akan diubah pada titik ini:
Include(Node.States, vsOnFreeNodeCallRequired);
dan menghasilkan AV.
Saya tahu saya dapat mengubah loop, untuk memeriksa node root dengan benar , tetapi saya ingin memahami ini, jadi saya mungkin memperbaiki komponen (jika itu adalah bug komponen), daripada kode saya.
Kode:
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;
Cukup letakkan pohon dan tombol di formulir, saya meletakkan loop di klik tombol.
Jawaban
Pertama, dokumentasi: (mis.) https://documentation.help/VirtualTreeview/TBaseVirtualTree_RootNode.html
mengatakan (penekanan milik saya)
properti RootNode: PVirtualNode;
Deskripsi
Untuk menjangkarkan hierarki pohon, simpul pohon internal dipertahankan yang sebagian besar sama seperti simpul pohon lainnya tetapi terkadang ditangani secara berbeda. Node root selalu diperluas dan diinisialisasi. Anggota induknya menunjuk ke tampilan pohon tempat simpul tersebut berada dan anggota PreviousSibling dan NextSiblingnya menunjuk ke simpul akar itu sendiri untuk membuatnya mungkin untuk benar-benar mengenali simpul ini .
Catatan
Anda tidak boleh menggunakan simpul akar untuk melakukan iterasi melalui pohon. Ini hanya dapat diakses publik karena merupakan induk dari semua node tingkat atas dan dapat digunakan untuk menguji node apakah itu node tingkat atas atau bukan.
Kedua
Dalam kode Anda
var
N: PNode;
P: PVirtualNode;
begin
P := tree.FocusedNode;
while Assigned(P) do
begin
N := tree.GetNodeData(P);
P := P.Parent;
end;
Anda melintasi tanpa syarat ke atas melewati simpul akar internal karena Anda tidak memeriksa Psebagai akar internal, dengan membandingkan dengan tree.RootNode(atau membandingkan dengan P.NextSibling).
Mungkin mengubah
while Assigned(P) do
kondisi untuk
while P <> tree.RootNode do
akan cocok untukmu
Jadi, berkat jawaban Tom, dan dokumentasi komponennya, induk RootNode sebenarnya adalah pohon itu sendiri. Jadi, saat membuat perulangan dengan Parent = niltitik berhenti sebagai, saat mencapai akar induk (pohon), kode Includedi dalam GetNodeDatadieksekusi, dan, dalam kasus saya, saat mengakses Stateproperti, alamatnya sangat dekat dengan Alamat nama pohon , karena alamat "node" adalah pohonnya.
Jadi, saya memperbaiki loop untuk memeriksa Node <> Tree.RootNodedan juga memperbaiki GetNodeDatauntuk juga memeriksa apakah Node adalah sendiri: or (Node = Pointer(Self), hanya dalam kasus kode lain dalam aplikasi saya (warisan dari 2 juta + kode) menggunakan loop yang salah sama :
Sebelum:
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;
Setelah:
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;