Ho una TTreeView che popolo senza problemi, ma quando vado per eliminare un elemnto mi va in errore dicendo
<progetto> raised exception class 'external: sigsegv'.
E mi si ferma alla riga 392 di treeview.inc
Result:=NodeState in FStates;
Il codice di eliminazione è il seguente che l'ho trovato sul wiki:
procedure TfrmMain.btnDelHostClick(Sender: TObject);
Procedure DeleteNode(Node:TTreeNode);
begin
while Node.HasChildren do DeleteNode(Node.GetLastChild);
lstHosts.Items.Delete(Node);
end;
begin
if lstHosts.Selected = nil then exit;
If lstHosts.Selected.HasChildren then
if MessageDlg('Delete node and all children ?',mtConfirmation,
[mbYes,mbNo],0) <> mrYes then exit;
DeleteNode(lstHosts.Selected);
end;
Debuggando va in crash alla riga
lstHosts.Items.Delete(Node);
Idee?
Rispondo solo ora che il progetto l'ho in ufficio e nel weekend non potevo provare.
Sul tuo appunto ho provato a fare un debug passo passo e l'errore esce prima ancora che cominci a eliminare il nodo, sembra quasi non riesca a leggere il lstHosts.Selected
Confermo è quello, ho forzato un
DeleteNode(lstHosts.Items[1]);
E li l'errore non esce
Ehm ... aggiornamento ... io mi sto perdendo, ha funzionato una volta e poi ora da errore anche così.
Aggiungo una nota, se prima della cancellazione provo a leggere il nodo quello va senza problemi
ShowMessage(Node.Text);
lstHosts.Items.Delete(Node);
Lo shomessage mi mostra correttamente il testo del nodo ma poi al delete va in errore.
No, non ho provato lo snapshot, se a voi funziona con la stessa versione è qualcosa che sbaglio, devo cercare di capire cos'è.
Ho trovato il barbatrucco, il pulsante che uso per eliminare la voce è disabilitato di default, nell'evento selectionchanged della treeview ho aggiunto il codice per abilitarmi il pulsante solo se in precedenza ho selezionato una voce
Remmando questo comando e lasciando il pulsante abilitato di default tutto funziona correttamente senza errori.
Se avete voglia di ripetere la situazione per vedere se anche a voi esce l'errore basta che fate un nuovo progetto, aggiungete un controllo treeview e un pulsante che sia disabilitato di default, nell'evento create del form popolate la treeview con dei dati a casa
procedure TfrmMain.FormCreate(Sender: TObject);
begin
lstHosts.Items.Add(nil, 'uno');
lstHosts.Items.Add(nil, 'due');
lstHosts.Items.Add(nil, 'tre');
lstHosts.Items.Add(nil, 'quattro');
lstHosts.Items.Add(nil, 'cinque');
end;
Ora nell'evento selectionchanged della treeview mettete l'abilitazione del pulsante
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
btnDeleteItem.Enabled:=lstHosts.Selected.Selected;
end;
E infine la funzione al pulsante di eliminazione
procedure TfrmMain.btnDeleteItemClick(Sender: TObject);
procedure DeleteNode(Node:TTreeNode);
begin
while Node.HasChildren do DeleteNode(Node.GetLastChild);
lstHosts.Items.Delete(Node);
end;
begin
if lstHosts.Selected = nil then exit;
If lstHosts.Selected.HasChildren then
if MessageDlg('Delete node and all children ?', mtConfirmation
, [mbYes,mbNo], 0) <> mrYes then exit;
DeleteNode(lstHosts.Selected);
end;
Eseuite e provate a eliminare una voce, se vi crasha anche a voi remmate l'istruzione nel selectionchanged e abilitate di default il pulsante e riprovate ora non da + errori e cancella giusto.
Qualcuno sa spiegarmi come mai ?
Intando ho trovato come farlo funzionare ugualmente anche con l'abilitazione del pulsante anche se ancora non capisco il perchè nell'altra maniera non vada e faccia crashare tutto:
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
btnDeleteItem.Enabled:=not(lstHosts.Selected = nil);
end;
Perché con lstHosts.Selected.selected fai riferimento ad un oggetto (lstHosts.Selected) che hai appena cancellato :)
Probabilmente potresti anche fare qualcosa del tipo:
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
btnDeleteItem.Enabled := lstHosts.items.SelectionCount > 0;
end;
Il fatto è che al momento della cancellazione del nodo l'evento viene generato di nuovo; a questo punto avviene l'errore. Prova a modificare la funzione così:
procedure TForm1.lstHostsSelectionChanged(Sender: TObject);
begin
ShowMessage('sending 1');
btnDeleteItem.Enabled:=lstHosts.Selected.Selected;
ShowMessage('sending 2');
end;
Tenendo conto di questo, ad esempio, così funziona:
procedure TForm1.lstHostsSelectionChanged(Sender: TObject);
begin
if lstHosts.Selected <> nil then
btnDeleteItem.Enabled:=lstHosts.Selected.Selected
else
btnDeleteItem.Enabled:=false;
end;
Come ho scritto sopra ho già trovato come farla funzionare con il codice
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
btnDeleteItem.Enabled:=not(lstHosts.Selected = nil);
end;
Volevo solo cercare di capire come mai quella che avevo usato la prima volta faceva collassare tutto ANCORA prima di cancellare il nodo.
Comunque non importa, grazie lo stesso.
Come ho scritto sopra ho già trovato come farla funzionare con il codice
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
btnDeleteItem.Enabled:=not(lstHosts.Selected = nil);
end;
Sì, ma assegni la negazione di un'identità quando basterebbe assegnare la non-identità. Questa:
procedure TfrmMain.lstHostsSelectionChanged(Sender: TObject);
begin
btnDeleteItem.Enabled:= lstHosts.Selected <> nil;
end;
è meno ridondante. :)
Volevo solo cercare di capire come mai quella che avevo usato la prima volta faceva collassare tutto ANCORA prima di cancellare il nodo.
Comunque non importa, grazie lo stesso.
Prova questo con il codice che dava errore, tanto per vedere che in effetti il nodo va a nil:
procedure TForm1.lstHostsSelectionChanged(Sender: TObject);
begin
Form1.Caption := IntToStr(integer(pointer(lstHosts.Selected)));
btnDeleteItem.Enabled := lstHosts.Selected.Selected;
end;
Nella caption del form c'è l'indirizzo di memoria del nodo selezionato. Nel momento in cui viene cancellato, il valore va a 0 immediatamente prima di andare in SIGSEGV, indice del free del nodo.