2014-11-06 11 views
5

Sto usando una TVirtualStringTree per archiviare i puntatori ai record.VirtualStringTree - Puntatore memorizzato <> Puntatore recuperato! Il mio approccio al codice è sbagliato?

Originariamente c'è un TList che contiene l'elenco dei record.

Sto utilizzando l'evento OnInitNode per scorrere il TList e assegnare i dati di ogni record ai nodi dell'albero.

Tuttavia, quando si recuperano i dati associati a un nodo nel gestore eventi OnNewText, il puntatore restituito ha un indirizzo diverso da quello memorizzato in origine nell'albero.

Inoltre, è possibile vedere attraverso il debug che il puntatore (ai dati del record) recuperato dal nodo è non lo uguale a quello memorizzato originariamente nel nodo. Devo salvare i dati modificati in un database e devo fare riferimento al record con i dati modificati. Dovrebbe essere semplice come riferimento al puntatore, ma il problema è che il puntatore non è lo stesso.

Non sono sicuro di cosa sto sbagliando e spero che qualcuno possa aiutarmi a risolverlo.

Grazie in anticipo.

Ecco il mio codice:

struttura dei dati e dichiarazioni:

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; 

Caricamento dei dati nel TList, e poi contro l'albero:

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; 

il recupero dei dati. Ho notato che il problema qui:

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; 

Questo è dove ho salvare i dati della lista al database, e la ragione per cui ho bisogno l'albero per modificare le dati originali e non una copia:

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; 

Ecco uno screenshot della finestra dalla Debug-> selezione:

enter image description here

+1

Oh, finalmente abbiamo ottenuto ['a c'] (http://stackoverflow.com/questions/26633391/how-to-assign-data-to-node-of-virtualstringtree-in-initnode-event/26636017# comment41883233_26636017) :) – TLama

+0

@TLama :) Sì. Che viaggio lungo e strano è stato .. –

risposta

5

per rispondere a la domanda nel titolo, direi di sì, il tuo approccio al codice è sbagliato.

L'errore è che non si salva un puntatore al record di dati sul nodo di VT, si sta allocando un record (separato) TTherapData completo per ciascun nodo! Così l ' "errore" è la linea

vstRxList_Asg.NodeDataSize := SizeOf(TTherapData); 

nel metodo TfmPatient_Conslt.LoadTherapList.Che probabilmente si desidera è un ulteriore record che detiene il puntatore al record di dati:

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

e l'uso che record come record di dati nodo:

vstRxList_Asg.NodeDataSize := SizeOf(TVTNodeData); 

e il nodo di init diventa qualcosa di simile

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; 

e utilizzando i dati in altri eventi ad albero come

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; 
+1

Grazie. Questo mi ha illuminato per davvero. –

+0

Devo liberare la memoria allocata per i nodi (NodeData) nel gestore di eventi OnFreeNode? Se é cosi, come? –

+1

No, il VT gestisce la memoria del suo set di dati. Tuttavia, VT non finalizza il record di dati dell'utente, quindi se il tuo record di dati del nodo (TVTNodeData) conterrà tipi gestiti come stringa o interfaccia, sarà necessario finalizzarli, cioè impostare i campi stringa su "(stringa vuota), interfacce su" nil' ecc. Ma nel tuo caso c'è solo un campo puntatore che non ha bisogno di finalizzazione (supponendo che tu liberi la memoria allocata per i record di 'PTherapData' quando liberi la' FTherapDataList'). – ain

Problemi correlati