Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   ¿Quién llamó mi form? (https://www.clubdelphi.com/foros/showthread.php?t=30475)

dape 07-04-2006 05:37:03

¿Quién llamó mi form?
 
hola amigos, estoy tratando de averiguar con que botón se ha llamado a una form, para eso utilizo el siguiente código:

Código Delphi [-]
procedure form1.create(sender: tobject)
begin
    if sender = form2.boton1 then
       {ejecutar código 1}
    if sender = form3.boton2 then
      {ejecutar código 2}
    {realiza el resto}
end;

como lo habrán notado al form1 puedo llamarlo desde el form2 o el form3, para esto realizo una evaluación en el oncreate del form1 para saber quién lo ha llamado para poder ejecutar un pequeño código específico para cada botón, el caso es que al ejecutar el programa me sale un error de violación de acceso si no recuerdo mal, ejecuta el programa paso a paso y el error salta al hacer el llamado del form1 y evaluar el primer if, al aceptar el error el programa continua pero no evalúa los if, ¡se los pasa de largo olímpicamente como si no hubiera presionado ningún botón para llamar al form1!.

¿Es correcta mi forma de utilizar el sender? ¿existe alguna otra manera de saber quién llamó al form1?.

Desde ya gracias por la ayuda que puedan brindarme.

Saludos desde Tacna - Perú

David

Edgtho 07-04-2006 09:53:33

Te da una violacion de memoria porque estas comparando un tobject con un tbutton sin ningun parseo de clase. Es decir, aparte que hay maneras alternativas mucho mejores. Yo por ejemplo utilizaria el nombre del boton, la propiedad tag o la propiedad hint para identificar el boton origen.


Código:

If (Sender as Tcontrol).name = 'button1' then
 ....

Tomando tag=1 para form1 y tag=2 para form2
Código:

If (Sender as Tcontrol).tag = '1' then
 ....

O mejor aun, pensando que puedes llamarlo desde distintos botones utilizar la propiedad parent

Código:

If (Sender as TWincontrol).Parent.name = 'form1' then
 ....
If (Sender as TWincontrol).Parent.name = 'form2' then
  ....


Lepe 07-04-2006 14:32:48

dape Usando ese código estas suponiendo que Form2 y Form3 se crean desde el principio de la aplicación, y además se crean antes que el Form1. ¿es correcto todo esto?

Este tipo de acciones son muy propensas a fallos, por lo que se suele controlar bien:
Código Delphi [-]
procedure form1.create(sender: tobject)
var Encontrado : Boolean;
begin
  Encontrado := false;
  if Sender is TButton then
  begin
    if Assigned(Form2) then // si está creado el Form2
    if Tbutton(sender) = form2.boton1 then
    begin
       {ejecutar código 1}
      Encontrado := true;
    end;

    if not(Encontrado) and Assigned(Form3) then
    if Tbutton(Sender) = form3.boton2 then
    begin
      {ejecutar código 2}
      Encontrado := true;
    end;
  end;
  
  If not Encontrado then
    {hacer lo que sea}

    {realiza el resto}
end;

Saludos

dape 08-04-2006 04:40:54

Hola, gracias por las respuestas.

Edgtho, voy a probar tus recomendaciones y después cuento como me fue.

Lepe, los form los creo a "pedido", es decir no no se crean al ejecutar la aplicación, solo cuando los necesito, no sé si eso influye en algo.

Lo que ocurre es que nunca he hecho algo así, siempre evité usar el sender porque no lo entendía bien y cuando necesitaba usar más de una ves un form, pues, lo volvía a rehacer pero me cansé de eso, no me parece nada práctico así que decidí investigar algo sobre el uso del sender y, bueno, lo usé a como lo entendí y según parece no lo entendí bien.

De nuevo gracias por las respuesas y seguiré sus consejos.

saludos desde Tacna - Perú

David

Lepe 08-04-2006 11:24:19

Sender es un poquito especial precisamente está derivando de TObject porque al hacer la VCL realmente no se sabe quien puede ir en ese parámetro, así que se manda como TObject que es la clase Base. Digamos que puede ser cualquier componente gráfico que tiene eventos definidos.

Tú tienes 100 botones que hacen lo mismo, en lugar de crear 100 eventos distintos, creas uno solo, seleccionas los 100 botones en el IDE y despues le asignas el mismo evento.

Cuando se hace clic en ejecución, El Sender será el objeto que desencadena el evento, es decir el propio boton.

Como tú eres el que estas programando, sabes que el sender puede ser uno de esos 100 botones, en este caso particular no habría que preguntar el tipo de ese sender, es decir:

Código Delphi [-]
procedure TForm1.Button1Click(Sender:TObject);
begin
// recuerda, este evento está asignado a los 100 botones.

if (Sender is TButton) then   // <<<<<< esto no hace falta, porque tú
// has enlazado el evento SOLO CON BOTONES TBUTTON.

// Directamente escribimos:
TButton(Sender).blablabla
end;
Es decir, sabemos de antemano que es un TButton el que desencadena el evento.

Imagina que ahora tienes 99 botones (TButton) y un TBitBtn y tambien compartes el mismo evento para todos, ahora si es necesario preguntar quien desencadena el evento, porque son clases distintas:

Código Delphi [-]
procedure TForm1.Button1Click(Sender:TObject);
begin
// recuerda, este evento está asignado a los 100 botones.

if (Sender is TButton) then  // hacer lo que sea con el TButton
  TButton(Sender).blabla
else if (Sender is TBitBtn) 
  TBitBtn(Sender).blabla
end;
Primero preguntamos si es un TButton, si lo es, lo tratamos como tal.
TButton(Sender) es lo que se llama un moldeo de tipos, viene a decir: "usa el Sender como si fuera un Tbutton", y así tenemos acceso a todas las propiedades y métodos de ese TButton.

En tu caso particular, ese botón no está en la misma ventana, y además dices que las creas dinamicamente, pues supon este escenario:

- el form2 no está creado todavia.
- Abres el form3 y mandas a crear el Form1 desde alli:
  • Se crea el form1 y entra en el Form1Create
  • Pregunta si Form2.Button1..... espera, espera, delphi intenta acceder a Button1.... pero si todavía no está creado el Form2, tampoco lo estará Button1 ==> Access Violation.

Ahora si lo ves lógico ¿verdad?, Bueno y como se controla, pues tenemos que tener todas las precauciones posibles.

Assigned(Form2) es identico a preguntar if Form2 <> nil then

pero ¿y cómo sé yo que Form2 tiene nil dentro o cualquier otra cosa? Aqui es donde se empieza a elegir un criterio.

Si no necesitamos hacer cosas como:
Código Delphi [-]
  with TForm2.Create do
  try
....
   finally 
     Free
   end;
Tendremos que asegurarnos de que la variable Form2 solo tenga dentro un valor cuando se cree la ventana, y despues de liberarla de memoria, le ponemos un nil dentro:
Código Delphi [-]
procedure Tform3.Boton1Click(...);
begin
  Form2 := Tform2.Create(nil); // nadie la destruye porque la destruyo despues yo.

procedure TForm2.Form2Close(Sender: TObject; var Action: TCloseAction);
begin
  Action := cafree; // lo mandamos a liberar
  Form2 := nil ; le metemos a la variable el nil.
end;
Este truco de roman (si no me equivoco.. :D) nos asegura que cuando se cierre la ventana, la variable tiene nil dentro, y por tanto:
- Si Form2 = nil significa que no se ha creado el Form2
- Si Form2 <>nil significa que la ventana se está mostrando o está oculta... pero fijo que está creada.

De ahí que use if Assigned(Form2) then para preguntar si está creada o no en mi mensaje anterior.

SAludos y espero se entienda.

dape 11-04-2006 02:02:16

hola, después de tanto pelear con el fatidioso sender, el código me quedo así:

Código Delphi [-]
procedure form1.create(sender: tobject)
var Encontrado : Boolean;
begin 
    Encontrado := false; 
    if Sender is TButton then 
    begin 
        if Assigned(Form2) then // si está creado el Form2 
           if not (Tbutton(sender) = form2.boton1) then //¿acaso fue <> en lugar de =? 
        begin 
            {ejecutar código 1} 
            Encontrado := true; 
        end; 
        if not(Encontrado) and Assigned(Form3) then 
          if not (Tbutton(Sender) = form3.boton2) then 
          begin 
               {ejecutar código 2} 
               Encontrado := true; 
          end; 
    end; 
    If not Encontrado then 
      {hacer lo que sea} 
      {realiza el resto}
end;

Bueno, no sé si será así com debe quedar el código, pero funcionó; talvez se pregunten porqué están negados los if, la respuesta es que si no lo niego no me los evaluava, no me pregunten porque ya que no tengo ni la más mínima idea y me gustaría mucho tenerla.

Gracias por la ayuda prestada.

saludos desde Tacna - Perú

David

Lepe 11-04-2006 11:29:20

Delphi no se salta un if así por las buenas ;).

Bien tienes desactivada "complete evaluation" en las opciones del compilador, y entonces si la primera condición de un if es falsa y están unidos con operador "and" entonces no sigue evaluándolas. Yo tambien la tengo desactivada.

Tambien puede ocurrir que por optimizaciones del debugger de delphi, no pueda darte un valor concreto usando F7 y F8.

Ese código debe funcionar en todas las situaciones mientras se llame desde un botón.

Si directamente se hace:
Código Delphi [-]
 Application.CreateForm(TForm1, Form1);
No se encontrará y ejecutará el "If not Encontrado then" que debe controlar precisamente ese posible error.

Lo que haya en "realiza el resto" no debe hacer llamadas ni a Form2 ni al Form3. De esa forma, jamás dará errores extraños.

amadis 13-04-2006 02:24:59

Atadura con Alambre
 
Yo hace tiempo intentaba saber lo mismo!

Al final me las arreglé con un poco de astucia y ate con alambre.

Lo que hice fue crear en cada form una propiedad llamada LLAMADOR del tipo Tform.
Código Delphi [-]
llamador : Tform;

cada vez que voy a crear un form ya sea de un boton o menu luego de crearlo le paso el valor a llamador que será el Form Actual.

Por ejemplo si tienes un Form 1 que puede llamar a demas forms y quieres saber a quien devolver el foco.

Cuando desde form 1 creo al Form3 le paso en la propiedad LLAMADOR el valor FORM1.
Luefo desde form 3 preguntas si LLAMADOS = FORM1 y haces lo que quieras.
O si quieres al cerrar Form3 preguntas si llamador = Form1 le devuelves el foco o haces lo que quiera.

Yo directamente lo que hacia al cerrar era maximizar llamador ya que es una variable Tform encontes solo le decia LLAMADOR.WINDOWSSTATE := MAXIMIZED; y listo.

Espero que te sirva mi ayuda aunque no se que era lo que buscabas


La franja horaria es GMT +2. Ahora son las 19:47:55.

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