Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Acceder al "data" de un treeview (https://www.clubdelphi.com/foros/showthread.php?t=78975)

newtron 31-05-2012 13:03:18

Acceder al "data" de un treeview
 
Hola a tod@s.

Tengo un problema al intentar acceder al "data" de un treeview, lo creo de la siguiente manera:

Código Delphi [-]
procedure TFormFamilias.ActualizaArbol;
var
Nodo0,Nodo1,Nodo2,Nodo3,Nodo4: TTreeNode;
N: SmallInt;
begin
  Screen.Cursor:=crHourGlass;
  TreeView1.Items.Clear;
  Nodo0:=TreeView1.Items.AddChild(nil, 'Familias');
  DataModule1.EDBQuery1.SQL.Clear;
  DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO');
  DataModule1.EDBQuery1.ExecSQL;
  while not DataModule1.EDBQuery1.eof do begin
    N:=Length(DataModule1.EDBQuery1.FieldByName('CODIGO').AsString);
    case N of
      3: begin
        Nodo1:=TreeView1.Items.AddChild(Nodo0, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString);
        Nodo1.Data := Pointer(DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString);
      end;
      6: begin
        Nodo2:=TreeView1.Items.AddChild(Nodo1, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString);
        Nodo2.Data := Pointer(DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString);
      end;
      9: begin
        Nodo3:=TreeView1.Items.AddChild(Nodo2, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString);
        Nodo3.Data := Pointer(DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString);
      end;
      12: begin
        Nodo4:=TreeView1.Items.AddChild(Nodo3, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString);
        Nodo4.Data := Pointer(DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString);
      end;
    end;
    DataModule1.EDBQuery1.Next;
  end;
  Screen.Cursor:=crDefault;
end;

El problema es que después quiero acceder al valor del data, por ejemplo en el evento click del treeview pero no me da el valor correcto. He estado echando un vistazo a los post del foro y no encuentro dónde puede estar el problema.

He probado de dos maneras:

Código Delphi [-]
procedure TFormFamilias.TreeView1Click(Sender: TObject);
var
Nodo: TTreeNode;
begin
  Nodo:=TreeView1.Selected;
  ShowMessage(String(Nodo.Data));
end;

con este código salen cosas raras que no tienen nada que ver con el dato que debe de tener almacenado.

Código Delphi [-]
procedure TFormFamilias.TreeView1Click(Sender: TObject);
var
Nodo: TTreeNode;
begin
  Nodo:=TreeView1.Selected;
  ShowMessage(String(Nodo.Data^));
end;

con este otro código o no sale nada o da un access violation error

Agradeceré cualquier ayuda.

Gracias y un saludo

ecfisa 31-05-2012 13:18:54

Hola amigo. :)

En principio veo que tendrías que cambiar:
Código Delphi [-]
  ...
  DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO');
  DataModule1.EDBQuery1.ExecSQL;
  ...
Por:
Código Delphi [-]
  ...
  DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO');
  DataModule1.EDBQuery1.Open;
  ...

Saludos.

newtron 31-05-2012 13:21:56

Cita:

Empezado por ecfisa (Mensaje 433908)
Hola amigo. :)

En principio veo que tendrías que cambiar:
Código Delphi [-] ... DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO'); DataModule1.EDBQuery1.ExecSQL; ...

Por:
Código Delphi [-] ... DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO'); DataModule1.EDBQuery1.Open; ...


Saludos.

¿y eso? ¿qué diferencia hay entre una forma y otra?

Casimiro Notevi 31-05-2012 13:30:42

¿Quieres creer que nunca he usado esa propiedad "data" de un ttreenode?.

Me parece que ayer mismo había un hilo que trataba sobre ese asunto, a ver si lo encuentro.

olbeup 31-05-2012 13:34:43

La diferencia es la siguiente

Este no te devuelve nada, la consulta la realiza internamente
Código Delphi [-]
DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO'); DataModule1.EDBQuery1.ExecSQL;
Este te devuelve la consulta del query con todos los campos
Código Delphi [-]
DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO'); DataModule1.EDBQuery1.Open;
Un saludo.

ecfisa 31-05-2012 13:34:59

Cita:

Empezado por newtron (Mensaje 433909)
¿y eso? ¿qué diferencia hay entre una forma y otra?

Bueno, es que cuando se trata de una consulta (que devolverá datos) se debe utilizar Open o Active. ExecSQL se utiliza cuando se manipula algún dato (inserta, modifica, borra, etc).

Saludos. :)

Casimiro Notevi 31-05-2012 13:36:31

Aquí está, no sé si te servirá.

Aunque depende de los componentes que uses, lo normal es .Open para los select (te devuelve registros) y .ExecSQL para insert, update, etc. (ejecuta una acción, pero no devuelve registros)

Casimiro Notevi 31-05-2012 13:37:23

Vale, ya me callo, que parezco un loro repitiendo lo que dice ecfisa.

p.s. Es que no lo había visto, no vayáis a pensar que soy un loro de verdad.

ecfisa 31-05-2012 13:44:29

Cita:

Empezado por Casimiro Notevi (Mensaje 433914)
Vale, ya me callo, que parezco un loro repitiendo lo que dice ecfisa.

p.s. Es que no lo había visto, no vayáis a pensar que soy un loro de verdad.

Entonces también debo tener algo de papagayo (o de ciego), me pasa bastante seguido... :D

Saludos. :)

newtron 31-05-2012 13:51:45

Gracias por vuestra aclaración pero imagino que será por la base de datos las dos instrucciones me hacen exactamente lo mismo. Miraré en la documentación de la base de datos a ver qué diferencias tiene.

Casimiro, ya había visto ese post pero me parecía más simple como se hace en este que es algo parecido a lo que yo estoy haciendo.

Intentaré hacerlo de la manera que se comenta en tu post a ver cómo va.

Gracias a todos.

newtron 31-05-2012 14:01:09

Solucionado
 
Hola de nuevo. He modificado el código en función al post que me comentaba Casimiro y funciona correctamente. Se ha quedado de la siguiente forma:

Código Delphi [-]
procedure TFormFamilias.ActualizaArbol;
var
Nodo0,Nodo1,Nodo2,Nodo3,Nodo4: TTreeNode;
N: SmallInt;
MiClase: TMiClase;
begin
  Screen.Cursor:=crHourGlass;
  TreeView1.Items.Clear;
  Nodo0:=TreeView1.Items.AddChildObject(nil, 'Familias', MiClase);
  DataModule1.EDBQuery1.SQL.Clear;
  DataModule1.EDBQuery1.SQL.Add('SELECT * FROM FAMILIAS ORDER BY CODIGO');
  DataModule1.EDBQuery1.Open;
  DataModule1.EDBQuery1.First;
  while not DataModule1.EDBQuery1.eof do begin
    N:=Length(DataModule1.EDBQuery1.FieldByName('CODIGO').AsString);
    MiClase:= TMiClase.Create;
    MiClase.Codigo :=DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString;
    case N of
      3: begin
        Nodo1:=TreeView1.Items.AddChildObject(Nodo0, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString, MiClase);
      end;
      6: begin
        Nodo2:=TreeView1.Items.AddChildObject(Nodo1, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString, MiClase);
      end;
      9: begin
        Nodo3:=TreeView1.Items.AddChildObject(Nodo2, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString, MiClase);
      end;
      12: begin
        Nodo4:=TreeView1.Items.AddChildObject(Nodo3, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString, MiClase);
      end;
    end;
    DataModule1.EDBQuery1.Next;
  end;
  Screen.Cursor:=crDefault;
end;

y en el evento OnClick

Código Delphi [-]
procedure TFormFamilias.TreeView1Click(Sender: TObject);
var
Nodo: TTreeNode;
begin
  Nodo:=TreeView1.Selected;
  ShowMessage(TMiClase(Nodo.Data).Codigo)
end;



Gracias a todos y un saludo

Edito: para que veais que soy bueno he cambiado el ExecSql por el Open :P

Casimiro Notevi 31-05-2012 14:06:38

Cita:

Empezado por newtron (Mensaje 433919)
Edito: para que veais que soy bueno he cambiado el ExecSql por el Open :P

En caso contrario no habrías podido dormir tranquilo nunca más :)

ecfisa 31-05-2012 14:15:26

Hola newtron.

No cambies todo todavía, primero revisá esta prueba reducida, usando básicamente tu código y que funciona correcto.

Saludos. :)

Edito: ¿ Ves lo que te decía Casimiro? (no ví el mensaje que ya estaba solucionado :D)

LoPiTaL 31-05-2012 15:17:03

Un apunte de última hora, y no es por ir de "listillo" :P y por favor, corregidme si me equivoco....

El problema que tenías originalmente, era con acceder a los datos del campo "Data" del TreeView, no con la base de datos, ¿correcto?

Tú hacías lo siguiente (lo pongo de nuevo, ya que hace tropecientos mensajes que pasó):

Código Delphi [-]
Nodo1.Data := Pointer(DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString);

Y al hacer:

Código Delphi [-]
ShowMessage(String(Nodo.Data^));

no te funcionaba.

Pues bien, yo creo, independientemente de todo el tema de bases de datos que habéis estado discutiendo (del cual, yo no tengo ni idea), que no te funciona porque el tipo "string" es un tipo manejado, creado y destruido por Delphi cuando se queda sin referencias. Si haces un casting a Pointer, la referencia se pierde y Delphi te destruye la string nada más salir de la función, pudiendo ser reutilizado su espacio.

Por eso tu otra solución SI funciona:

Código Delphi [-]
MiClase.Codigo :=DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString;
Nodo1:=TreeView1.Items.AddChildObject(Nodo0, DataModule1.EDBQuery1.FieldByName('NOMBRE').AsString, MiClase);

ya que tienes una instancia de un objeto NO manejado (de tipo TMiClase), por lo que no se te autodestruye. Y este objeto mantiene una referencia al string (MiClase.Codigo) por lo que tampoco te destruye éste y todo funciona bien.

Otra solución, sin usar clases auxiliares, podría haber sido la siguiente (escrita sobre el navegador y sin probar):

Código Delphi [-]
var
  LPAnsiChar: PAnsiChar;
  LStr: AnsiString;
begin
  //Hago casting a AnsiString porque así cada caracter ocupa un único byte. Para usar 
  //la versión Unicode, en lugar de length, usar la función ByteLength
  LStr:=AnsiString(DataModule1.EDBQuery1.Fieldbyname('Codigo').AsString);
  LPAnsiChar:=AllocMem(length(LStr)+1);
  Move(LStr[1], LPAnsiChar^, length(LStr);
  Nodo1.Data:=LPAnsiChar;
end

Por supuesto, cuando elimines el nodo del TreeView, deberás hacer un Dispose(Nodo1.Data).

Un saludo,
LoPiTaL

newtron 31-05-2012 16:24:28

Siempre está bien ver distintas formas para hacer cualquier cosa.

Gracias LoPiTal. :)

roman 31-05-2012 17:18:11

newtron, supongo que lo has considerado, pero en caso de que no sea así, te comento que hay que tener cuidado con el código que finalmente escogiste, en el sentido de que debes destruir en algún momento los objetos que creas y pegas en el Data de los nodos.

// Saludos

newtron 31-05-2012 17:33:39

Cita:

Empezado por roman (Mensaje 433946)
newtron, supongo que lo has considerado, pero en caso de que no sea así, te comento que hay que tener cuidado con el código que finalmente escogiste, en el sentido de que debes destruir en algún momento los objetos que creas y pegas en el Data de los nodos.

// Saludos

Amigo roman, veo que me sobreestimas :D. No había caido en eso.

¿Cómo se destruirían esos objetos entendiendo que se ha creado uno por cada item del treeview?

roman 31-05-2012 17:34:18

Usa el evento OnDeletion del TreeView.

// Saludos

Chris 31-05-2012 17:41:59

Cita:

Empezado por roman (Mensaje 433946)
newtron, supongo que lo has considerado, pero en caso de que no sea así, te comento que hay que tener cuidado con el código que finalmente escogiste, en el sentido de que debes destruir en algún momento los objetos que creas y pegas en el Data de los nodos.

// Saludos

Lo mismo iba a comentar. Si hubiera visto tu mensaje, hubiera terminado como Casimiro, repitiendo lo que otros dicen :P :D

newtron 31-05-2012 17:48:51

Cita:

Empezado por roman (Mensaje 433948)
Usa el evento OnDeletion del TreeView.

// Saludos

Vale... ¿y sería posible un ejemplito para torpes? :D


La franja horaria es GMT +2. Ahora son las 08:02:49.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi