Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Llamada de procedimientos (https://www.clubdelphi.com/foros/showthread.php?t=6626)

wallesis 15-01-2004 19:59:05

Llamada de procedimientos
 
Hola a tod@s.
Hace unos dias os pedia ayuda para una aplicacion MDI. Exactamente queria saber como trabajar o llamar a los objetos de la ventana padre desde la ventana hija. Hoy la duda es a la inversa. Lo he intentado así: (ejemplo)
Ventana_Hija.Componente.Caption := 'Hola';
Pero me da error.
Consigo hacerlo de esta forma:

with (ActiveMDIChild as TVentana_Hija) do
Componente.Caption := 'Hola';

Pero yo quiero saber si se puede hacer como lo he puesto al principio.

Otra cosa.
Otro tipo de llamada que quiero utilizar es la de los procedimientos. Yo se como llamar a un procedimiento ubicado en el codigo principal (ventana padre) desde el codigo de la ventana hija:
Ventana_Principal.Procedimiento(nil);
Sin embargo cuando llamo a un procedimiento de la ventana hija desde la ventana padre me da error. Lo escribo así:

Ventana_Hija.Procedimiento(nil);

¿Como puedo realizarlo?

Tambien me interesa saber como seria la llamada de procedimientos en los que pasamos parametros por referencia. Aqui no he conseguido nada. Me gustaria saber como se realiza la llamada de estos procedimientos desde ambas ventanas.
Por ejemplo como llamaria desde la ventana padre a un procedimiento como este, que está ubicado en el codigo de la ventana hija?.

procedure TVentana_Hija.FormCloseQuery(Sender: TObject;var CanClose: Boolean);

Y éste mismo procedimiento a la inversa?

Gracias por todo
Salu2

eduarcol 15-01-2004 20:06:28

no se si me equivoque pero por lo que te pude entender deberias colocar la unidad dond esta la ventana hija en el uses de tu form padre de esta forma pudieras hacer la referencia, pruebalo y suerte

roman 15-01-2004 20:11:37

Es difícil saber qué pasa si no indicas cuáles son los errores que aparecen.

Lo que puedo aventurar es que cuando usas por ejemplo

Ventana_Hija.Componente.Caption := 'Hola';

el formulario Ventana_Hija no existe. ¿Cómo creas la ventana hija? Si lo haces por código con algo como:

with TVentana_Hija.Create(...) do

entonces Ventana_Hija, que es la variable que por default crea Delphi, no está creada.

Detalla más acerca del error y de cómo creas la ventana hija para que podamos ayudarte más.

// Saludos

roman 15-01-2004 20:13:16

Y viendo el mensaje de eduarcol me doy cuenta de que ni siquiera indicas si el error aparece al compilar o durante la ejecución.

// Saludos

wallesis 16-01-2004 18:26:31

Hola.
He elaborado un documento con impresiones de pantalla para explicaros mejor todo lo que me pasa. Cuando he ido a subirlo he visto que no podia porque es más grande de lo permitido.
¿Hay alguna forma de hacéroslo mandar?
Salu2

eduarcol 16-01-2004 18:34:50

probastes colocandolo en un ZIP... me imagino que las pantallas las capturastes en gif o jpg

wallesis 16-01-2004 20:28:22

Hola.
Es un documento de Word con texto y imagenes en jpg. Su tamaño es de 365 Kb (comprimido) y cuando intento subirlo me pone este mensaje:

MDI.zip:
File Too Large. Limit for this filetype is 1,0 KB. Your file is 364,4 KB.

Tambien he intentado subirlo sin comprimir y por su puesto su respuesta fue la misma.

eduarcol 16-01-2004 20:48:48

la verdad es qie me dejas dsconcertado, necesitariamos ayuda de uno de los moderadores o alguien que sepa por que si el limite es 1kb es algo pequeño

guillotmarc 16-01-2004 21:27:34

Hola.

Estás utilizando las variables globales que se crean por defecto con los formularios. Pero por la forma en que creas dinamicamente los formularios MDI hijos, no debes inicializar esas variables.

Cuando creas un formulario :

TVentana_Hija.Create(Application); // Supongo que harás esto o algo parecido

Cámbialo para asignar también la variable global :

Ventana_Hija := TVentana_Hija.Create(Application);

Aunque te puedes encontrar con varios problemas.
  • Puede ser el formulario Ventana_Hija aún no se haya creado, o bien que si se haya creado en su momento pero ya ha sido cerrado. En ambos casos la variable Ventana_Hija no puede apuntar a ninguna parte, y obtendrás un error.
Solución : Antes de usar la variable Ventana_Hija, comprueba que tiene algún valor.

if Assigned(Ventana_Hija) then Ventana_Hija.Componente.Caption := 'Hola';

Aunque esto solo sirve, para detectar que un formulario aún no ha sido creado. Para detectar si un formulario creado anteriormente ha sido cerrado, cuando cierras el formulario (por el ejemplo en el evento OnDestroy) debes poner asignar la variable a Nil. (Sinó lo haces, sigue teniendo la dirección del formulario anterior, que ya no es válida)
  • Puedes tener dos formularios de la clase TVentana_Hija, de forma que la variable Ventana_Hija siempre apuntará al último creado (el último que ha puesto su dirección en la variable global).
Solución : No utilizes esas variables globales (yo es lo primero que borro al crear un formulario). En lugar de ello, intenta utilizar variables locales que apunten siempre al formulario que quieres modificar :

Ejplo. :

Código:

proc LoQueSea;
var Ventana_Hija: TVentana_Hija;
begin
  Ventana_Hija := TVentana_Hija.Create(Application);
  // Hacemos otras cosas
  Ventana_Hija.MiComponente.Caption := Hola;
end;

O bien utiliza la colección MDIChildren del formulario principal.

Código:

for i := 1 to Application.MainForm.MDIChildCount do begin
  if Application.MainForm.MDIChildren[i] is TVentana_Hija then begin
        (Application.MainForm.MDIChildren[i] As TVentana_Hija).Componente.Caption := Hola;
  end;
end;

Por cierto, no te recomiendo acceder directamente a los componentes de un formulario, desde otro. Soy de la opinión que los componentes del formulario deberían ser protected es decir, no verse desde otros formularios. De esta forma ganas modularidad.

Seria mejor declarar un método public en el formulario, que sea el que accede al componente, y desde los otros formularios unicamente llamamos al método.

O sea, en la sección public del formulario declaras :

procedure CambiarTitulo(Mensaje: String);

y en la sección implementation, lo defines como :

Código:

procedure TVentana_Hija.CambiarTitulo(Mensaje: String);
begin
  Componente.Caption := Mensaje;
end;

Ahora cuando desde otro formulario, quieres cambiar el Caption de ese formulario, lo que haces es llamar al método correspondiente :

Ventana_Hija.CambiarTitulo('Hola');

Espero que te sea de utilidad.

Saludos.

wallesis 16-01-2004 23:04:14

Hola.
A ver si puedo explicarlo un poco mejor.

Para empezar quiero decir que las unidades están colocadas bien y en ambos códigos:

Estos son los procedimientos para crear las ventanas hijas (por cierto se trata de un editor), en concreto los procedimientos Nuevo, Abrir y uno privado.

procedure TPrincipal.Crear_Ventana(Nombre: String; Modificado: Boolean);
var
ventana: TVentana_Hija;
begin

ventana := TVentana_Hija.Create(Self);
ventana.Caption := Nombre ;
SB_Barra.Panels[1].Text := ventana.Caption;
ventana.Show;

end;

procedure TPrincipal.Nueva_VentanaExecute(Sender: TObject);
begin
Crear_Ventana('Sin título '+ inttostr(MDIChildCount+1), False);

TB_Alin_IzquClick(nil);//Para comenzar con la alineacion a la izquierda
// en cada ventana.
end;

procedure TPrincipal.Abrir_FicheroExecute(Sender: TObject);
begin
if (OD_Abrir_Fichero.Execute) then
if (FileExists(OD_Abrir_Fichero.FileName)) then
begin
Crear_Ventana(OD_Abrir_Fichero.FileName, False);
With ActiveMDIChild as TVentana_Hija do
begin
RE_Editor.Lines.LoadFromFile(OD_Abrir_Fichero.FileName);
RE_Editor.Modified := False;
end;
end;
end;

Resumiendo un poco. Hay dos cosa que no me salen bien:
1.- No se o no puedo llamar a un objeto ubicado en la ventana hija desde el código de la ventana principal (codigo_Principal) , sin utilizar la sentencia
with (ActiveMDIChild as Tventana_Hija) do
Por ejemplo. En el procedimiento abrir, que he puesto antes, si yo quito la sentencia With ActiveMDIChild as TVentana_Hija do y coloco:

...............
begin
Ventana_Hija.RE_Editor.Lines.LoadFromFile(OD_Abrir_Fichero.FileName);
Ventana_Hija.RE_Editor.Modified := False;
end;


Esto me compila bien pero da un error en ejecución

¿Por que al anular la sentencia with (ActiveMDIChild as Tventana_Hija) do e incluir el nombre de la ventana hija (que se llama también Ventana_Hija) delante del componente RE_Editor, no me funciona y me da el error en tiempo de ejecución que veis en la imagen?
Este mismo sistema aplicado al reves si que funciona. Es decir desde el codigo de la ventana hija llamar o utilizar a un componente de la ventana padre. Simplemente hago lo mismo coloco Ventana_Principal.Componente....


2.-Segundo problema. No se como llamar a los procedimientos que incluyen parámetros por referencia, desde ambos códigos.
Por ejemplo:
Yo quiero llamar desde el código de la ventana padre (que lo he llamado codigo_Principal) a un procedimiento ubicado en el código de la ventana hija (Codigo_Hija), por ejemplo el de cerrar las ventanas.
Este sería el código de cerrar ventanas que estaría ubicado en el código de la ventana hija:

procedure TVentana_Hija.FormCloseQuery(Sender: TObject;var CanClose: Boolean);
begin
......
end;

Y yo lo que quiero es llamarlo desde el código de la ventana padre.Yo he intentado llamarlo de esta forma, que me imagino que sea incorrecto:
Ventana_Hija.FormCloseQuery(nil; var CanClose);

Y estos son los errores en la compilación:
“ Not enough actual parameters”
Es decir, que no lo he puesto bien....


Estos son las dos cosas que no se hacer. Perdonar todo este rollo que os he metido, creo que no me olvido de nada. Muchas gracias por vuestra ayuda.
Salu2

eduarcol 16-01-2004 23:16:28

Mira lo que pude observar es qe tienes una pequeña confusion, te explico cuando haces with ActiceMdiChild as TVentana_Hija estas haciendo un type cast de la clase TVentanaHija, pero cuando haces Ventana_Hija.Caption te da el error debido a que la variable Ventana_Hija no ha sido incializado porq en el procedimiento creas una variable denominada Ventana no ventana_Hija, alli esta tu primer problema...

lo segundo es que para pasar una variable por referencia no se lo indicas al momento de Llamarla solo cuando la defines por ejemplo
esta bien hecho cuando lo defines
procedure TVentana_Hija.FormCloseQuery(Sender: TObject;var CanClose: Boolean);
begin
......
end

pero cuando lo llames no le digas

Ventana_Hija.FormCloseQuery(nil; var CanClose);

dile Ventana_Hija.FormCloseQuery(nil; CanClose);

asumiendo que canclose es una variable que tienes definida
y no estoy seguro pero creo que tienes que pasarle tambien el form que estas cerrando y no un puntero nil

guillotmarc 17-01-2004 00:10:08

Veamos el primer problema.

El error está, como he comentado en el mensaje anterior, en la variable que utilizas para referenciar el formulario.

Durante la creación que haces del formulario :

Código:

procedure TPrincipal.Crear_Ventana(Nombre: String; Modificado: Boolean);
var
ventana: TVentana_Hija;
begin

ventana := TVentana_Hija.Create(Self);
ventana.Caption := Nombre ;
SB_Barra.Panels[1].Text := ventana.Caption;
ventana.Show;

end;

Estás accediendo al formulario (El Caption del Formulario), y funciona bien ¿ verdad ?.

Si aquí mismo hicieras :

Código:

Ventana.RE_Editor.Lines.LoadFromFile(OD_Abrir_Fichero.FileName);
Ventana.RE_Editor.Modified := False;

Funcionaria correctamente. Esto es porqué referencias al formulario mediante una variable local, como propongo al final de mi mensaje.

En cambio, en tu código, una vez creado el formulario, finaliza el procedimiento y desaparece la variable Ventana con que lo has referenciado. Cuando quieres volver a acceder a ella, utilizas la variable Ventana_Hija, cosa que el compilador accepta puesto que es del tipo adecuado, pero como no está inicializada con ningún valor, da un error en ejecución al usarla.

Para solucionarlo, una posiblidad es poner todo el código en un solo procedimiento que mantenga una referencia Ventana válida :

Código:

procedure TPrincipal.Abrir_FicheroExecute(Sender: TObject);
var Ventana: TVentana_Hija;
begin
  if not OD_Abrir_Fichero.Execute then Exit;
  if not FileExists(OD_Abrir_Fichero.FileName) then Exit;

  ventana := TVentana_Hija.Create(Self);
  ventana.Caption := OD_Abrir_Fichero.FileName;
  SB_Barra.Panels[1].Text := OD_Abrir_Fichero.FileName;

  Ventana.RE_Editor.Lines.LoadFromFile(OD_Abrir_Fichero.FileName);
  Ventana.RE_Editor.Modified := False;
end;

En todo caso, creo que es conveniente que te leas todo mi mensaje anterior, puesto que hay más casos comentados.

Saludos.

guillotmarc 17-01-2004 00:21:01

Veamos ahora el segundo problema :

Como han comentado, no hace falta que uses la directiva var al pasar por referencia un parámetro. Esa directiva solo se pone en la definición del parámetro, pero en el momento de llamar a la función, en el parámetro tienes que poner una variable (como podrías hacer perfectamente en una parámetro pasado por valor).

Tienes que poner una variable porqué, una vez ejecutado el procedimiento, si se ha modificado el valor, solo tienes que leer el contenido de la variable para conocer el valor modificado.

Así pues, la llamada te quedaría :

Código:

var PuedeCerrar: boolean;
begin
  PuedeCerrar := True;
  Ventana.FormCloseQuery(Ventana; PuedeCerrar);
end;

Como puedes ver, tienes que referenciar el formulario con una variable que apunte a él. (Al igual que pasaba con los componentes, sinó tendrías el mismo problema al intentar ejecutar el procedimiento).

Como comenté en el primer mensaje, no es nada aconsejable programar de esta forma, accediendo directamente a componentes y llamando a eventos de otros formularios. Esto te complica mucho el mantenimiento del programa, hacer modificaciones, ver donde falla, etc .... Seria mucho mejor que todo lo que quieras hacer sobre un formulario, lo pongas en unos métodos públicos que sean lo único a lo que se accede desde fuera del formulario.

Saludos.

wallesis 20-01-2004 11:06:30

Entiendo muy bien lo de las variables locales y globales y como puedo haceder a un formulario desde el mismo codigo.
Guillotmarc escribio:
Code:
procedure TPrincipal.Abrir_FicheroExecute(Sender: TObject);
var Ventana: TVentana_Hija;
begin
if not OD_Abrir_Fichero.Execute then Exit;
if not FileExists(OD_Abrir_Fichero.FileName) then Exit;

ventana := TVentana_Hija.Create(Self);
ventana.Caption := OD_Abrir_Fichero.FileName;
SB_Barra.Panels[1].Text := OD_Abrir_Fichero.FileName;

Ventana.RE_Editor.Lines.LoadFromFile(OD_Abrir_Fichero.FileName);
Ventana.RE_Editor.Modified := False;
end;

Pero que pasa cuando quieres haceder a cualquier formulario, ya creado,para poder hacer cosas en el. Porque en el ejemplo anterior lo que hacemos es crear un formulario nuevo y a la vez aprobechamos para actualizarle.
Tambien dices que no recomiendas acceder a otros formularios desde otros. Pero es imposible, por ejemplo:
En este editor de texto dispongo de una barra de herramientas y un menú, que están ubicados en la ventana padre o principal. El principal componente de este programa lógicamente es un editor de típo TrichEditor y este componente está en la ventana hija. Cuando yo pulso el botón cerrar ventanas que está ubicado en la ventana principal o padre, me estoy refiriendo o deseo referirme a una ventana hija, que pertenece a otro formulario.
Mi problema en concreto es en la parte de cerrar ventanas.En el formulario de la ventana hija he utilizado el evento:
procedure TVentana_Hija.FormCloseQuery(Sender: TObject;var CanClose: Boolean);
Y lo he codificado tal y como me sugieres pero me da error en la ejecución.
Pero tambien en la ventana padre, en la barra de herramientas, he incluido un boton para cerrar ventanas y lo que yo pretendia es desde el procedimiento de ese boton(codigo padre) hacer una llamada al procedimiento anteriormente citado de cerrar ventanas, situado en el formulario de la ventana hija.
He realizado lo de poner el procedimiento en la sección public y he conseguido ejecutar el programa pero no ejecuta bien el procedimineto ya que no cierra ventanas.
Tal vez una solucción sería hacer codigos independientes para cada botón?
De todas formas voy a seguir estudiando el tema y ya os contaré.
Salu2

guillotmarc 26-01-2004 15:20:45

Hola.

He estado una semana fuera, ¿ como tienes el tema ?, ¿ has podido solucionarlo ?

Saludos.

wallesis 26-01-2004 19:22:30

Hola Guillotmarc.
Estoy en un punto muerto, digamos que he tirado la toalla. No he sabido, o podido, dar con ello.
Con la llamada a otro procedimiento lo que queria hacer es, logicamente, evitar escribir de nuevo parte del codigo.
Consegui hacer la llamada sin que me diese error alguno ni al compilar ni en ejecución, pero al pulsar el boton el programa no hacia nada.
Cuando me empezo a salir humo por las orejas :D , decidi olvidarme de la llamada al procedimiento y ejecutar para ese procedimiento unas nuevas sentencias. Pero, de nuevo, la suerte no me acompaño.

Resumiendo un poco, yo dispongo de un editor con ventanas MDI. Codifique el evento On CloseQuery de la ventana hija, y funciona bien.
En la ventana principal o padre, dispongo de una barra de herramientas y en ella está un boton para cerrar ventanas (activas).Mi idea al principio fue, desde este boton realizar una llamada al procedimiento del formulario hijo, On CloseQuery y así cerrar las ventanas. Pero :confused: y :mad:
Ultimamente, como he dicho antes, he codificado nuevas sentencias para ese boton:
var
respuesta,i: integer;
Ventana_Activa: string;
ventana: TVentana_Hija;

{ for i := 0 to MI_Ventana.Count - 1 do
if (MI_Ventana.Items[i].Tag = 0) then
if (MI_Ventana.Items[i].Checked = True) then
Ventana_Activa := MI_Ventana.Items[i].Caption; }

ventana := Application.FindComponent(ActiveMDIChild.Caption) as TVentana_Hija;
if assigned (ventana) then
ventana.Show
else exit;

if (ventana.RE_Editor.Modified) then
begin
Respuesta := Application.MessageBox('¿Desea guardar los cambios? ',
' Guardar',mb_IconWarning + mb_yesNoCancel);
Case Respuesta of
idYes: begin
if (FileExists (ventana.Caption)) then
begin
ventana.RE_Editor.Lines.SaveToFile(ventana.Caption);
Close;
end
else
begin
if(SD_Guardar_Fichero.Execute) then
begin
SD_Guardar_Fichero.Filename := Ventana_Activa;
ventana.RE_Editor.Lines.SaveToFile(ventana.Caption);
Close;
end;
end;
end;
idNo : Close;
idCancel: ;
end;
end
else Close;

Este es el procedimiento que estoy utilizando. Problemas: creo que no me asigna nada a la variable ventana y como ñe tengo puesto exit , pues se sale del procedimiento. es decir, que cuando pulso el boton no ocurre nada. Tambien intente, como se ve arriba, darle el nombre de la ventana cogiendolo del menu ventana, pero tampoco funciona.
Una vez que conseguí entrar en el procedimiento me lo ejecutaba dos veces, por ejemplo`pulsaba el boton y aparecia el mensaje de si quería guardar los cambios, le decia, por ejemplo, que no y volvia a salir de nuevo el memnsaje y si pulsaba de nuevo que no se cerraba la ventana.

Otra cosa que me sucede en este y en el otro procedimiento (On CloseQuery) es que he observado que las ventanas nuevas (en blanco) se crean con la propiedad modified en True, a pesar de que al crear estas ventanas le coloco una sentencia, que tambien está en el botón abrir y si funciona, en la que le pongo la propiedad a false. Tambien el el boton on create de la ventana hija le tengo puesto otra sentencia el la que pongo modified a false.

Bueno, como puedes ver, estoy bastante entretenido :)
De todas formas te agradezco mucho que te hallas acordado de mi.
Muchas gracias.

wallesis 26-01-2004 20:44:22

Perdona no hagas caso a mi anterior mensaje. SI que he conseguido entrar en el procedimiento de esta forma:

var
respuesta: integer;

begin

with (ActiveMDIChild as TVentana_Hija) do
begin
if (RE_Editor.Modified) then
begin
Respuesta := Application.MessageBox('¿Desea guardar los cambios? ',
' Guardar',mb_IconWarning + mb_yesNoCancel);
Case Respuesta of
idYes: begin
SD_Guardar_Fichero.Filename := Caption;
if (FileExists (SD_Guardar_Fichero.Filename)) then
begin
RE_Editor.Lines.SaveToFile(SD_Guardar_Fichero.Filename);
Close;
end
else
begin
if(SD_Guardar_Fichero.Execute) then
begin
RE_Editor.Lines.SaveToFile(SD_Guardar_Fichero.Filename);
Close;
end;
end;
end;
idNo : Close;
idCancel: ;
end;
end
else Close;
end;

Pero si es cierto que se me ejecuta dos veces a partir de que hago una modificacion en el documento, es decir si tengo un documento en el que la propiedad modified está en False y pulso el botón cerrar, la v3ntana se cierra bien. Pero en el momento que entra en la sentencia: if (RE_Editor.Modified) then, todas las opciones que tóme se repiten, eso si al final de la segunda vez si que se realiza lo que halla elegido.

Lo ultimo que te cuento, lo de que un documento nuevo se inicia con la propiedad modified en true, tambien me sigue ocurriendo. Por ejemplo cuando creo una ventana nueva se pulso el boton cerrar ventana, en vez de cerrarse tal cual, me entra en la sentencia if (RE_Editor.Modified) then y me pregunta si deseo guardar los cambios.

Perdona el desliz.
Salu2

roman 27-01-2004 07:39:47

Vamos a intentar aclarar un poco las cosas.

El primer paso, aunque parezca un poco pesado, es que aprendas a utilizar la etiqueta [ code ] para escribir código en tus mensajes. Esta etiqueta te permite preservar las indentaciones en las líneas que escribes lo cual facilita muchísimo la lectura:

Código:

if UsoEtiquetaCode then
    LasLineasPreservanLaIndentacion;

{
    Lo anterior fue escrito así:

    [ code ]
    if UsoEtiquetaCode then
        LasLineasPreservanLaIndentacion;
   
[ /code ]
}

Pasado este punto mi primera recomendación es que revises el ejemplo que viene con Delphi de un editor mdi ya que aprenderás mucho acerca de cómo manejar ventanas mdi incluyendo los comandos "Nuevo", "Abrir", etc.

Ahora bien, hay que tener claras algunas cosas.

En algún momento preguntaste por cómo llamar a un procedimiento que tiene un parámetro por referencia. Tu pregunta se originó por tu deseo de llamar desde la ventana padre al procedimiento para cerrar la ventana hija.

Aquí hay una confusión de conceptos. El procedimiento para cerrar una ventana es Close mientras que OnCloseQuery es el evento que se genera cuando se intenta cerrar una ventana, sea cual sea el método con el que se cierre (procedimiento Close, hacer click en el cuadrito de la cruz, oprimir Alt-F4, etc.)

Por regla general los eventos no deben llamarse explícitamente ya que no es esa su funcionalidad. Para entender esto conviene separar estos dos conceptos:
  • evento
  • manejador de evento

El evento lo genera "el sistema" y el manejador es el código que responde al evento. Tu parte como programador es escribir el manejador y dejar que el sistema genere el evento (es decir, que sea el sistema el que llame a tu manejador).

Para concretizar pensemos en el evento en particular de CloseQuery:

Cuando un usuario quiere cerrar una ventana (sea cual sea el método que utilice) el sistema (en este caso Windows) manda la señal WM_CLOSE a la ventana. Delphi intercepta esta señal y toma uno de dos caminos:
  • Si no hay manejador disponible (no definiste OnCloseQuery) deja que el evento siga su marcha normal y el resultado es que la ventana se cierra.
  • Si hay un manejador disponible entonces lo llama así:
    Código:

    var
      CanClose: Boolean;

    begin
      CanClose := true;
      OnCloseQuery(..., CanClose);
      if CanClose then
        permite cerrar la ventana
      else
        impide cerrar la ventana;
    end;

    Es decir, llama al manejador y dependiendo de lo que éste decida (mediante CanClose), permite o no cerrar la ventana.

Con esto verás que es inútil que tú intentes llamar directamente al manejador ya que es Delphi y no tú quien puede aprovechar el valor de CanClose.

Aplicando esto a tu problema específico: según me da la impresión tu intentas o intentabas usar el manejador OnCloseQuery de una ventana hija desde la ventana padre para controlar si el editor está o no modificado y en su caso presentar un mensaje. Pero esta no es labor de la ventana padre. Es la ventana hija (cada ventana hija) la encargada de determinar esto. Por eso tu código:

Código:

with (ActiveMDIChild as TVentana_Hija) do
begin
  if (RE_Editor.Modified) then
  begin
    Respuesta := Application.MessageBox('¿Desea guardar los cambios? ',
      ' Guardar',mb_IconWarning + mb_yesNoCancel);

    Case Respuesta of
      idYes:
      begin
        SD_Guardar_Fichero.Filename := Caption;
        if (FileExists (SD_Guardar_Fichero.Filename)) then
        begin
          RE_Editor.Lines.SaveToFile(SD_Guardar_Fichero.Filename);
          Close;
        end
        else
          etc

es conceptualmente incorrecto y, por tanto, propenso a los problemas que enfrentas.

Todo el código para determinar si el editor tiene cambios o no, presentar un mensaje, guardar en su caso los cambios, etc. es labor de la ventana hija que esté a punto de cerrarse. Todo esto debe ir en el manejador de CloseQuery.

Piénsalo de esta forma: la ventana hija es un ente independiente de su padre. Es una ventana cuya función en la vida es permitir la edición de textos. Si el padre desaparece (por ejemplo si el día de mañana decides que tu aplicación sólo edite un archivo a la vez) la ventana hija sigue estando lista para hacer todo el trabajo relacionado con el archivo a editar.

La labor del padre es controlar a las hijas independientemente de qué hagan estas hijas: podrías tener distintos tipos de ventanas hijas (editores, calculadoras, gráficos, etc.) y con tú método el padre tendría que ocuparse de todos los posibles casos lo que a todas luces es inconveniente.

El control del padre radica, por ejemplo, en acomodar a las ventanas dentro de su área, mantener una lista de las ventanas abiertas, etc. También se puede encargar de mandar comandos a las ventanas pero dejar que ellas hagan su trabajo. Así por ejemplo, cuando en Word el usuario oprime el botón de guardar, la ventana padre manda llamar al procedimiento Guardar de la ventana activa (ActiveMdiChild) pero nada más; es ésta ventana activa la que se encarga de preguntar al usuario el nombre del archivo, etc.

Si el usuario usa el menú Archivo|Cerrar, la ventana padre se limita a llamar a algún procedimiento de la ventana hija activa del estilo de Close y es ésta última quien se encarga de todo lo demás.

Otro punto de vista para aclarar:

¿Qué pasa cuando el usuario desea apagar la PC?

Puedes considerar a Windows como la ventana padre. Como tal, él se encarga de llamar al procedimiento Close de cada ventana abierta pero estarás de acuerdo que es cada una de éstas, y no Windows, quien hace el trabajo de limpieza (guardar, advertir de cambios, etc.)

Así pues, si deseas que la ventana padre cierre a una hija limítate a llamar al método Close de la hija y deja que ésta haga todo (bueno, tú pero a través de ella).


Yo sé que todo esto es un rollazo pero espero que te sirva para entender que en ocasiones tenemos muchos problemas con el código por la "simple" razón de que estamos enfocando mal el problema.

El resumen aquí podría ser:

Si quieres hacer un editor de textos mdi piensa primero en el problema más sencillo de cómo hacer un editor de una sóla ventana. Programas esta ventana, te peleas con todos los problemas que salgan, etc. Una vez que lo dominas, le pones a la ventana la propiedad MdiChild y la insertas en un proyecto Mdi. Todo, absolutamente todo (te lo aseguro) será mucho más sencillo.


// Saludos

wallesis 27-01-2004 19:22:01

Hola
Gracias Roman por tu aportación. En primer lugar darte las gracias de nuevo por tu aclaración sobre la etiqueta [code], la utilizaré en adelante.

Comentas al principio que eche un vistazo al ejemplo de Delphi sobre Aplicaciones MDI. Si que lo he visto y estudiado, y no sólo ese ejercicio sino que tengo otros 4 ejercicios similares bajados de Internet. Gracias a ellos he llegado a donde estoy (sólo me queda codificar el cierre de ventanas), y por supuesto a las aportaciones de los compañeros del foro.
Tengo que deciros que mi relación con Delphi se remonta a penas unos meses y estudiandolo mediante manuales. Ni siquiera tengo experiencia en otros lenguajes.Tambien aprobecho para disculparme si mi lenguaje sobre el tema no es lo suficientemente claro y conciso.

Has explicado muy bien la relacion entre ventanas padre y ventanas hijas y creeme, no ha sido ningún rollo. Para mi cada mensaje vuestro es toda una lección de programación a pesar de que hay algunos conceptos que se me escapan.

Gracias.

roman 27-01-2004 19:39:15

Cita:

Empezado por wallesis
Tambien aprobecho para disculparme si mi lenguaje sobre el tema no es lo suficientemente claro y conciso.

De esto ni hablar. Lo único malo de la ignorancia es no intentar salir de ella. En tu caso es claro que deseas aprender.

// Saludos


La franja horaria es GMT +2. Ahora son las 18:43:09.

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