もちろんできます。ノードとデータを頭の中で分離する必要があります。TVirtualStringTree のノードはデータを保持する必要はありません。単にデータが存在するインスタンスを指すために使用できます。もちろん、2 つのノードを同じオブジェクト インスタンスにポイントすることもできます。
TPerson のリストがあり、それぞれの人を異なるノードに表示したいツリーがあるとします。次に、ノードに使用するレコードを次のように宣言します。
TNodeRecord = record
... // anything else you may need or want
DataObject: TObject;
...
end;
ノードが初期化されるコードでは、次のようにします。
PNodeRecord.DataObject := PersonList[SomeIndex];
それが要点です。上に示したように、一般的な NodeRecord が必要な場合は、さまざまな Get... メソッドで使用するために、適切なクラスにキャストし直す必要があります。もちろん、ツリーごとに特定のレコードを作成することもできます。この場合、DataObject をツリーに表示する特定のタイプのクラスとして宣言します。唯一の欠点は、そのクラスのオブジェクトの情報を表示するようにツリーを制限することです。
どこかに横たわっているより精巧な例が必要です。見つけたら、この回答に追加します。
例
ツリーで使用するレコードを宣言します。
RTreeData = record
CDO: TCustomDomainObject;
end;
PTreeData = ^RTreeData;
TCustomDomainObject は、すべてのドメイン情報の基本クラスです。次のように宣言されています。
TCustomDomainObject = class(TObject)
private
FList: TObjectList;
protected
function GetDisplayString: string; virtual;
function GetCount: Cardinal;
function GetCDO(aIdx: Cardinal): TCustomDomainObject;
public
constructor Create; overload;
destructor Destroy; override;
function Add(aCDO: TCustomDomainObject): TCustomDomainObject;
property DisplayString: string read GetDisplayString;
property Count: Cardinal read GetCount;
property CDO[aIdx: Cardinal]: TCustomDomainObject read GetCDO;
end;
このクラスは、他の TCustomDomainObject インスタンスのリストを保持できるように設定されていることに注意してください。ツリーを表示するフォームに次を追加します。
TForm1 = class(TForm)
...
private
FIsLoading: Boolean;
FCDO: TCustomDomainObject;
protected
procedure ShowColumnHeaders;
procedure ShowDomainObject(aCDO, aParent: TCustomDomainObject);
procedure ShowDomainObjects(aCDO, aParent: TCustomDomainObject);
procedure AddColumnHeaders(aColumns: TVirtualTreeColumns); virtual;
function GetColumnText(aCDO: TCustomDomainObject; aColumn: TColumnIndex;
var aCellText: string): Boolean;
protected
property CDO: TCustomDomainObject read FCDO write FCDO;
public
procedure Load(aCDO: TCustomDomainObject);
...
end;
Load メソッドがすべての始まりです。
procedure TForm1.Load(aCDO: TCustomDomainObject);
begin
FIsLoading := True;
VirtualStringTree1.BeginUpdate;
try
if Assigned(CDO) then begin
VirtualStringTree1.Header.Columns.Clear;
VirtualStringTree1.Clear;
end;
CDO := aCDO;
if Assigned(CDO) then begin
ShowColumnHeaders;
ShowDomainObjects(CDO, nil);
end;
finally
VirtualStringTree1.EndUpdate;
FIsLoading := False;
end;
end;
実際に行うことは、フォームをクリアして、ほとんどの場合、他の CustomDomainObjects を含むリストである新しい CustomDomainObject 用に設定することだけです。
ShowColumnHeaders メソッドは、文字列ツリーの列ヘッダーを設定し、列の数に応じてヘッダー オプションを調整します。
procedure TForm1.ShowColumnHeaders;
begin
AddColumnHeaders(VirtualStringTree1.Header.Columns);
if VirtualStringTree1.Header.Columns.Count > 0 then begin
VirtualStringTree1.Header.Options := VirtualStringTree1.Header.Options
+ [hoVisible];
end;
end;
procedure TForm1.AddColumnHeaders(aColumns: TVirtualTreeColumns);
var
Col: TVirtualTreeColumn;
begin
Col := aColumns.Add;
Col.Text := 'Breed(Group)';
Col.Width := 200;
Col := aColumns.Add;
Col.Text := 'Average Age';
Col.Width := 100;
Col.Alignment := taRightJustify;
Col := aColumns.Add;
Col.Text := 'CDO.Count';
Col.Width := 100;
Col.Alignment := taRightJustify;
end;
AddColumnHeaders は、このフォームをツリーで情報を表示する他のフォームのベースとして使用できるようにするために分離されました。
ShowDomainObjects は、ツリー全体がロードされるメソッドのように見えます。そうではありません。結局のところ、仮想ツリーを扱っています。したがって、仮想ツリーにノードの数を伝えるだけで済みます。
procedure TForm1.ShowDomainObjects(aCDO, aParent: TCustomDomainObject);
begin
if Assigned(aCDO) then begin
VirtualStringTree1.RootNodeCount := aCDO.Count;
end else begin
VirtualStringTree1.RootNodeCount := 0;
end;
end;
これでほとんどの設定が完了し、さまざまな VirtualStringTree イベントを実装するだけですべてを開始できます。実装する最初のイベントは OnGetText イベントです。
procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node:
PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText:
string);
var
NodeData: ^RTreeData;
begin
NodeData := Sender.GetNodeData(Node);
if GetColumnText(NodeData.CDO, Column, {var}CellText) then
else begin
if Assigned(NodeData.CDO) then begin
case Column of
-1, 0: CellText := NodeData.CDO.DisplayString;
end;
end;
end;
end;
VirtualStringTree から NodeData を取得し、取得した CustomDomainObject インスタンスを使用してそのテキストを取得します。これには GetColumnText 関数を使用します。これは、このフォームをツリーを表示する他のフォームのベースとして使用できるようにするために行われました。そのルートに進むときは、このメソッドを仮想として宣言し、子孫のフォームでオーバーライドします。この例では、次のように単純に実装されています。
function TForm1.GetColumnText(aCDO: TCustomDomainObject; aColumn: TColumnIndex;
var aCellText: string): Boolean;
begin
if Assigned(aCDO) then begin
case aColumn of
-1, 0: begin
aCellText := aCDO.DisplayString;
end;
1: begin
if aCDO.InheritsFrom(TDogBreed) then begin
aCellText := IntToStr(TDogBreed(aCDO).AverageAge);
end;
end;
2: begin
aCellText := IntToStr(aCDO.Count);
end;
else
// aCellText := '';
end;
Result := True;
end else begin
Result := False;
end;
end;
ノード レコードから CustomDomainObject インスタンスを使用する方法を VirtualStringTree に指示したので、もちろんメイン CDO のインスタンスをツリーのノードにリンクする必要があります。これは OnInitNode イベントで行われます。
procedure TForm1.VirtualStringTree1InitNode(Sender: TBaseVirtualTree;
ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
ParentNodeData: ^RTreeData;
ParentNodeCDO: TCustomDomainObject;
NodeData: ^RTreeData;
begin
if Assigned(ParentNode) then begin
ParentNodeData := VirtualStringTree1.GetNodeData(ParentNode);
ParentNodeCDO := ParentNodeData.CDO;
end else begin
ParentNodeCDO := CDO;
end;
NodeData := VirtualStringTree1.GetNodeData(Node);
if Assigned(NodeData.CDO) then begin
// CDO was already set, for example when added through AddDomainObject.
end else begin
if Assigned(ParentNodeCDO) then begin
if ParentNodeCDO.Count > Node.Index then begin
NodeData.CDO := ParentNodeCDO.CDO[Node.Index];
if NodeData.CDO.Count > 0 then begin
InitialStates := InitialStates + [ivsHasChildren];
end;
end;
end;
end;
Sender.CheckState[Node] := csUncheckedNormal;
end;
CustomDomainObject は他の CustomDomainObjects のリストを持つことができるため、lsit の Count がゼロより大きい場合に HasChildren を含めるようにノードの InitialStates も設定します。これは、ユーザーがツリーのプラス記号をクリックしたときに呼び出される OnInitChildren イベントも実装する必要があることを意味します。繰り返しますが、そこで行う必要があるのは、準備する必要があるノードの数をツリーに伝えることだけです。
procedure TForm1.VirtualStringTree1InitChildren(Sender: TBaseVirtualTree; Node:
PVirtualNode; var ChildCount: Cardinal);
var
NodeData: ^RTreeData;
begin
ChildCount := 0;
NodeData := Sender.GetNodeData(Node);
if Assigned(NodeData.CDO) then begin
ChildCount := NodeData.CDO.Count;
end;
end;
以上です!
簡単なリストの例を示したように、どのデータ インスタンスをどのノードにリンクする必要があるかを把握する必要がありますが、どこでそれを行う必要があるかについては、かなりのアイデアが必要です: OnInitNode イベントを設定する場所選択した CDO インスタンスを指すノード レコードの CDO メンバー。