VirtualStringTree - stored pointer <> Pointer received! Is my coding approach wrong?

I use TVirtualStringTree to store pointers to records.

Initially, there is a TList that contains a list of entries.

I use the OnInitNode event to iterate through a TList and assign the data of each record to the nodes of the tree.

However, when retrieving the data associated with the node in the OnNewText event handler, the returned pointer has a different address than the one that was originally stored in the tree.

In addition, through debugging, you can see that the pointer (to the record data) obtained from the node does not match the one that was originally stored in the node. I need to save the changed data to the database and you will need to reference the record with the changed data. It should be simple as a reference to a pointer, but the problem is that the pointer is not the same.

I'm not sure what I'm doing wrong, and I hope someone can help me fix this.

Thanks in advance.

Here is my code:

Data structure and declarations:

  TTherapData = record TherapID: Integer; TherapName: String[120]; TherapInstr: String[120]; Selected_DB: Byte; Selected: Byte; end; PTherapData = ^TTherapData; FTherapDataList: TList<PTherapData>; FTherapDataListAsg_Iter: Integer; vstRxList_Asg: TVirtualStringTree; 

Loading data into a TList and then into the tree:

 procedure TfmPatient_Conslt.LoadTherapList(const ADBLoad: Boolean = False); var TherapData: PTherapData; d, x: Integer; begin datamod.uspLKTHERAP_S.First; while not datamod.uspLKTHERAP_S.Eof do begin New(TherapData); TherapData^.TherapID := datamod.uspLKTHERAP_SROW_ID.AsInteger; TherapData^.TherapName := datamod.uspLKTHERAP_SIMPRTHERAP.AsString; TherapData^.TherapInstr := EmptyStr; TherapData^.Selected := 0; TherapData^.Selected_DB := 0; FTherapDataList.Add(TherapData); datamod.uspLKTHERAP_S.Next; end; datamod.uspCONSLT_RX_S.First; while not datamod.uspCONSLT_RX_S.Eof do begin d := datamod.uspCONSLT_RX_SRX_ID.AsInteger; TherapData := FTherapDataList[TherapDataList_GetIndexOfID(d)]; TherapData^.TherapInstr := datamod.uspCONSLT_RX_SRX_INSTRUCTION.AsString; TherapData^.Selected := 1; TherapData^.Selected_DB := 1; datamod.uspCONSLT_RX_S.Next; end; x := TherapDataList_CountSelectedItems; FTherapDataListAsg_Iter := 0; vstRxList_Asg.NodeDataSize := SizeOf(TTherapData); vstRxList_Asg.RootNodeCount := 0; vstRxList_Asg.RootNodeCount := x; end; procedure TfmPatient_Conslt.vstRxList_AsgInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); var TherapData: PTherapData; begin TherapData := Sender.GetNodeData(Node); while (FTherapDataList[FTherapDataListAsg_Iter]^.Selected <> 1) do Inc(FTherapDataListAsg_Iter); TherapData^.TherapID := FTherapDataList[FTherapDataListAsg_Iter]^.TherapID; TherapData^.TherapName := FTherapDataList[FTherapDataListAsg_Iter]^.TherapName; TherapData^.TherapInstr := FTherapDataList[FTherapDataListAsg_Iter]^.TherapInstr; { TherapData := FTherapDataList[FTherapDataListAsg_Iter]; } // { TherapData^ := FTherapDataList[FTherapDataListAsg_Iter]^; } // Inc(FTherapDataListAsg_Iter); end; procedure TfmPatient_Conslt.vstRxList_AsgGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var TherapData: PTherapData; begin TherapData := Sender.GetNodeData(Node); if Assigned(TherapData) then if (Column = 0) then CellText := TherapData^.TherapName else if (Column = 1) then CellText := TherapData^.TherapInstr; end; procedure TfmPatient_Conslt.vstRxList_AsgEditing(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; var Allowed: Boolean); begin Allowed := (Column = 1); end; 

Data extraction. I noticed a problem here:

 procedure TfmPatient_Conslt.vstRxList_AsgNewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: string); var TherapData: PTherapData; begin if (Column = 1) then begin TherapData := Sender.GetNodeData(Node); if Assigned(TherapData) then // <---- There is a debug breakpoint here // and the watch window screen-shot // is taken here TherapData^.TherapInstr := NewText; // Showmessage(Format('%p', [TherapData])); // <---- The pointer value is not the same // as that originally stored ! end; end; 

Here I save the list data in the database and the reason why I need a tree to change the source data, not the copy:

 procedure TfmPatient_Conslt.SaveRxListToDB; var TherapData: PTherapData; begin for TherapData in FTherapDataList do begin if (TherapData^.Selected = 1) and (TherapData^.Selected_DB = 0) then begin // Add new entries to DB // :ROW_ID, :CONSLT_ID, :RX_ID, :RX_INSTRUCTION datamod.uspCONSLT_RX_I.ParamByName('ROW_ID').AsInteger := 0; datamod.uspCONSLT_RX_I.ParamByName('CONSLT_ID').AsInteger := FConsultationID; datamod.uspCONSLT_RX_I.ParamByName('RX_ID').AsInteger := TherapData^.TherapID; datamod.uspCONSLT_RX_I.ParamByName('RX_INSTRUCTION').AsString := TherapData^.TherapInstr; datamod.uspCONSLT_RX_I.PrepareSQL(False); datamod.uspCONSLT_RX_I.ExecProc; TherapData^.Selected_DB := 1; end else if (TherapData^.Selected = 1) and (TherapData^.Selected_DB = 1) then begin // Update existing DB entries // :CONSLT_ID, :RX_ID, :RX_INSTRUCTION datamod.uspCONSLT_RX_U.ParamByName('CONSLT_ID').AsInteger := FConsultationID; datamod.uspCONSLT_RX_U.ParamByName('RX_ID').AsInteger := TherapData^.TherapID; datamod.uspCONSLT_RX_U.ParamByName('RX_INSTRUCTION').AsString := TherapData^.TherapInstr; datamod.uspCONSLT_RX_U.PrepareSQL(False); datamod.uspCONSLT_RX_U.ExecProc; end else if (TherapData^.Selected = 0) and (TherapData^.Selected_DB = 1) then begin // Delete removed entries from DB // :CONSLT_ID, :RX_ID datamod.uspCONSLT_RX_D.ParamByName('CONSLT_ID').AsInteger := FConsultationID; datamod.uspCONSLT_RX_D.ParamByName('RX_ID').AsInteger := TherapData^.TherapID; datamod.uspCONSLT_RX_D.PrepareSQL(False); datamod.uspCONSLT_RX_D.ExecProc; TherapData^.Selected_DB := 0; end; end; end; 

Here is a screenshot from the Debug-> Watch List window:

enter image description here

+5
source share
1 answer

To answer the question in the title, I would say yes, your approach to coding is incorrect.

The error is that you do not save a pointer to writing data to the VT node, you allocate an entire (separate) TTherapData record for each node! So "error" is a string

 vstRxList_Asg.NodeDataSize := SizeOf(TTherapData); 

in the TfmPatient_Conslt.LoadTherapList method. Perhaps you need an extra record that contains a pointer to a data record:

 type TVTNodeData = record TherapData: PTherapData; end; PVTNodeData = ^TVTNodeData; 

and use this record as a node data record:

 vstRxList_Asg.NodeDataSize := SizeOf(TVTNodeData); 

and node init becomes something like

 procedure TfmPatient_Conslt.vstRxList_AsgInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates); var NodeData: PVTNodeData; begin NodeData := Sender.GetNodeData(Node); while (FTherapDataList[FTherapDataListAsg_Iter]^.Selected <> 1) do Inc(FTherapDataListAsg_Iter); NodeData^.TherapData := FTherapDataList[FTherapDataListAsg_Iter]; Inc(FTherapDataListAsg_Iter); end; 

and using data in other tree events such as

 procedure TfmPatient_Conslt.vstRxList_AsgGetText(Sender: TBaseVirtualTree; Node:PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var NodeData: PVTNodeData; TherapData: PTherapData; begin NodeData := Sender.GetNodeData(Node); if Assigned(NodeData) and Assigned(NodeData.TherapData) then begin TherapData := NodeData.TherapData; if (Column = 0) then CellText := TherapData^.TherapName else if (Column = 1) then CellText := TherapData^.TherapInstr; end; end; 
+5
source

Source: https://habr.com/ru/post/1206353/


All Articles