PDA

Ver la Versión Completa : Crear evento a un componente creado en ejecución


enecumene
04-10-2008, 00:14:31
Hola compañeros, vengo con esta pequeña duda, estoy creando componentes en tiempo de ejecución, ¿existe alguna forma de crearle un evento como OnClick y llamarlo?.

Saludos.

EDITO: se me olvidó el código:

constructor TPanel1.Create(aOwner: TComponent);
begin
inherited;
with TLabel.Create(aOwner) do
begin
Parent := Self;
Caption := ID;
Font.Name := 'Tahoma';
Font.Size := 8;
Font.Color := clBlack;
Top := 0;
end;
with TLabel.Create(aOwner) do
begin
Parent := Self;
Caption := AUTOR;
Top := 30;
end;
with TImage.Create(aOwner) do
begin
Parent := Self;
Align := alClient;
Picture.LoadFromFile(FOTO);
Stretch := True;
end;
Width := 200;
Height := 200;
end;

roman
04-10-2008, 00:30:41
Pues sí claro,


TuComponente.OnClick := ComponenteClick;


donde ComponenteClick es un método que tú defines explícitamente:


TForm1 = class(TForm)
private
procedure ComponentClick(Sender: TObject);
end;


// Saludos

enecumene
04-10-2008, 00:32:29
Gracias Roman, justamente estaba probando este método que comentas, en unos minutos te comento como me va.

Saludos.

enecumene
04-10-2008, 00:37:22
Bueno volví rápido, me entró una duda, por ejemplo, en este pedazo de código donde se crea el componente TImage bajo TPanel1:

constructor TPanel1.Create(aOwner: TComponent);
begin
inherited;
with TLabel.Create(aOwner) do
begin
Parent := Self;
Caption := ID;
Font.Name := 'Tahoma';
Font.Size := 8;
Font.Color := clBlack;
Top := 0;
end;
with TLabel.Create(aOwner) do
begin
Parent := Self;
Caption := AUTOR;
Top := 30;
end;
with TImage.Create(aOwner) do
begin
Parent := Self;
Align := alClient;
Picture.LoadFromFile(FOTO);
Stretch := True;
Onclick := Click;
end;
Width := 200;
Height := 200;
end;

¿El procedure lo creo fuera del constructor o dentro o entre begin...end del Timage?, disculpa mi ignorancia.

Saludos.

roman
04-10-2008, 00:44:23
Recuerda que Delphi no es un lenguaje de esos interpretados que hay por ahí :D No puedes crear funciones durante la ejecución. El evento debes declararlo como te comenté anteriormente, en la declaración del formulario, e implementarlo en la zona interface.

// Saludos

enecumene
04-10-2008, 01:01:49
Gracias Roman, pero ahora tengo otro problema, dentro del panel se crean dos labels (TLabel), y ahora no logro llamar el caption de uno específico :confused::

procedure TFMain.Click(Sender: TObject);
begin
Panel2.Caption := ???;
end;

Saludos.

roman
04-10-2008, 01:12:52
¡Oh! Disculpa. No había visto que está definiendo tu propio componente. El método debes definirlo en la declaración de TPanel1 e implementarlo en la zona implementation de ese mismo componente:


interface

type
TPanel1 = class(TPanel)
private
procedure Click(Sender: TObject)
end;

implementation

procedure TPanel1.Click(Sender: TObject)

end.


// Saludos

enecumene
04-10-2008, 01:16:58
Disculpa roman, como estoy creando un Timage dentro del TPanel que lo cubre por completo, ¿al dar click funciona el procedimiento aunque la imagen esté encima? :confused: :confused:.

Saludos.

roman
04-10-2008, 01:21:05
Como que ya no esstoy entendiendo. ¿De qué va el componente? Si es un panel y dentro hay un Image que abarca todo entonces estamos hablando del click de la imágen y ésa es la que va a responder.

// Saludos

enecumene
04-10-2008, 01:26:50
Como que ya no esstoy entendiendo. ¿De qué va el componente? Si es un panel y dentro hay un Image que abarca todo entonces estamos hablando del click de la imágen y ésa es la que va a responder.

// Saludos

Es lo que estoy tratando de explicar, ¿cómo le creo el procedure para ese Timage que se crea dentro Tpanel?, ya el evento sé como llamarlo, ahora, dentro del procedure quiero llamar el caption de uno de los labels que se crea que es otra duda que tengo.

Saludos.

PD. Post #6 (http://www.clubdelphi.com/foros/showpost.php?p=318070&postcount=6)

Lepe
04-10-2008, 01:27:47
enecumene eso ¿no debería ser un Frame en lugar de un panel?

Según veo tienes un ID, un autor y la foto, pues eso es un frame, con dos labels y un Image colocados en tiempo de diseño, asignas sus eventos y listo.

Cuando quieras crear varios:

uses MiFrame;

procedure TForm1.CreaFrame;
var
uno, dos : TMiFrame;
begin
uno := TMiFrame.Create(self);
uno.labelID.Caption := ' 0001';
uno.labelAutor.Caption := 'autor 1';
uno.image1.loadFromFile('c:\mifoto.jpg');
uno.top := 0;

dos := TMiFrame.Create(self);
dos.labelID.Caption := ' 0002';
dos.labelAutor.Caption := 'autor 2';
dos.image1.loadFromFile('c:\mifoto.jpg');
dos.top := uno.top + uno.height+5;


Lo he puesto así por claridad, se puede mejorar aun más. Si quieres más info, avisa. En el FTP tienes el ejemplo "crear calendario dinamico con frames", quizás te sirva.

Que conste que lo que estás haciendo se puede hacer, pero el Frame creo que puede facilitarte las cosas.

Saludos

maeyanes
04-10-2008, 01:32:43
Hola...

Para poder usar las etiquetas y la imagen, necesitas crear las variables que van a servir de referencia:


type
TPanel1 = class(TPanel)
private
FLabe1: TLabel;
FLabe2: TLabel;
FImage: TImage;

procedure Click(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;

implementation

constructor TPanel1.Create(aOwner: TComponent);
begin
inherited;
FLabel1 := TLabel.Create(nil);
with FLabel1 do
begin
Parent := Self;
Caption := ID;
Font.Name := 'Tahoma';
Font.Size := 8;
Font.Color := clBlack;
Top := 0;
end;
FLabel2 := TLabel.Create(nil);
with FLabel2 do
begin
Parent := Self;
Caption := AUTOR;
Top := 30;
end;
FImage := TImage.Create(nil);
with FImage do
begin
Parent := Self;
Align := alClient;
Picture.LoadFromFile(FOTO);
Stretch := True;
Onclick := Click;
end;
Width := 200;
Height := 200;
end;

destructor TPanel1.Destroy;
begin
FLabel1.Free;
FLabel2.Free;
FImage.Free;
inherited
end;


De esta forma ya podrás hacer referencia a la etiqueta que quieras y a la imagen...


Saludos...

enecumene
04-10-2008, 01:33:19
Vale gracias Lepe, trataré de aplicar eso que comentas con los frames, miren un ejemplo de lo que estoy haciendo (estoy haciendo una vellonera para fines personales y de aprendizaje):

http://img118.imageshack.us/my.php?image=velloneraio1.jpg

Eso lo hice con un scrollbox y los panels creado en tiempo de ejecución sacando las imagenes y la información desde la BD con un while. Si con los frames es más fácil pues ahí voy.

Saludos.

maeyanes
04-10-2008, 01:37:46
Hola...

Veo que quieres hacer referencia a las propiedades desde fuera del componente, o sea, cuando este se encuentra en una forma.

Para esto puedes crear propiedades en tu TPanel1 que hagan referencia a las propiedades de los componentes internos:


type
TPanel1 = class(TPanel)
private
function GetCaption: string;
procedure SetCaption(const Value: string);
published
property CaptionLabel1: string read GetCaption write SetCaption;
end;

implementation

function TPanel1.GetCaption: string;
begin
Result := FLabel1.Caption
end;

procedure TPanel1.SetCaption(const Value: string);
begin
FLabel1.Caption := Value
end;


De esta forma ya puedes hacer algo como:


procedure TMainForm.Create(Sender: TObject);
begin
APanel.CaptionLabel1 := 'Hola mundo';
end;


Saludos...

enecumene
04-10-2008, 02:14:17
Aplicando la opción de Lepe, hice lo siguiente:

procedure TFMain.FormCreate(Sender: TObject);
begin
ZCanciones.Open;
ZCanciones.First;
While Not ZCanciones.Eof do
begin
with TFrame1.Create(Application) do
begin
MuID.Caption := ZCancionesMA_ID.AsString;
MuArtista.Caption := ZCancionesARTISTA.AsString+' - '+ZCancionesALBUM.AsString;
Image1.Picture.LoadFromFile(ZCancionesIMAGEN.AsString);
Parent := SCrollBox1;
Left := 2 + (Width * (ScrollBox1.ControlCount - 1));
Top := 0;
end;
ZCanciones.Next;
end;
end;

Problema:

---------------------------
Debugger Exception Notification
---------------------------
Project Vellonera.exe raised exception class EComponentError with message 'A component named Frame1 already exists'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------


¿Cómo resuelvo esto :D?

Saludos.

gatosoft
04-10-2008, 02:15:59
bueno amigo enecumene, independintemente de si lo haces con Frames o Paneles, creo que debes aclarar un poco mas el tema de donde y por que se crea este evento y como funciona...

Si buscas en la ayuda, verás que el evento OnClick está definido como una propiedad (Property) dentro de la mayoria de objetos visuales (¿o todos?)


Property OnClick:TNotifyEvent;


entonces Onclick es una variable (puntero) que referencia a un procedimiento... Pero atención no es cualquier procedimiento..!!! es uno especial definido como un tipo asi:


type TNotifyEvent = procedure (Sender: TObject) of object;


Esto quiere decir que el tipo TNotifyevent es un tipo de procedimiento que debe estar definido dentro de una clase (De ahi el of Object)


Entonces... tu puedes definir tu procedimiento dentro de cualquier clase que tengas en esta u otra unidad... lo mas comun es hacerlo dentro del Tform


TForm1 =class(Tform)
public
procedure MyOnClick (Sender: TObject);
end; //de la clase formulario

//Pero tambien puedes hacerlo en cualquier otro objeto, asi

TMyPanelPersonalizado = class(Tpanel)
Public
procedure MyOnClick (Sender: TObject);
end; //de la clase panel

TunaOtraClaseDePanel=class(TPanel)
public
//un panel normalito...
end; //



La implementación, la debes hacer (Obviamente en la seccion implementation)

De la siguiente Manera:


Procedure TForm1.MyOnClick(Sender:TObject);
Begin
//
end;

Procedure TMyPanelPersonalizado.MyOnClick(Sender:TObject);
Begin
//
end;



ahora, puedes comenzar a asignarle tus eventos Onclick al objeto que quiereas..!!!

Aunque aqui tengo una duda y por favor me corrigen....creo que no puedes asignarselo a la misma clase donde fue definida... es decir, en el caso de


TMyPanelPersonalizado = class(Tpanel)
Public
procedure MyOnClick (Sender: TObject);
end; //de la clase panel


No podrias hacer esto


Constructor TMyPanelPersonalizado.Create;
Begin
...
{Self.}OnClick:={Self.}MyOnClick;
end;



Pero si puedes hacer esto:


procedure TForm1.FormCreate(Sender: TObject);
Begin
//Objetos creados en tiempo de diseño:
Label1.OnClick:=MyOnclick; //Asigan el onclik definido en TForm1
Panel1.OnClick:=MyOnClick; //Asigan el onclik definido en TForm1

//Objeto Creado dinamicamente:

with TMiPanelpersonalizado.Create(...) do
begin
Parent := Self; //Self aqui es el TForm
Align := alClient;
Onclick := MyClick; //¿Cual Onclick ?... pues el de Tform...
//por que estamos dentro del procedimiento
//OnCreate del tForm
end;//
end;



En tu caso definiste el evento en el Tpanel1 y se lo asignaste a tu imagen en el evento OnCreate del Panel....

como todos los objetos referncian al mismo evento onclick, entonces los puedes diferenciar mmediante el sender...

El sender, en este y todos los objetos es la instancia que dispara el evento... y puedes difernciar tus objetos asi:


Procedure TForm1.MyOnClick(Sender:TObject);
Begin
if Sender is tLabel then
(Sender as Tlabel).Captio:='Hola';

if Sender is TMyPanelPersonalizado then
(sender as TPanelPersonalizado).Caption:='Otra cosa....'

// o si implementaste una diferenciación por tags, u otra propiedad puedes manejarla tambien...

if Sender.Name='TLabel1' then
showMessage('Pinchaste el Label1');

end;



Saludos....

gatosoft
04-10-2008, 02:19:50
El problema está en que cuando creas tus frames en forma dinámica, por defecto siempre seestá creando con el mismo nombre de componente.... FRAME1...

Debes asiganarle un nombre específico..

procedure TFMain.FormCreate(Sender: TObject);
begin
ZCanciones.Open;
ZCanciones.First;
While Not ZCanciones.Eof do
begin
with TFrame1.Create(Application) do
begin
//Por ejemplo
Name:='FRAME_'+ZCancionesMA_ID.AsString;
MuID.Caption := ZCancionesMA_ID.AsString;
MuArtista.Caption := ZCancionesARTISTA.AsString+' - '+ZCancionesALBUM.AsString;
Image1.Picture.LoadFromFile(ZCancionesIMAGEN.AsString);
Parent := SCrollBox1;
Left := 2 + (Width * (ScrollBox1.ControlCount - 1));
Top := 0;
end;
ZCanciones.Next;
end;
end;

enecumene
04-10-2008, 02:25:34
¡Muchas gracias Gatosoft!, era lo que me faltaba, me funcionó de pelos, opté por la opción de Lepe, no se me hubiese ocurrido pues nunca he trabajado con frames, ahora lo usaré con más frecuencia.

Saludos y gracias a todos ;).

enecumene
04-10-2008, 21:15:55
Saludos compañeros, aprovechando el hilo se me presentado un problemilla y es como eliminar los frames creados en tiempo de ejecución que están dentro de un scrollbox, hice lo siguiente:

procedure TFShowgen.Button3Click(Sender: TObject);
var I: Integer;
begin
for I := 0 to FMain.ScrollBox1.ComponentCount - 1 do begin
//destruye los componentes creados
FMain.ScrollBox1.Components[I].Destroy;
//
end;
FMain.ZCanciones.Close;
FMain.ZCanciones.ParamByName('ID').Value := 2;
FMain.ZCanciones.Open;
FMain.ZCanciones.First;
While Not FMain.ZCanciones.Eof do
begin
with TFrame1.Create(Application) do
begin
Name := 'F_'+FMain.ZCancionesMA_ID.AsString;
MuID.Caption := FMain.ZCancionesMA_ID.AsString;
MuArtista.Caption := FMain.ZCancionesARTISTA.AsString+' - '+FMain.ZCancionesALBUM.AsString;
Image1.Picture.LoadFromFile(FMain.ZCancionesIMAGEN.AsString);
Parent := FMain.SCrollBox1;
Left := 2 + (Width * (FMain.ScrollBox1.ControlCount - 1));
Top := 0;
end;
FMain.ZCanciones.Next;
end;
end;

Pero no me funcionó, ¿alguien me da una mano?.

Saludos.

enecumene
04-10-2008, 21:37:25
Intenté este otro código aún no me funciona:

for I := 0 to FMain.ComponentCount - 1 do begin
//destruye los componentes creados
if FMain.Components[I] is TFrame1 then
TFrame1(FMain.Components[I]).Destroy;
//

Saludos.

Lepe
05-10-2008, 00:34:59
Si están dentro del Scrollbox, creo que seria Scrollbox.Controls[i].Free (obviamente el bucle es sobre scrollbox.controls.count -1)

Destroy se usa cuando diseñas el componente, pero no se debe llamar nunca, hay que usar Free.

Saludos

enecumene
05-10-2008, 00:41:40
¿Sin importar que están dentro a través del parent?.

enecumene
05-10-2008, 01:02:13
Bueno haciendo la modificación con tu consejo Lepe:

var I: Integer;
begin
for I := 0 to FMain.ScrollBox1.ControlCount do begin
//destruye los componentes creados
if FMain.ScrollBox1.Controls[I] is TFrame then
TFrame(FMain.ScrollBox1.Controls[I]).Free;
//

Error:

---------------------------
Debugger Exception Notification
---------------------------
Project Vellonera.exe raised exception class EListError with message 'List index out of bounds (3)'. Process stopped. Use Step or Run to continue.
---------------------------
OK Help
---------------------------

En el Scrollbox tenía seis frame y sólo destruyó 3 intercalados y no completó el código.

Saludos.

Delphius
05-10-2008, 01:04:43
Bueno haciendo la modificación con tu consejo Lepe:
Código Delphi [-] (http://www.clubdelphi.com/foros/#)var I: Integer; begin for I := 0 to FMain.ScrollBox1.ControlCount do begin //destruye los componentes creados if FMain.ScrollBox1.Controls[i] is TFrame then TFrame(FMain.ScrollBox1.Controls[i]).Free; //


Error:


En el Scrollbox tenía seis frame y sólo destruyó 3 intercalados y no completó el código.

Saludos.

Hola enecumene, Por la descripción del error y el código intuyo que se debe a que te faltó restar 1 a ControlCount.;)

Saludos,

enecumene
05-10-2008, 01:08:13
Hola enecumene, Por la descripción del error y el código intuyo que se debe a que te faltó restar 1 a ControlCount.;)

Saludos,

Pues no, sigue con el mismo error :)

Delphius
05-10-2008, 01:15:15
Pues no, sigue con el mismo error :)
Ummmm que raro.

Ahora que veo el código... yo diría que debería ser downto. Es decir que el for debe ir desde ControlCount - 1 hasta 0:

for i := ScrollBox1.ControlCount - 1 donwto 0 do...

Saludos,

enecumene
05-10-2008, 01:18:51
Ummmm que raro.

Ahora que veo el código... yo diría que debería ser downto. Es decir que el for debe ir desde ControlCount - 1 hasta 0:

Código Delphi [-] (http://www.clubdelphi.com/foros/#)for i := ScrollBox1.ControlCount - 1 donwto 0 do...


Saludos,

Pues efectivamente mi amigo salteño, me resolvió ese problema, le estoy agradecido. ;)

Saludos.

Delphius
05-10-2008, 01:23:37
Pues efectivamente mi amigo salteño, me resolvió ese problema, le estoy agradecido. ;)

Saludos.
Un gusto en poder ayudarte Fernando.
Para mi no hay mejor recompensa que el haber ayudado a buen amigo.

Saludos,

rgstuamigo
09-10-2008, 19:57:40
Hola enecumene prueba con esto:
for I := 0 to FMain.ComponentCount - 1 do begin
//destruye los componentes creados
if FMain.Components[i] is TFrame1 then
TFrame1(FMain.Components[i]).Free;
//


Saludos....;)

enecumene
09-10-2008, 20:22:36
Gracias rgstuamigo (http://www.clubdelphi.com/foros/member.php?u=33325), la solución de Delphius me resolvió el problema, de todos modos te agradezco tu ayuda ;)

Saludos.

enecumene
09-10-2008, 23:31:37
Ahora se me presenta otra duda, en los frames que se crean y se eliminan en tiempo de ejecución tienen una imagen con su propiedad visible en False, que sólo se presentará al darle click a otra imagen, ¿qué sucede?, al darle click a las demás imágenes se presenta esa imagen sin lograr que las demás vuelvan al estado de visibilidad a False. Para mejor apreciación adjunto una imagen:

http://img217.imageshack.us/img217/9143/vellonera1ps5.jpg

como pueden ver las figuras 1, 2 y 3, al dar click una imagen aparece la figura 1, pero al dar la siguiente imagen aparece la figura 2 pero no desaparece la figura1 y así sucesivamente, como se trata de un ciclo y hay dos TImage en el frame no sabría cómo llegarle al asunto.

Saludos.

Lepe
10-10-2008, 11:02:10
Al final vas a necesitar una lista. Es como un Listbox, que al poner su propiedad Seletected := 3, deselecciona el anterior y selecciona el nuevo.

Puedes heredar de un TObjectList e implementarlo, te será fácil y muy cómodo de mantener el código. Podrías tener una variable global que indique el elemento seleccionado, pero con el tiempo se te complicará.

La lista, también te permitiría liberar todos los componentes sin tener que recorrer los controles del scrollbox.

Busca en el foro, hay mucho sobre TObjectList, así como en la propia ayuda de windows.

Saludos

enecumene
10-10-2008, 19:01:29
Muchas gracias Lepe, ahora mismo lo verifico :)

Saludos.