VirtualStringTree -在使用对象时添加/处理子节点/子节点的正确方法



我正在使用Delphi2010,并试图使用VirtualStringTree来包装我的头。

我一直试图让它与对象的工作,并没有运气,直到我跟随Philipp Frenzel的虚拟树视图教程,我发现在softgems.net网站。到目前为止,我所提出的工作,但我不认为我正确地处理子节点(即子节点)。

我唯一能做的就是为每个孩子链接整个对象,然后只显示我需要的字段-但它只是感觉错了。

建议/反馈非常感谢。


我有对象列表,我试图与VirtualStringTree连接,我试图实现这样的东西,其中一个字段将作为标签的父和字段的其余部分显示为子节点。

  • 罗伯特·雷恩
      男性35
    • 洛杉矶
    • 黑发
  • Jane Doe
      女性
    • 红发人

我的课就是这样设置的。

type
  PTreeData = ^TTreeData;
  TTreeData = record
    FObject : TObject;
  end;
  TCustomerNode = class(TObject)
  private
    fName: string;
    fSex: string;
    fAge: integer;
    fHair: string;
    //...
  public
    property Name: string read fName write fName;
    //...
  end;

一旦我填充了对象,我将它们添加到基于下面提到的TList的另一个类(CustomerObjectList)中。

这里是我连接VirtualStringTree与我的对象列表

procedure TfrmMain.btnLoadDataClick(Sender: TObject);
var
  i, j : integer;
  CustomerDataObject: TCustomerNode;
  RootXNode, XNode: PVirtualNode;
  Data: PTreeData;
begin
  vstree.NodeDataSize := SizeOf( TTreeData );
  vstree.BeginUpdate;
  for i := 0 to CustomerObjectList.Count - 1 do
  begin
    CustomerDataObject := CustomerObjectList[i];
    //load data for parent node
    RootXNode := vstree.AddChild(nil);
    Data  := vstree.GetNodeData(RootXNode);
    Data^.FObject:= PINodeSource;
    //now add children for rest of fields
    //Isn't there a better way to do this?
    for j := 1 to NUMBERofFIELDS -1 do  //first field is label for parent so -1
    begin
      XNode := vstree.AddChild(RootXNode);
      Data  := vstree.GetNodeData(XNode);
      Data^.FObject:= PINodeSource;
    end;
  end;
  vstree.EndUpdate; 
end;    
procedure TfrmMain.vstreeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
 Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
  Data : PTreeData;
begin
  Data := vstObjects.GetNodeData(Node);
  ////if Node.ChildCount  = 0 then //edit - oops typo!
  if Node.ChildCount  > 0 then
  begin
    CellText := TCustomerNode(Data.FObject).Name;
    exit;
  end;
  //handle childnodes
  case Node.Index of
    0:  CellText := TCustomerNode(Data.FObject).Sex;
    1:  CellText := IntToStr(TCustomerNode(Data.FObject).Age);
    2:  CellText := TCustomerNode(Data.FObject).Hair;
    3:  CellText := TCustomerNode(Data.FObject).City;
  end;  
end;

您不需要将所有数据加载到树中。你可以使用树的"虚拟性"。我是这样做的。

将树的RootNodeCount设置为CustomerObjectList中的记录数:

vstree.RootNodeCount := CustomerObjectList.Count;

然后,在OnInitChildren事件中,将0级节点的子节点数设置为您想要显示的属性数:

procedure TfrmMain.vstreeInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
begin
  if Sender.GetNodeLevel(Node) = 0 then
  begin
    Sender.ChildCount[Node] := 4;
    // Comment this out if you don't want the nodes to be initially expanded
    Sender.Expanded[Node] := TRUE;
  end;
end;

现在,只需在OnGetText事件中检索正确的数据。

procedure TfrmMain.vstreeGetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
begin
  if Column <= 0 then
  begin
    if Sender.GetNodeLevel(Node) = 0 then
      CellText := CustomerObjectList[Node.Index].Name else
    if Sender.GetNodeLevel(Node) = 1 then
    begin
      case Node.Index of
        0: CellText := CustomerObjectList[Parent.Node.Index].Sex;
        1: CellText := CustomerObjectList[Parent.Node.Index].Age;
        2: CellText := CustomerObjectList[Parent.Node.Index].Hair;
        3: CellText := CustomerObjectList[Parent.Node.Index].City;
      end; // of case
     end;
end;

你可能想要添加更多的范围检查,以防万一。

我唯一能做的就是把整个联系起来为每个子节点再次调用object in,然后只显示我需要的字段-但感觉太不对了…建议/反馈非常感谢。

你必须这样做,因为你在混音。

您的树被设置为期望每个级别的对象实例。但是你的实例列表只在主级有对象,在子级有对象属性。

就所有的意图和目的而言,这并没有什么错。

如果你真的想避免它,你将不得不使用一个复合模式,其中一个对象拥有一个属性列表,而这些属性本身又是对象。可以工作,但也会使处理类变得非常混乱。尽管您总是可以通过使用索引属性来提供对所拥有属性的访问。

TSomeObject = class(TObject)
private
  MyObjects: TList<TSomeObject>;
protected
  function GetStringProperty(const aIndex: Integer): string;
public
  property Name: string index 1 read GetStringProperty;
end;

这被称为实体属性值模型。(数据表示的EAV/CR模型)它提供了灵活性,但也有缺点,特别是如果你要以类似的方式构建你的数据库。

最新更新