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:
