Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   TreeView (https://www.clubdelphi.com/foros/showthread.php?t=51831)

LENOCB 31-12-2007 03:16:31

TreeView
 
Hola, necesito ayuda para lo siguiente, mi idea es llanar un treewiev con los datos correspondientes a una tabla, la cosa es que no se como hacerlo, jeje.... mi idea es que quedara alg así :

+General
+General1
SubGeneral1
SubGeneral2
+General2
SubGeneral21
SubGeneral22

y así sucesivamente ......

o si hay otra forma no tan complicada de presentar informacion, aclarando que cuando uno hace click sobre alguno de los links del treewiev, al lado se muestra otra informacion que se corresponde con lo clickeado anteriormente, se entiende ? o la complique mucho ??

saludos y gracias .-

egbaquela 31-12-2007 18:02:56

TreeView
 
Hola Lenocb: yo tuve que hacer un sistema que hacia algo parecido, mostraba los datos de una tabla en un treeview y, modificando la estructura del treeview, modificaba las relaciones padre-hijo en la tabla. Esta hecho para un sistema que gestiona auditorías y permite realizar análisis de arbol de causas.

Te paso a continuación el código para mostrar la tabla en el treeview con comentarios:

Código Delphi [-]
procedure TFrmAuditoriaArbolCausa.CargarTree(Sender: TObject; SQL: String);
//Esta rutina permite mostrar los resultados de una consulta
//en un TreeView. Para ello la consulta debe estar estructurada
//de la siguiente forma:
//      Campo 'Codigo', Integer (ID del registro)
//      Campo 'Descripcion', String (valor a mostrar en el nodo)
//      Campo 'Padre', Integer (ID del padre del registro)
//La consulta debe mostrar relaciones padre e hijo, siendo
//ambos registros de la consulta en cuestión.
//Los registros sin padre deben tener el valor '0' en su
//campo 'padre'. De esto se desprende que ningún registro puede
//tener un 'Codigo'=0.

var
  I, X, N: Integer;
  Nod: TTreeNode;
  NodSuplementario: TTreeNode;
  MiPunteroInteger: PunteroInteger;
begin

  // Cargo la consulta a mostrar en el TreeView
  Query1.SQL.Clear;
  Query1.SQL.ADD(SQL);
  Query1.Active := True;

  //Recorro la consulta registro por registro
  Query1.First;
  for X:=0 to (Query1.RecordCount-1) do
    begin
      I:=Query1.fieldByName('Padre').asinteger;
      // Si el campo padre vale 0 (no tiene padre)
      if I=0 then
        begin //Agrego un nuevo nodo, que nazca directamente del raíz
          Nod:=TreeView1.Items.Add (nil,Query1.FieldByName('Descripcion').asstring);
          New(MiPunteroInteger);
          MiPunteroInteger^:=Query1.Fieldbyname('Codigo').asinteger;
          nod.Data:=MiPunteroInteger;
          nod.Selected :=true;
        end
      else
        begin //Agrego un nuevo nodo hijo al padre que le corresponda
          //Recorro desde el último nodo al primero, hasta que la propiedad data (donde guardo
          //el código de cada nodo, sea igual al campo padre del nodo a agregar
          N:=TreeView1.Items.Count -1;
          while PunteroInteger(TreeView1.Items[N].Data)^ <>I do
            Dec(N);
          //Agrego el nodo hijo
          nodSuplementario:=TreeView1.Items.AddChild(TreeView1.Items[N],Query1.FieldByName('Descripcion').asstring);
          New(MiPunteroInteger);
          MiPunteroInteger^:= Query1.Fieldbyname('Codigo').asinteger;
          nodSuplementario.Data:=MiPunteroInteger;
          Nod.selected:=True;
          Nod.Expanded:=False;
        end;
      query1.Next;
    end;
end;

Cuando modifico la estructura de los nodos en el treeview, y quiero modificar la estructura padre-hijo de la tabla, ejecuto el siguiente código:

Código Delphi [-]
procedure TFrmAuditoriaArbolCausa.BtnActualizarBDClick(Sender: TObject);
var
  X: Integer;
  Nod: TTreeNode;
begin
  // Borro todos los registros de la base de datos que tengan padre y que cumplan las condiciones de filtrado
  Query1.SQL.Clear;
  Query1.SQL.Add('DELETE FROM Auditorias_Problemas WHERE Codigo_Auditoria =' + IntToStr(CodigoAuditoria) + 'AND Codigo_Probefecto <> 0');
  Query1.ExecSQL;

  //Cargo el tree en la base de datos
  for X:=0 to (treeview1.Items.Count-1) do
    begin
      Nod:= Treeview1.Items[X];
      Nod.Selected:=true;
      if Nod.Parent=nil then
        begin
          //No cargo nada, porque al no tener padre ya se carga al declararlo como problema de la auditoría
        end
      else
        begin
          Query1.SQL.Clear;
          Query1.SQL.Add('INSERT INTO Auditorias_Problemas (Codigo_Problema, Codigo_ProbEfecto, Codigo_Auditoria) VALUES (:a, :b, :c)');
          Query1.ParamByName('a').asinteger:= PunteroInteger(nod.data)^;
          if (nod.Parent=nil) then
            begin
              Query1.ParamByName('b').asinteger:=0;
            end
          else
            begin
              Query1.ParamByName('b').asinteger:=PunteroInteger(Nod.Parent.data)^;
            end;
          Query1.ParamByName('c').asinteger:=CodigoAuditoria;
          Query1.ExecSQL;
      end;
    end;

Basicamente lo que hace este ultimo código es borrar todo el arbol mostrado y volverlo a cargar como figura en el treeview.

Como veras, lo mas importante es armar los datos de la tabla indicando de que nodo se van a desprender al mostrarlos en un treeview.

Saludos, Enrique Gabriel Baquela.

Http://enrique-gabirel-baquela.neurona.com

JXJ 02-01-2008 04:10:43

no es que sea encajoso, pero no podrias poner el codigo fuente
de la base de datos y el programa..
si no tienes problema con ellos
gracias :D

eljuanan 19-02-2008 02:59:23

Hola, puedes indicarnos la delcaracion de PunteroInteger?
Por favor?

egbaquela 19-02-2008 04:53:30

Varios
 
Hola eljuan: PunteroInteger es un puntero del tipo integer, nada mas:
type PunteroInteger = ^Integer;
Lo tendría que haber puesto. Igual se puede obviar esta declaración ya que es un paso intermedio redundante:p.

*******
JXJ: hace rato que no me conectaba, no tengo problema en poner el código, dame unos días para que lo prepare y lo arregle un poco. Si necesitas algo mas pedimelo, no tengo drama:).

Saludos, Enrique Gabriel Baquela.
Http://enrique-gabriel-baquela.neurona.com

eljuanan 19-02-2008 09:07:24

Gracias por la respuesta

Lepe 19-02-2008 09:31:55

En realidad no hace falta adquirir memoria con la función "New", ya que la propiedad Data es de tipo puntero ocupando 2 bytes en memoria, lo mismo que ocupa un Integer.

Podría quedar así:
Código Delphi [-]
Nod:=TreeView1.Items.Add (nil,Query1.FieldByName('Descripcion').asstring);
Nod.Data := Pointer(Query1.Fieldbyname('Codigo').asinteger);
Y cuando queramos acceder:
Código Delphi [-]
MiNumero := Integer(Node.Selected.Data)
En este caso no tenemos que liberar nada de memoria cuando se libere el nodo, porque no hemos reservado memoria.

Lo realmente interesante es que podemos crear nuestro propio objeto y asociarlo a cada nodo, guardando mucha más información:
Código Delphi [-]
type Cliente = Class(TObject)
public
   nombre: string;
   apellido:string;
   direccion:string;
   TotalFacturado:currency;
end;

...
 cliente := TCliente.Create(nil);
 Nod:=TreeView1.Items.Add (nil,Query1.FieldByName('Descripcion').asstring);
          cliente);
  cliente.nombre := query1.fieldbyname(....);
  cliente.direccion:= ....

          nod.Selected :=true;
Por supuesto, al eliminar un TTreeNode si necesitamos liberar nuestro objeto Cliente (evento OnFreeNode).

Saludos

egbaquela 19-02-2008 12:56:37

Mucho mejor así, la verdad es que no se me había ocurrido.
Saludos, Enrique Gabriel Baquela.
Http://enrique-gabriel-baquela.neurona.com

JXJ 19-02-2008 23:05:42

claro, te espero.
 
egbaquela
me ayudaria bastante conocer como haces tu codigo.

ahora estoy empezando con un programa. co una interfaz
como la de la ventana principal del messenger.
y tengo que entender el manejo de treeviews.
para agregar nodos, arrastrar y soltar items de un grupo a otro.



te espero a que lo tengas listo.
Gracias por tu codigo...

eljuanan 20-02-2008 10:25:30

Sabeis alguna forma de obtener los nodos hijos,nietos,... a partir
de un nodo padre (cualquiera del arbol, no tiene que ser el nivel superior) de un Treeview,debe ser de forma recursiva

egbaquela 20-02-2008 13:22:56

Hijos y nietos
 
Bueno, cada nodo del treeview (TTreeNode) tiene una propiedad Parent del tipo TTreeNode:

Código Delphi [-]
property Parent: TTreeNode;

Esta propiedad devuelve la referencia al nodo padre del nodo actual.
Dado un nodo X, para ubicar a sus hijas habría que recorrer el treeview guardando en un array (de TTreeNode) la referencia de los nodos con la propiedad Parent igual a X. Luego habría que aplicar esta función al array generado para encontrar a los nietos de X.
Me pongo a pensar un poco y cuando lo implemente lo posteo.
Saludos, Enrique Gabriel Baquela.
Http://enrique-gabriel-baquela.neurona.com

eljuanan 20-02-2008 14:53:21

Te agradeceria mucho que lo hicieras, pk esto bastante pillado
en este tema, me podrias indicar mas o menos algun codigo
aunque no este probado para tenerlo de referencia?.
Gracias

Lepe 20-02-2008 15:00:12

El título es hijos y nietos, sin embargo, al hablar de "Parent" estas hablando de padres y abuelos... la ascendencia en lugar de la descendencia.

Bueno, después del trabalenguas, lo que debes mirar es la propiedad "HasChild" para saber si tienes hijos. Obtener el primero de ellos (GetFirstchild) y mediante un bucle realizar la misma tarea mientras el siguiente hermano (GetnextSibling ) sea distinto de nulo.

¿cómo quieres obtener los hijos y nietos, todos mezclados?
Porque un nodo puede tener dos hijos, y cada uno de ellos a su vez, pueden tener varios nietos del primero, obtener una lista "plana" quizás no sirva de nada.

El código es lo de menos. Lo importante es lo que tenía el programador en la cabeza cuando escribió el código ;).

PD: JXJ, tampoco hay que ser así, muchas veces se pone código y no pasa nada. El problema es "cuando te exigen que pongas el código" ;)

Saludos

eljuanan 20-02-2008 15:59:34

Lo siento, es que estoy bastante pillao
en este tema.
El caso es que quiero obtener en una lista, me da igual el orden
los nodos que dependan de uno padre (cualquier nivel del arbol),
es para realizar una serie de acciones con cada uno antes de
eliminar el padre y todos ellos...
Un Saludo

egbaquela 21-02-2008 14:28:21

Eljuan: te paso un procedimiento que que cambia el texto a toda la descendencia de un nodo determinado, supongo que es parecido a lo que quieres hacer:

Código Delphi [-]
procedure TForm1.CambiarTextoDescendencia(Nodo: TTreeNode);
var
  i:integer;
begin
  if nodo.HasChildren=true then
  begin
    for i := 0 to (Nodo.Count - 1) do
    begin
      CambiarTextoDescendencia(nodo.item[i]);
      nodo.item[i].text:='Texto Cambiado';
    end;
  end;
end;

Como bien dijo Lepe, HasChildren devuelve true si el nodo tiene descendencia. Item[n] te permite trabajar con el hijo número n del nodo actual (están indexados a partir de 0). Count cuenta cuantos hijos tiene el nodo actual.

Para ejecutarlo, suponiendo que queres alterar el texto de la descendecia del nodo seleccionado:

Código Delphi [-]
Form1.CambiarTextoDescendencia(treeview1.Selected);

Para adaptarlo a lo que necesitas, solo tendrías que cambiar la asignación de texto al nodo por la/s operación/es que necesitas realizar.
Espero que te sirva.
Saludos, Enrique Gabriel Baquela.

eljuanan 24-02-2008 00:19:37

Hola de nuevo, sabriais alguno decirme como resaltar (cambiando el color o el fondo por ejemplo), de ciertos nodos que son el resultado de una busqueda?

procedure TFHSFabaMain.BuscayResaltaNodo(n:string);
var
Nodo: TTreeNode;
I: Integer;
begin
for I := 0 to tv.Items.Count - 1 do
begin
Nodo := tv.Items[i];
if Pos(n,Nodo.Text)>0 then
begin
Nodo.Selected := true;
Nodo.MakeVisible;
AQUI NECESITO COLOREAR O RESALTAR DE ALGUNA MANERA LOS NODOS COINCIDENTES
break;
end;
end;
end;

egbaquela 29-02-2008 15:04:50

Resaltar o colorear nodos
 
Elijuan: la verdad es que no tenía ni idea:p, pero buscando en el google encontre esta web:):

http://delphiallimite.blogspot.com/2...view-y-ii.html

Fijate que explica como cambiar los atributos de las fuentes y como asociarle imágenes a cada ícono.
Saludos, Enrique Gabriel Baquela.
Http://enrique-gabriel-baquela.neurona.com

eljuanan 06-03-2008 09:15:27

Dado el procedimiento
para cargar el arbol de la BD
procedure TFrmAuditoriaArbolCausa.CargarTree(Sender: TObject; SQL: String);
os consulto lo siguiente:
he añadido un campo orden a cada registro de la base de datos para
mostrar los nodos de una forma ordenada, y en la consulta
del procedimiento, hago "order by orden".
Pues da un fallo, invalid index
¿Alguna sugerencia?

egbaquela 06-03-2008 20:28:11

Falla en el SQL
 
¿Podrías poner la sentencia SQL de la consulta que generás?
¿Con que base de datos trabajás?.
Saludos, Enrique Gabriel Baquela.

eljuanan 07-03-2008 00:41:46

Este es el procedimiento, el que tu indicaste pero
modificado para mi consulta:
procedure TFHSFabaMain.CargarTree(Sender: TObject; SQL: String;raiz:integer);
var
I, X, N,C: Integer;
Nod: TTreeNode;
NodSuplementario: TTreeNode;
MiPunteroInteger:^Integer;
q:TDataSet;
color:string;
begin
//Proceso que carga el arbol desde la BD
//Obtenemos los parametros para los colores
e.g('_col1',e.ValorParametro('FAB/NOD/NIV1/COLOR',False));
e.g('_col2',e.ValorParametro('FAB/NOD/NIV2/COLOR',False));
e.g('_col3',e.ValorParametro('FAB/NOD/NIV3/COLOR',False));
e.g('_col4',e.ValorParametro('FAB/NOD/NIV4/COLOR',False));
e.g('_col5',e.ValorParametro('FAB/NOD/NIV5/COLOR',False));
e.g('_col6',e.ValorParametro('FAB/NOD/NIV6/COLOR',False));
e.g('_col7',e.ValorParametro('FAB/NOD/NIV7/COLOR',False));
e.g('_col8',e.ValorParametro('FAB/NOD/NIV8/COLOR',False));
e.g('_col9',e.ValorParametro('FAB/NOD/NIV9/COLOR',False));
e.g('_col10',e.ValorParametro('FAB/NOD/NIV10/COLOR',False));
//Bloqueamos el onChange para que no se ejecute
e.g('_bloqchange',1);
tv.Items.Clear;
// Cargo la consulta a mostrar en el TreeView
q :=e.DataSetFromSQL(self,'select codigo,padre,nombre,nivel from fab_nodos where emp=:e_empresa and dep=:e_departamento order by orden');
q.Close;
q.Open;
if (q.eof) then exit;
//Recorro la consulta registro por registro
C:=0;
q.First;
while not q.eof do
begin
C:=C+1;
q.Next;
end;
q.First;
for X:=0 to (C-1) do
begin
I:=q.fieldByName('Padre').asinteger;
// Si el campo padre vale 0 (no tiene padre)
if I=raiz then
begin //Agrego un nuevo nodo, que nazca directamente del raíz
Nod:=tv.Items.Add (nil,q.FieldByName('nivel').asstring+'.'+q.FieldByName('codigo').asstring+' '+q.FieldByName('nombre').asstring);
Nod.ImageIndex:=-1;
New(MiPunteroInteger);
MiPunteroInteger^:=q.Fieldbyname('Codigo').asinteger;
nod.Data:=MiPunteroInteger;
nod.Selected :=true;
end
else
begin //Agrego un nuevo nodo hijo al padre que le corresponda
//Recorro desde el último nodo al primero, hasta que la propiedad data (donde guardo
//el código de cada nodo, sea igual al campo padre del nodo a agregar
N:=tv.Items.Count -1;
while PunteroInteger(tv.Items[N].Data)^ <>I do
Dec(N);
//Agrego el nodo hijo
nodSuplementario:=tv.Items.AddChild(tv.Items[N],q.FieldByName('nivel').asstring+'.'+q.FieldByName('codigo').asstring+' '+q.FieldByName('nombre').asstring);
nodSuplementario.ImageIndex:=-1;
New(MiPunteroInteger);
MiPunteroInteger^:= q.Fieldbyname('Codigo').asinteger;
nodSuplementario.Data:=MiPunteroInteger;
Nod.selected:=True;
Nod.Expanded:=False;
end;
q.Next;
end;
if (e.ValorParametro('FAB/TV','')=1) then tv.FullExpand;
if (e.ValorParametro('FAB/TV','')=2) then tv.FullCollapse;
e.g('_bloqchange',0);
if (tv.Items.Count>0) then
tvChange(tv,tv.Selected);
end;

Cuando se crea un nuevo nodo en el arbol
el campo orden se inicializa a 100;
como tengo implementada una opcion de copiar y pegar en el arbol
lo que pretendes que cuando copio y pego , el nodo copiado se inserte
inmediatamente antes que el destino en el que lo estoy pegando (orden 99),
pues hasta ahi lo hace bien, incluso si copio y pego alguno mas,
el problema viene al ejecutar el procedure para cargar el arbol, da un error de index, supongo que porque al no estar ordenado de padres a hijoss...
¿se os ocurre algo? Me seria de gran ayuda.
La base de datos es Firebird 2.0


La franja horaria es GMT +2. Ahora son las 01:10:50.

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