PDA

Ver la Versión Completa : recorrer lista de controles hijos.


Byfed
05-04-2007, 01:23:47
Hola a todos,

Tengo una duda. Habitualmente, cuando diseño una aplicación (suelen ser todas más o menos parecidas, de gestión, consultas a bases de datos y cosas así, no demasiado rebuscadas), suelo poner el formulario principal maximizado, y después ir construyendo las distintas secciones del programa en distintos frames.

Cuando se selecciona una opción del menú se esconden todos los frames y despues se hace visible el que corresponda. Esto me obliga a que en todos mis programas tengo que hacer una rutina de esconder_frames que es una pesadez:

frame1.hide;
frame2.hide;
.....
frame95.hide;

Cuando selecciono una opción del menu, hago:
escodertodos;
frameN.init;

y en el frameN he definido el procedimiento init, donde hago un self.show;
A parte, en ese procedimiento de inicio, que utilizo para cargar datos, si hacen falta, cargar opciones de combos, cosas así, suelo incluir también una rutina de limpiar_campos, donde tengo que recorrer todos los controles del frame haciendo:
Edit1.text := '';
Edit2.text := '';
...
Edit99.text := '';

Es muy repetitivo y una pesadez. Me preguntaba si no habrá alguna forma de otorgar un valor a una propiedad de un conjunto de controles del mismo tipo.En realidad me bastaría con saber como recorrer una lista de controles hijos.. así para hacer el esconder todos, podría ir recorriendo la lista desde el formularioprincipal y hacer

if tipocontrol(control)=Tframe then
control.hide

o en el init de cada frame, poder hacer

if tipocontrol(control)=Tedit then
control.text := '';

Espero haberme explicado bien. En realidad se puede resumir todo esto en que me gustaría saber si existe una forma de poder ir recorriendo todos los controles que están dentro de un formulario, un frame, o un groupbox.. sin tener que recurrir al nombre de cada control.

Gracias y un saludo.

vtdeleon
05-04-2007, 01:38:51
Prueba con esto para los edits


Procedure Limpiar(Frame:Tframe);
var
I:Integer;
begin
For I:=Frame.ComponentCount-1 downto 0 then
if Components[I] is TEdit then
Tedit(Component[I]).Clear;
end; Lo mismo lo puedes aplicar para los Frames.

roman
05-04-2007, 01:41:48
Realmente no tienes que esconder todos los frames cada vez, sino únicamente el que estuviere visible al momento de activar otro. Una solución podrías ser la de llevar una variable


FrameActivo: TFrame;


que apunte al frame activo. Cuando muestres un frame pondrías algo como:


FrameActivo.Hide;
FrameAMostrar.Show;
FrameActivo := FrameAMostrar;


// Saludos

Byfed
05-04-2007, 02:11:59
Encontré la solución haciendo lo siguiente:


procedure TFrameClientes.limpiar_detalle;
var
i : integer;
begin
for i:=0 to FormPrincipal.FrameClientes1.ComponentCount-1 do
begin
if (FormPrincipal.FrameClientes1.Components[i] is TEdit) then
Tedit(FormPrincipal.FrameClientes1.Components[i]).text := '';
if (FormPrincipal.FrameClientes1.Components[i] is TCheckBox) then
TcheckBox(FormPrincipal.FrameClientes1.Components[i]).checked := false;
if (FormPrincipal.FrameClientes1.Components[i] is TMemo) then
TMemo(FormPrincipal.FrameClientes1.Components[i]).text := '';
if (FormPrincipal.FrameClientes1.Components[i] is TComboBox) then
begin
TComboBox(FormPrincipal.FrameClientes1.Components[i]).text := '';
TComboBox(FormPrincipal.FrameClientes1.Components[i]).itemindex := -1;
end;
end;
end;


Ahora quedaría otro reto.. y es hacer este procedimiento más general aún...
Que no lo tenga que escribir para cada frame.. sino que el frame fuera un parámetro y poder llamar al procedimiento como:

limpiar_detalle(FrameClientes1);

¿Alguien sabe cómo?

Salu2

vtdeleon
05-04-2007, 02:20:49
Con el código que te di mas arriba, lo puedes hacer. El procedimiento capta un parametro de la clase TFrame.

vtdeleon
05-04-2007, 02:22:30
Una pregunta,...Por qué no destruyes el Frame? antes de llamar al próximo.

Byfed
05-04-2007, 02:23:30
Al final resultó ser una chorradilla...

Creé un fichero (suelo crearlo para meter en él todos los procedimientos "reutilizables") llamado unitFunciones que de momento solo contiene ésto:

unit UnitFunciones;

interface

uses Forms, StdCtrls;

procedure limpiar_detalle(frame: Tframe);

implementation

procedure limpiar_detalle(frame: Tframe);
var
i : integer;
begin
for i:=0 to frame.ComponentCount-1 do
begin
if (frame.Components[i] is TEdit) then
Tedit(frame.Components[i]).text := '';
if (frame.Components[i] is TCheckBox) then
TcheckBox(frame.Components[i]).checked := false;
if (frame.Components[i] is TMemo) then
TMemo(frame.Components[i]).text := '';
if (frame.Components[i] is TComboBox) then
begin
TComboBox(frame.Components[i]).text := '';
TComboBox(frame.Components[i]).itemindex := -1;
end;
end;
end;



La llamada la hago desde el init de cada uno de los frames, haciendo:


limpiar_detalle(FormPrincipal.FrameClientes1);


y voilá...

Si me hubiera puesto a investigar esto antes.. cuántas horas de trabajo me había ahorrado... El ultimo programa que hice tenía unos 20 frames, y limpiaba los campos uno a uno!!!.. no se cuantas líneas de código.. pero una burrada.. bueno, de los errores se aprende.

Gracias a todos.

Byfed
05-04-2007, 02:26:53
Con el código que te di mas arriba, lo puedes hacer. El procedimiento capta un parametro de la clase TFrame.

Vaya... no me fijé en que usabas el frame como parámetro.... lo probé por mi cuenta y funcionó.. de todos modos, gracias.

Salu2

Byfed
05-04-2007, 02:28:28
Una pregunta,...Por qué no destruyes el Frame? antes de llamar al próximo.

Porque no siempre limpio todos los campos del frame cada vez que entro. Depende de si llamo a init con algún parámetro o no. En ocasiones, interesa que al volver al frame que dejaste, mantenga el estado en el que estaba.

Salu2

Byfed
05-04-2007, 02:31:06
Joer.. Román.. como leches no he caido antes!!!.. si es que aún soy muy novato... en fin, muchas gracias de nuevo (utilizaré una variable de FrameActivo).

Nota: No os cuento la porrada de veces que he escrito un procedimiento para esconder todos los frames, porque me da vergüenza. :)

Saludos y gracias.

Lepe
05-04-2007, 12:38:01
Ya puestos a trabajar de la forma de roman, se podría hacer algo más automático:



TForm1 = class(TForm)

private
FFrameActivo : TFrame;
procedure SetFrameActivo(const Value:TFrame);
public
FrameActivo :TFrame read FFrameActivo write SetFrameActivo;
...

procedure TFrom1.SetFrameActivo(const Value:TFrame);
begin
if Assigned(FFrameActivo) and (FFrameActivo <> Value) then
begin
FFrameActivo.Hide;
end;
FFrameActivo := VAlue;
FFrameActivo.Show;
end;


Con este rollo, a partir de ahora solo tienes que hacer:

FrameActivo := frame1;

y listo, se oculta el que se estaba viendo y se muestra el nuevo.

Saludos

Byfed
05-04-2007, 16:41:33
Hola Lepe,

Gracias por tu respuesta, pero.. no es enrevesar demasiado las cosas?? No alcanzo a ver la ventaja de lo que propones.. aunque seguramente es porque no tengo conocimientos suficientes.

Aprovecho para ampliar mis horizontes y preguntarte por el código siguiente que has puesto en tu respuesta:


public
FrameActivo :TFrame read FFrameActivo write SetFrameActivo;

Eso de "read" y "write" qué significa y para qué sirve?

Gracias de nuevo y un saludo.

Lepe
05-04-2007, 18:43:25
Me equivoqué en esa línea, debería decir:
property FrameActivo :TFrame read FFrameActivo write SetFrameActivo;

Eso significa que cuando vamos a leer (read) la propiedad FrameActivo, en realidad se lee la variable FFrameActivo, típico en este caso:

var r:TFrame;
begin
r := FrameActivo;
end

Y cuando se va a escribir (write) en esa variable, Ejemplo:

FrameActivo := Frame1;

Se ejecutará el procedimiento SetFrameActivo, con lo cual, podemos realizar otras operaciones. Además, como ese procedimiento lleva un parámetro, podemos saber si FFrameActivo ya tenía asignado el Frame1, el Frame2, o cualquier otro.

En el código delphi anterior, el parámetro "Value" será el valor que queremos asignar, es decir "Frame1".

Fíjate que antes de mi mensaje, tú tendrías que hacer lo dicho por roman, 3 líneas de código:

FrameActivo.Hide;
FrameAMostrar.Show;
FrameActivo := FrameAMostrar;


A partir de ahora, es sólo una línea de código:

FrameActivo := Frame1;


Mira cualquier unidad de delphi con clases, y verás que trabaja así internamente, es decir, no estoy inventando nada nuevo ;).

Saludos