Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   OOP (https://www.clubdelphi.com/foros/forumdisplay.php?f=5)
-   -   Optimizando Creación de Formularios MDI (https://www.clubdelphi.com/foros/showthread.php?t=51504)

nelostanley 17-12-2007 19:30:15

Optimizando Creación de Formularios MDI
 
Hola Gente,

Ando ya varios dias buscando pero no con mucha suerte algo referente a una duda que tengo en esto de las aplicaciones MDI.

Todo esta ok con referente al formulario padre e hijos, llamados, liberacion de memoria etc, funciona de 10, pero mi aplicación tiene como 30 Forms child y a la hora del llamado es algo engorroso a lo cual se viene mi duda de tener una funcion o procedimiento que me pueda crear mis formularios child ya establecidos.

Llamo a los formularios de la sgte forma:


Código Delphi [-]
  if not Assigned(frmC1) Then begin
    frmC1 := TfrmC1.Create(Application);
    frmC1.Show;
  end
  else begin
    if frmC1.WindowState = wsMinimized then  frmC1.WindowState := wsNormal;
    frmC1.BringToFront;
  end;




Ahora me entenderan de poner todo eso en los mas de 30 onlclick para llamar a los Forms que tengo en la aplicacion, por eso me parece que un procedimiento .....


Código Delphi [-]
       LlamarFormularioChild(NombreForm) ;

.... seria mucho más practico pero nose como hacerlo.

Pero de tanto revisar me encontre algo :


Código Delphi [-]
procedure TfrmParent.CrearVentanaMDIHija(Nombre: string);
begin
  LockWindowUpdate(Handle);
  with TFrmChild.Create(Self) do
  begin
    Caption := Nombre;
    if FileExists(Nombre) then
      reMain.Lines.LoadFromFile(Nombre);
    reMain.Modified := False;
    WindowState := wsMaximized;
  end;
  LockWindowUpdate(0);
end;


Pero no logro acoplarlo a mi codigo, porque el parametro es un string y mi frmC1 es un objeto TfrmC1.

Nota: Uso D7.

De antemano muchas gracias.

ElDioni 20-12-2007 16:52:52

Creo que la instruccion FindComponent podría serte de ayuda para lo que quieres hacer, espero que así sea. puedes ver ejemplos de su utilización en estos hilos:

http://www.clubdelphi.com/foros/show...=FindComponent

http://www.clubdelphi.com/foros/show...=FindComponent

http://www.clubdelphi.com/foros/show...=FindComponent

Un saludo.

nelostanley 26-12-2007 23:59:17

sigo penando ...
 
Esto de optimizar me parece complicado, revise tus hilos recomendados pero ninguno llena la expectativa, lo mio es aparentemente simple una funcion que cree los Form child, dichos form ya tiene sus componentes objetos etc.

De antemano muhas gracias.

jcarteagaf 04-01-2008 03:44:40

Servira esto?
 
Yo uso la siguiente funcion

Código Delphi [-]
function ShowForm(TfrmClass: TFormClass) : boolean;
begin
  with TfrmClass.Create(Application) do
  begin
    result := ShowModal = mrOk;
    Free;
  end;
end;

Y la llamo asi

ShowForm(tfrmVentas);

Se que son para ventanas modales pero podrias acomodarlas para MDI.

Saludos

Lepe 04-01-2008 13:57:30

Yo uso una pequeña variante de jcarteagaf. Al hacerlo así, tenemos que añadir los "uses" de la ventana que queremos crear; con el tiempo y muchos Forms hijos, acabamos con referencias circulares y un dolor de cabeza.

Código Delphi [-]
type TOpen = (oCliente, oFactura, oProductos);

var  OpenForm : Array [TOpen] of TForm = (TFrmcliente, TfrmFactura, TFrmProductos);

function ShowForm(Abrir : TOpen) :TForm;
begin
 Result := TForm(OpenForm(Abrir)).Create(Application)
end;

El tipo "TOpen" lo añado a una unidad publica.pas (sin form asociado), todas las ventanas hacen uso de ella.

Cada ventana hija, tiene en el Onclose Action := cafree, por lo que se libera de memoria. Tampoco uso las variables globales FrmCliente, FrmFactura que propone delphi.

Saludos

maeyanes 04-01-2008 16:30:58

Hola...

Yo uso un procedimiento como este:

Código Delphi [-]
procedure ShowMDIChildForm(AFormClass: TFormClass);
var
  I: Integer;
  AForm: TForm;

begin
  for I := 0 to Pred(Application.MainForm.MDIChildCount) do
  begin
    AForm := Application.MainForm.MDIChildren[i];
    if AForm is AFormClass then
    begin
      if IsIconic(AForm.Handle) then
        ShowWindow(AForm.Handle, SW_RESTORE);
      AForm.BringToFront;
      Exit
    end
  end;
  AFormClass.Create(nil)
end;

Saludos...

Chris 04-01-2008 17:00:40

Tomando lo mejor de todos
 
Me disculpan apreciados compañeros, pero en mi humilde conocimiento al respecto me parecen que han estado escribiendo código -que muy bien puede servir- no es el más adecuado a mi parecer.

El código original propuesto por nelostanley es el que devería de seguirse.

Acá mi sugerencia:
Código Delphi [-]
Procedure TMainForm.CreateOrRestoreForm(aForm : TForm);
  if not Assigned(aForm) Then begin
    aForm := TFormClass(aForm).Create(Application);
    aForm.Show;
  end
  else begin
    if aForm.WindowState = wsMinimized then  
        aForm.WindowState := wsNormal;
    aForm.BringToFront;
  end;[FONT=verdana,geneva,lucida,'lucida grande',arial,helvetica,sans-serif]
end;
Espero sirva de algo. Saludo.
[/font]

nelostanley 04-01-2008 23:52:32

afinando algunas dudas
 
Antes de todo, muchas gracias por su ayuda a todos, les cuento que ya me estaba resignando y tuve que poner todo mi "extenso" codigo en cada onclick :).

Vayamos al grano ....

Probe dos sugerencias que me venian como anillo al dedo ... las demas se ven interesantes pero al tratar de hacerlas me complique un poco asi que las obvie.

del amigo maeyanes

..... funciono casi perfecto, pero hay un detalle, cuando dentro del mismo formulario quiero a acceder a:

Código Delphi [-]
      if (frmC1.Text1.text ='') then ....

muestra el clasico error "Access Violation" ..... al revisar paso a paso muestra "inaccesible value" .... entonces decide cambiar a ....:

Código Delphi [-]
      if (Text1.text ='') then ....

entonces compilo y funciono correctamente ...entonces va mi pregunta existe alguna forma que funcione el procedimiento .... indicando la referencia del frmC1 ?? el motivo que no funcione ??.... segun mi humilde opinion es porque al declarar TfrmClass implica ya por defecto los componentes de la clase del Form??


Pero debo serles sincero me gusto a primera vista los del amigo D&W obviamente entenderan que esta casi un 90% de mi codigo original. Pero compila muy bien pero al llamar al procedimiento me manda el clasico error "Access Violation" ..... quice pincelear un poco el codigo pero ....no pude.

Quizas sino fuera molestia y para cerrar este hilo me gustaria que aclararan mis dudas haciendo referencia a las propuesta de D&W y maeyanes.


De antemano muchisimas gracias.

Lepe 05-01-2008 02:00:11

¿código adecuado? Perdona D&W, pero no se ha dicho si quiere una instancia o varias del mismo Form, hecho que nos haría modificar bastante nuestro código.

Tampoco se dice si se quiere tener una referencia a la ventana creada, o no le importa.

Código adecuado no creo que exista, lo tendrá que adaptar a sus necesidades y a su gusto. Al menos yo pretendía dar ideas, después él tendrá que adaptarlo.

Cita:

Empezado por D&W (Mensaje 255986)
Código Delphi [-]
Procedure TMainForm.CreateOrRestoreForm(aForm : TForm);
1  if not Assigned(aForm) Then begin
2    aForm := TFormClass(aForm).Create(Application);
3    aForm.Show;
4  end
5  else begin
6    if aForm.WindowState = wsMinimized then  
7        aForm.WindowState := wsNormal;
8    aForm.BringToFront;
9  end;

En cuanto a tu código, no entiendo muy bien como harías una llamada a esa rutina, porque el parámetro es de tipo TForm y después haces un moldeo de tipos a TFormClass en la línea 2. Que conste que no es una crítica, es más bien una duda.


Otros comentarios:
- La línea 3 puede quitarse, como son mdichild, al crear la ventana se muestra por defecto.

nelostanley en el código de maeyanes, no verás ninguna parte hacer referencia a frmC1, por eso no puedes hacer referencia con frmC1.LoQueSea. Aunque es fácil convertir ese procedimiento a una función y que devuelva el "aForm".

Saludos

nelostanley 05-01-2008 02:09:07

un empujonsito mas ...
 
Cita:

Empezado por Lepe;256131

[B
nelostanley[/b] en el código de maeyanes, no verás ninguna parte hacer referencia a frmC1, por eso no puedes hacer referencia con frmC1.LoQueSea. Aunque es fácil convertir ese procedimiento a una función y que devuelva el "aForm".

Saludos

Obviamente para un novato como yo, necesito hacer referencia a los frmC1.loquesea, te agradeceria decirme como.

Gracias por tu tiempo.

Lepe 05-01-2008 02:23:42

Dentro de un tiempo, no tanto como crees, sonreirás al ver este mensaje ;). Te invito a que intentes deducir qué hace el código metiendo ShowMessages, cambiando líneas de código, etc, ya que es la única forma de aprender.

Código Delphi [-]
function ShowMDIChildForm(AFormClass: TFormClass):TForm;
var
  I: Integer;
begin
  for I := 0 to Pred(Application.MainForm.MDIChildCount) do
  begin
    Result := Application.MainForm.MDIChildren[i];
    if Result is AFormClass then
    begin
      if IsIconic(Result.Handle) then
        ShowWindow(Result.Handle, SW_RESTORE);
      Result.BringToFront;
      Exit
    end
  end;
  Result := AFormClass.Create(nil) // ejem, yo en lugar de "nil" pondría "Application"
// es más seguro si tienes varias ventanas hijas abiertas y le das a cerrar
// la ventana principal.
end;

Ahora puedes llamar a esa rutina así:

Código Delphi [-]
procedure Mio;
var F: TFrmClientes;
begin
  F=:  TFrmClientes(ShowMDIChildForm(TFrmClientes));
  F.LoqueSea

Saludos

nelostanley 05-01-2008 19:52:31

no da....
 
Compila correcto ... pero cuando haces refrencia a F.Cualquiercosa sigue el error de "Access Violation".

Ejm. Cuando en un reporte R1 quieres hacer refrencia algo de F1.CualquierCosa, te tira el mismo error, cosa que no sucede en lo absoluto cuando llamas al formulario.

Código Delphi [-]
  if not Assigned(frmC1) Then begin
    frmC1 := TfrmC1.Create(Application);
    frmC1.Show;
  end  else begin
    if frmC1.WindowState = wsMinimized then 
          frmC1.WindowState := wsNormal;
    frmC1.BringToFront;
  end;

Asi corre de 10 !! pero cuando lo llevas a la funcion o procedimiento no funcina las refrencias.

Para evitarme recodificar varias cosas he optado por llamar algunos formularios directamente sin funcion o procedimiento.

Ojala alguien tenga un poquito de paciencia, sino de todas formas gracias.

jachguate 05-01-2008 20:38:02

Supongo que no estas haciendo la asignación al llamar a la función.

La llamada debiera lucir algo como esto:

Código Delphi [-]
  Form1 := ShowMDIChildForm(TForm1) as TForm1;

Hasta luego.

;)

Lepe 06-01-2008 10:20:39

Cita:

Empezado por nelostanley (Mensaje 256278)
Compila correcto ... pero cuando haces refrencia a F.Cualquiercosa sigue el error de "Access Violation".

Otro detalle que no se ha comentado.

En la Unit del TForm1, no harás referencia para nada a FrmC1 ¿verdad?

Me explico, si tienes un código así:
Código Delphi [-]
procedure TForm1.ElEventoQuesea(...);
begin 
   frmC1.Loquesea
end;

Eso dará un Access Violation, porque estamos creando la ventana con la variable "F" (desde nuestra rutina), pero en el TForm1 estamos usando la variable frmC1 que no ha sido creada, tiene un puntero nulo.

Saludos

Chris 07-01-2008 17:49:36

Cita:

Empezado por Lepe (Mensaje 256131)
... Perdona D&W, pero no se ha dicho si quiere una instancia o varias del mismo Form, hecho que nos haría modificar bastante nuestro código.

Tampoco se dice si se quiere tener una referencia a la ventana creada, o no le importa.

Disculpa Lepe, pero de que haya una o varias instancias del mismo form, eso depende de la variable que se le pase como paramentro a la función que postie. (Si siempre le paso Form1, sólo habrá una instancia de tipo TForm1 y será siempre Form1.

Cita:

Empezado por Lepe (Mensaje 256131)
Código adecuado no creo que exista, lo tendrá que adaptar a sus necesidades y a su gusto. Al menos yo pretendía dar ideas, después él tendrá que adaptarlo.

En el club, cuando posteamos un código, se da por sobre entendido que es un molde y en muchos casos una sugencia. Cuando dije "Poco adecuado" en ningún momento pretendí decir que era un mal código, al contrario.

Cita:

Empezado por Lepe (Mensaje 256131)
En cuanto a tu código, no entiendo muy bien como harías una llamada a esa rutina, porque el parámetro es de tipo TForm y después haces un moldeo de tipos a TFormClass en la línea 2. Que conste que no es una crítica, es más bien una duda.

Si yo pido como parámetro una variable tipo TFormClass entonces me tendrían que pasar por ejemplo TForm1 en vez de Form1 (según entiendo Hasta donde llega mi limitada comprensión del lenguaje). De todos modos, luego que postie el código me dí cuenta que produciía el Acccess Violation que me mencionó nelostanley , fue entonces que ví que había que pedir dos parámentros en vez de sólo uno -el nombre de la varíable y el tipo de variable o form- no puede postear los cambios porque no me dio tiempo de hacerlo.

Cita:

Empezado por Lepe (Mensaje 256131)
Otros comentarios:
- La línea 3 puede quitarse, como son mdichild, al crear la ventana se muestra por defecto.

Gracias por la aclaración, ya lo sabía. También sé que no está de más la linea y que de esa forma la función también puede servir para todo tipo de formularios, ya sean mdiChild o mdiNormal.

aquí va la modificación, espero sirva.
Código Delphi [-]
Procedure TMainForm.CreateOrRestoreForm(aForm : TForm, aFormClass : TFormClass);
  if not Assigned(aForm) Then begin
    aForm := aFormClass.Create(Application);
    aForm.Show;
  end
  else begin
    if aForm.WindowState = wsMinimized then  
        aForm.WindowState := wsNormal;
    aForm.BringToFront;
  end;
end;
Para llamar a la función lo haces así:
Código Delphi [-]
CreateOrRestoreForm(frmC1,TfrmC1);

jachguate 07-01-2008 17:56:02

Solo una observación.

Cita:

Empezado por D&W (Mensaje 256504)
aquí va la modificación, espero sirva.
Código Delphi [-]
Procedure TMainForm.CreateOrRestoreForm(aForm : TForm, aFormClass : TFormClass);
  if not Assigned(aForm) Then begin
    aForm := aFormClass.Create(Application);
    aForm.Show;
  end
  else begin
    if aForm.WindowState = wsMinimized then  
        aForm.WindowState := wsNormal;
    aForm.BringToFront;
  end;
end;

Con esta rutina propuesta, ninguna asignación ocurre sobre la variable de formulario que se pasa como parámetro.

Si se hacen dos llamadas consecutivas, por ejemplo:

Código Delphi [-]
var
  Form1: TForm1;
begin
  MainForm.CreateOrRestoreForm(Form1, TForm1);
  MainForm.CreateOrRestoreForm(Form1, TForm1);
end;

Se tendrá dos instancias de TForm1.

Hasta luego.

;)

Sugerencia: Si querés crear una rutina que asigne el valor de esa variable, date una vuelta por el código fuente del método CreateForm de la clase TApplication.

Chris 07-01-2008 18:28:43

Gracias por la observación jachguate tienes toda la razón. Nunca había probado el código hasta después de lo que me comentastes. Ya lo he reparado -según yo- :)
Código Delphi [-]
Procedure TMainForm.CreateOrRestoreForm(var aForm : TForm; aFormClass : TFormClass);
Begin
  if not Assigned(aForm) Then begin
    aForm := aFormClass.Create(Application);
    aForm.Show;
  end
  else begin
    if aForm.WindowState = wsMinimized then
        aForm.WindowState := wsNormal;
    aForm.BringToFront;
  end;
end;

Para llamarlo así:
Código Delphi [-]
CreateOrRestoreForm(TForm(Form2), TForm2);

Saludos.

jachguate 07-01-2008 18:31:40

Si ves el método que he propuesto, te darás cuenta que hay manera de evitar el moldeado de tipo en la llamada.

Hasta luego.

;)

Chris 07-01-2008 18:36:33

El moldeado es porque la declaración del procedimiento CreateOrRestoreForm a cambiado y ha quedado así:CreateOrRestoreForm(var aForm : TForm; aFormClass : TFormClass);

No sé que versión de Delphi usa, pero en la 7 no me deja compilar si no hago el moldeado ahora con la declaración Var.

Saludos.

jachguate 07-01-2008 19:38:53

claro, no te dejará en ninguna.

Quien esté interesado, puede darse una vuelta por el método propuesto y realizar la mejora necesaria a la rutina para que esto no sea necesario.

Hasta luego.

;)


La franja horaria es GMT +2. Ahora son las 02:10:43.

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