要求しているデータ構造は非常に単純です。非常に単純なので、Windowsが提供するものをTTreeView
使用することをお勧めします。追加の作業なしでテキストとIDをツリーのノードに直接保存できます。
より単純なものを使用することをお勧めしますがTTreeView
、データ構造の問題についての私の見解を示します。まず、レコードではなく、クラスを使用します。非常に短いコードサンプルでは、非常に不規則な方法でレコードとクラスを混合しています。レコードのコピーを作成する場合(レコードは常に「値」として扱われるため、レコードを割り当てると完全なコピーが作成されます)、作成しません。ツリーの「ディープコピー」:レコードとは異なり、クラスは参照であるため、の完全なコピーには元のコピーと同じものが含まれます。参照の値に対処していることになります。TRoot
TRoot
Kids:TList
オブジェクトフィールドを持つレコードがある場合のもう1つの問題は、ライフサイクル管理です。レコードにはデストラクタがないため、所有しているオブジェクトを解放するために別のメカニズムが必要になります(Kids:TList
)。TList
をに置き換えることもできますが、モンスターレコードを渡すときは、非常にarray of Tkid
注意する必要があります。これは、予想外のときに巨大なレコードのディープコピーを作成しなくなる可能性があるためです。
私の意見では、最も賢明なことは、レコードではなくクラスに基づいてデータ構造を作成することです。クラスインスタンス(オブジェクト)は参照として渡されるため、問題なく必要な場所に移動できます。また、組み込みのライフサイクル管理(デストラクタ)を取得します
基本クラスは次のようになります。ルートとキッドの両方がデータを共有するため、ルートまたはキッドのいずれかとして使用できることに気付くでしょう。どちらにも名前とIDがあります。
TNodeClass = class
public
Name: string;
ID: Integer;
end;
このクラスをルートとして使用する場合は、キッズを保存する方法が必要です。Delphi 2010以降を使用していると思いますので、ジェネリックを使用しています。リストを備えたこのクラスは、次のようになります。
type
TNode = class
public
ID: integer;
Name: string;
VTNode: PVirtualNode;
Sub: TObjectList<TNode>;
constructor Create(aName: string = ''; anID: integer = 0);
destructor Destroy; override;
end;
constructor TNode.Create(aName:string; anID: Integer);
begin
Name := aName;
ID := anID;
Sub := TObjectList<TNode>.Create;
end;
destructor TNode.Destroy;
begin
Sub.Free;
end;
これにすぐには気付かないかもしれませんが、このクラスだけでマルチレベルツリーを実装できます。ツリーをデータで埋めるためのコードを次に示します。
Root := TNode.Create;
// Create the Contacts leaf
Root.Sub.Add(TNode.Create('Contacts', -1));
// Add some contacts
Root.Sub[0].Sub.Add(TNode.Create('Abraham', 1));
Root.Sub[0].Sub.Add(TNode.Create('Lincoln', 2));
// Create the "Recent Calls" leaf
Root.Sub.Add(TNode.Create('Recent Calls', -1));
// Add some recent calls
Root.Sub[1].Sub.Add(TNode.Create('+00 (000) 00.00.00', 3));
Root.Sub[1].Sub.Add(TNode.Create('+00 (001) 12.34.56', 4));
このタイプを使用して仮想ツリービューを埋めるには、再帰的な手順が必要です。
procedure TForm1.AddNodestoTree(ParentNode: PVirtualNode; Node: TNode);
var SubNode: TNode;
ThisNode: PVirtualNode;
begin
ThisNode := VT.AddChild(ParentNode, Node); // This call adds a new TVirtualNode to the VT, and saves "Node" as the payload
Node.VTNode := ThisNode; // Save the PVirtualNode for future reference. This is only an example,
// the same TNode might be registered multiple times in the same VT,
// so it would be associated with multiple PVirtualNode's.
for SubNode in Node.Sub do
AddNodestoTree(ThisNode, SubNode);
end;
// And start processing like this:
VT.NodeDataSize := SizeOf(Pointer); // Make sure we specify the size of the node's payload.
// A variable holding an object reference in Delphi is actually
// a pointer, so the node needs enough space to hold 1 pointer.
AddNodesToTree(nil, Root);
オブジェクトを使用する場合、仮想ツリーのノードごとに異なるタイプのオブジェクトが関連付けられている場合があります。この例では、タイプのノードのみを追加していTNode
ますが、実際には、タイプ、、、のノードがTContact
すべてTContactCategory
1TRecentCall
つのVTに含まれている場合があります。演算子を使用して、is
次のようにVTノード内のオブジェクトの実際のタイプを確認します。
procedure TForm1.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var PayloadObject:TObject;
Node: TNode;
Contact : TContact;
ContactCategory : TContactCategory;
begin
PayloadObject := TObject(VT.GetNodeData(Node)^); // Extract the payload of the node as a TObject so
// we can check it's type before proceeding.
if not Assigned(PayloadObject) then
CellText := 'Bug: Node payload not assigned'
else if PayloadObject is TNode then
begin
Node := TNode(PayloadObject); // We know it's a TNode, assign it to the proper var so we can easily work with it
CellText := Node.Name;
end
else if PayloadObject is TContact then
begin
Contact := TContact(PayloadObject);
CellText := Contact.FirstName + ' ' + Contact.LastName + ' (' + Contact.PhoneNumber + ')';
end
else if PayloadObject is TContactCategory then
begin
ContactCategory := TContactCategory(PayloadObject);
CellText := ContactCategory.CategoryName + ' (' + IntToStr(ContactCategory.Contacts.Count) + ' contacts)';
end
else
CellText := 'Bug: don''t know how to extract CellText from ' + PayloadObject.ClassName;
end;
そして、ノードインスタンスへのVirtualNodeポインタを保存する理由の例を次に示します。
procedure TForm1.ButtonModifyClick(Sender: TObject);
begin
Root.Sub[0].Sub[0].Name := 'Someone else'; // I'll modify the node itself
VT.InvalidateNode(Root.Sub[0].Sub[0].VTNode); // and invalidate the tree; when displayed again, it will
// show the updated text.
end;
単純なツリーデータ構造の実用的な例があることをご存知でしょう。ニーズに合わせてこのデータ構造を「成長」させる必要があります。可能性は無限大です。あなたにいくつかのアイデアを与えるために、探求する方向:
- を
Name:string
仮想メソッドに変換してから、そのオーバーライドGetText:string;virtual
の特殊な子孫を作成して、特殊な動作を提供できます。TNode
GetText
TNode.AddPath(Path:string; ID:Integer)
これを可能にするを作成しますRoot.AddPath('Contacts\Abraham', 1);
。つまり、ツリーを簡単に作成できるように、すべての中間ノードを最終ノードに自動的に作成するメソッドです。
- ノードが仮想ツリーで「チェック」されているかどうかをチェックできるように、それ自体にインクルードします
PVirtualNode
。TNode
これは、データとGUIの分離の架け橋になります。