1

I am not sure how to assign data to a node in a VirtualStringTree. I'm need to assign a pointer to a record object to the Node's Data property in the tree control's InitNode event. However I'm getting a 'Pointer type required' compile-time error.

type
  TDiagData = record
    DiagID: Integer;
    DiagName: String;
    Selected: Byte;
  end;

  PDiagData = ^TDiagData;

var
  FDiagDataList: TObjectList;
  c: Integer; // used as an iterator for the list // incremented in vst1InitNode



procedure Btn1Click;
var
  DiagData : PDiagData;
begin

  try
    FDiagDataList := TObjectList.Create; // TODO: Move this to form constructor

    for c := 1 to 10 do
    begin
      New(DiagData);

      DiagData.DiagID := c;
      DiagData.DiagName := Format('Diag# %d', [c]);

      FDiagDataList.Add(DiagData);
    end;

    c := 0;

    vst1.NodeDataSize := SizeOf(TDiagData);
    vst1.RootNodeCount := 10; // test

  finally
    //  FDiagDataList.Free;   //TODO: Move this to form destructor
  end
end;

procedure vst1InitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
  var InitialStates: TVirtualNodeInitStates);
var
  DiagData: PDiagData;
begin

  DiagData = TDiagData(FDiagDataList.Items[c]); // FDiagDataList is a TObjectlist

  Node.Data^ := DiagData; // <--- this is not working ..
  // The error is: Pointer type required.

  Inc(c);
end;

I need to assign the data to the node in the InitNode event, but not am sure how to assign it.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Steve F
  • 1,527
  • 1
  • 29
  • 55
  • I can guess what the compiler error is, but to be didactic, can you please edit the question to include the compiler error. I actually think that you have two distinct errors here. But it would be better for you to tell us what they are. – David Heffernan Oct 29 '14 at 14:49
  • 1
    `DiagData := Sender.GetNodeData(Node); DiagData.DiagID := ...` or `DiagData := Sender.GetNodeData(Node); DiagData^ := FDiagDataList.Items[c]` but what is `c` ? – TLama Oct 29 '14 at 14:49

1 Answers1

2

Do not read or write Node.Data directly. The data you need won't necessarily be exactly at the address of that field. (The tree control has a mechanism for allowing descendants to reserve additional data for themselves.) Instead, call Sender.GetNodeData.

var
  NodeData: PDiagData;
begin
  NodeData := Sender.GetNodeData(Node);
  NodeData^ := TDiagData(FDiagDataList.Items[c]);
end;

Your code fails because Node.Data has type record; you cannot dereference it with ^. In the simple case, the value returned by GetNodeData will be equal to the address of that field (i.e., GetNodeData(Node) = @Node.Data). But don't assume all cases are simple. As I said, tree-control descendants can reserve data space of their own, so you're sharing that space with code that's outside your control, and it's up to the tree control to manage which data space is yours. Always call GetNodeData.


Furthermore, you're confused about your data types. You say FDiagDataList is a TObjectList, but you're clearly storing something in it that isn't a descendant of TObject. When you're not using objects, don't use TObjectList. If you're using a version of Delphi earlier than 2009, then use TList and store pointers to TDiagData:

NodeData^ := PDiagData(FDiagDataList[c])^;

If you're using Delphi 2009 or later, then use TList<TDiagData>, and then get rid of the type cast:

NodeData^ := FDiagDataList[c];

Either way, you'll probably find things easier to manage if every event handler starts out the same way, with a call to GetNodeData to fetch the type-safe pointer to the current node's data.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Eeek! That `c` is still with us. – TLama Oct 29 '14 at 17:03
  • So what, @Tlama? We could replace it with some meta-expression, *`expression-that-selects-data-for-current-node`*, but we can also assume that *`c`* already *is* such an expression. Why be wordier? – Rob Kennedy Oct 29 '14 at 17:18
  • @RobKennedy: Good and relevant answer. – Steve F Oct 29 '14 at 17:36
  • @RobKennedy, I would expect you to explain what the c can be in that context. Steve, what is that c in real ? – TLama Oct 29 '14 at 18:31
  • @RobKennedy: Why do you say "NodeData^ := FDiagDataList[c];"? Shouldn't it be "NodeData := FDiagDataList[c];"? – Steve F Nov 06 '14 at 15:19
  • Definitely not, Steve. I already assigned that *local variable* on the previous line. Now I want to assign the memory that the local variable *points at*. – Rob Kennedy Nov 06 '14 at 15:29
  • @RobKennedy Hi Rob, there's something really wrong with my code and the pointers / addresses. I'm making a new question now. – Steve F Nov 06 '14 at 15:38