Formularios

10

Click here to load reader

Transcript of Formularios

Page 1: Formularios

El Rinconcito de Delphi

Formularios

El presente artículo se publica tal cual por su autor.Cualquier manipulación o publicación sin la debidaautorización puede vulnerar los derechos de autor.

Pablo Reyes

[email protected]

Page 2: Formularios

Tutoriales Delphi 7

Introducción

Formularios Los formularios son el punto de comunicación entre una aplicación y sus usuarios. Delphi nos permite crear formularios de manera visual generando el código fuente necesario para pueda ser mostrado por la aplicación en tiempo de ejecución. En este tutorial vamos a conocer los detalles que hay por detrás de un formulario Delphi.

Algunos conceptos de programación orientada a objetos No nos vamos a sumergir en las profundidades de la programación orientada a objetos pero para entender un poco mejor qué es un formulario vamos a darle un vistazo a la superficie. Un formulario es un objeto. Un objeto es una instancia de una clase. Por lo tanto cuando creamos un formulario estamos creando una clase. Esta clase no la creamos desde cero sino a partir de una clase existente. Es decir, heredamos funcionalidad de otra clase. ¿De qué clase? De la clase TForm. Cuando creamos un formulario nuevo, Delphi genera el siguiente código: unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} end.

Prestemos atención a todo lo que dice Form1. Por un lado está la declaración de la clase del formulario, es decir, de la clase TForm1. Por otro la declaración de la variable Form1 del tipo TForm1. Si modificamos el nombre del formulario, es decir, el valor de su propiedad Name el nombre de la clase y el de la variable también cambiarán. Para que no queden dudas: si cambiamos el valor de la propiedad Name de este formulario a, por ejemplo, FRMPrueba, el nombre de la clase cambiará a TFRMPrueba y el nombre de la variable a FRMPrueba. Este comportamiento puede ser un poco confuso. Parece

Page 3: Formularios

como si entre el valor de la propiedad Name, el nombre de la clase del formulario y el nombre de la variable hubiera una relación muy estrecha. Pues bien, no la hay. Delphi hace estos cambios para que nos resulte más fácil trabajar con el formulario pero la realidad demuestra que no hay ninguna relación entre estos tres elementos. Vamos a hacer un ejemplo para comprobar lo que acabo de decir. Crear un proyecto nuevo. Para ello seleccionar del menú File | New | Application. Añadir al formulario principal los siguientes componentes:

• De la página Standard, un Label, un Edit y dos Button. El formulario debería verse así:

Como se puede ver en la imagen de pantalla, modifiqué el valor de la propiedad Caption del Label y de los dos Button y el valor de la propiedad Text del Edit. Lo que vamos a hacer es escribir código para el evento OnClick de los dos Button. En el caso del Button "GetName" simplemente vamos a mostrar el nombre del formulario en el Label. En el caso del Button "SetName" vamos a asignarle un valor a la propiedad Name del formulario. El código del evento OnClick del Button "GetName" es el siguiente: procedure TForm1.Button1Click(Sender: TObject); begin Label1.Caption := Format('Form.Name = %s', [Form1.Name]); end;

El código del evento OnClick del Button "SetName" es el siguiente: procedure TForm1.Button2Click(Sender: TObject); begin Form1.Name := Edit1.Text; end;

Como puede verse claramente en el código de arriba, estoy haciendo referencia al formulario por su nombre o, mejor dicho, por la variable que inicialmente tiene el mismo nombre que el formulario. Al cambiar el valor de la propiedad Name el código sigue funcionando perfectamente. Lo vuelvo a decir. No existe una relación implícita entre el nombre de la clase del formulario, el nombre de la variable del formulario y el nombre del formulario, es decir, el valor de la propiedad Name. Guardar el proyecto, compilarlo y ejecutarlo. Un detalle adicional es que al cambiar el nombre del formulario, es decir (insisto), el valor de su propiedad Name también cambia el título del formulario, es decir, el valor de su propiedad Caption. Cuando el valor de la propiedad Caption es igual al valor de la propiedad Name, si cambiamos el valor de la propiedad Name también cambia el valor de la propiedad Caption.

Page 4: Formularios

Creación de formularios Cuando ejecutamos la aplicación, Delphi se encarga de mostrar el formulario principal. Lo que ocurre detrás de escena es que Delphi se encarga de crear una instancia de la clase del formulario, es decir, un objeto del tipo TForm1, y asignar una referencia a dicho objeto en la variable Form1. ¿Dónde lo hace? En el código fuente del proyecto podemos verlo claramente. Para visualizar el código fuente del proyecto seleccionar del menú Project | View Source. La siguiente línea: Application.CreateForm(TForm1, Form1);

Se encarga de crear una instancia de la clase TForm1 y asignar una referencia al objeto creado en la variable Form1. Ambos elementos, es decir, la clase TForm1 y la variable Form1 están declarados en la unidad Unit1, incluida en la cláusula uses del proyecto. Hagamos un pequeño experimento. Añadir al formulario principal el siguiente componente:

• De la página Standard, un Button. Asignarle a su propiedad Caption el valor "Form1A". Ahora vamos a declarar una variable llamada Form1A del tipo TForm1, o sea, el formulario. El código es el siguiente: var Form1: TForm1; Form1A: TForm1;

Hemos declarado la variable Form1A debajo de la declaración de la variable Form1. Escribir el siguiente código para el evento OnClick del Button "Form1A": procedure TForm1.Button3Click(Sender: TObject); begin Form1A := Form1; ShowMessage(Form1A.Name); end;

Cualquier variable declarada como TForm1 es capaz de mantener una referencia a un objeto del tipo TForm1. Como la variable Form1 es del tipo TForm1 y la variable Form1A también es del tipo TForm1 significa que ambas variables son compatibles y por lo tanto podemos asignarle el valor de una a la otra. Esto es precisamente lo que hemos hecho y el código funciona perfectamente. Vamos a hacer otro experimento. Añadir el siguiente componente al formulario:

• De la página Standard, un Button. Asignarle a su propiedad Caption el valor "Nuevo". Escribir el siguiente código para el evento OnClick del Button "Nuevo": procedure TForm1.Button4Click(Sender: TObject); begin Application.CreateForm(TForm1, Form1A); Form1A.Left := Form1A.Left + 10; Form1A.Top := Form1A.Top + 10; Form1A.Show; end;

Page 5: Formularios

Lo que estamos haciendo aquí es creando una nueva instancia de la clase TForm1 y asignándole a la variable Form1A una referencia al objeto creado. Luego modificamos el valor de las propiedades Left y Top para que al mostrar el nuevo formulario no aparezca en la misma posición que el formulario que ya tenemos. Ahora el comportamiento del código es un poco extraño. Si modificamos el nombre del formulario desde el nuevo objeto vemos como cambian las propiedades del formulario que ya teníamos. Esto es así porque ahora tenemos dos objetos del tipo TForm1. El primero creado al ejecutarse la aplicación y el segundo al hacer clic en el Button "Nuevo". En la variable Form1 tenemos una referencia al primero y en la variable Form1A tenemos una referencia al segundo.

La palabra reservada Self La palabra reservada Self nos permite hacer referencia al objeto para el cual estamos ejecutando un método. Si en lugar de escribir código como el que escribimos: Label1.Caption := Format('Form.Name = %s', [Form1.Name]); Form1.Name := Edit1.Text;

Hubiéramos utilizado la palabra reservada Self, el código hubiera tenido efecto sobre el objeto en el cual lo ejecutamos y no sobre el objeto al cual hace referencia la variable utilizada. Vamos a cambiar el código y a ejecutar el proyecto para comprobar todo esto. Ahora bien, ¿qué pasa si hacemos clic más de una vez sobre el Button "Nuevo"? El código del evento OnClick se encarga de crear una nueva instancia de la clase TForm1 y de asignar una referencia al objeto creado en la variable Form1A. Pues bien, cada vez que hacemos clic en el Button "Nuevo" creamos un nuevo formulario TForm1. ¿Qué pasa con el anterior? No pasa nada. Sólo que sigue existiendo en memoria pero perdimos toda referencia al mismo. En la variable Form1A sólo mantenemos una referencia al último formulario creado. Sin embargo el código sigue funcionando porque tiene efecto sobre el objeto en el cual lo ejecutamos.

Más sobre creación de formularios Vamos a añadir otro formulario al proyecto. Para ello seleccionar del menú File | New | From. A través de las opciones del proyecto podemos decirle a Delphi que se encargue de crear una instancia de la clase del nuevo formulario al ejecutar la aplicación. Seleccionar del menú Project | Options... y seleccionar la página Forms.

Page 6: Formularios

Los formularios que están en la lista Auto-create forms: son creados automáticamente por Delphi al ejecutar la aplicación. Los formularios que están en la lista Available forms: no son creados automáticamente por Delphi al ejecutar la aplicación. Simplemente están disponibles para que nosotros explícitamente por código nos encarguemos de crearlos cuando los necesitamos. Todas las clases que se pueden crear en Delphi tienen un ancestro común en la clase TObject. Esta clase es responsable de proveer la funcionalidad básica de todos los objetos. Para ello implementa un constructor y un destructor responsables de crear objetos y de destruirlos. Por convención de nombres, el nombre del constructor es siempre create y el nombre del destructor destroy. Para crear explícitamente por código un formulario tenemos que utilizar el constructor de su clase. Vamos a colocar el segundo formulario en la lista Available forms:. Podemos controlar a qué lista van a parar los formularios nuevos a través de las opciones del entorno de desarrollo. Seleccionar del menú Tools | Environment Options... y luego seleccionar la página Designer. En el recuadro Module creation options el campo Auto create forms & data modules controla a qué lista van a parar los formularios nuevos. Si el campo está marcado entonces los formularios nuevos van a parar a la lista Auto-create forms:. Si el campo está desmarcado entonces van a para a la otra lista.

Page 7: Formularios

Añadir el siguiente componente al formulario principal:

• De la página Additional, un SpeedButton. Escribir el siguiente código para el evento OnClick del SpeedButton: procedure TForm1.SpeedButton1Click(Sender: TObject); begin Form2 := TForm2.Create(Self); Form2.Show; end;

Presionar Ctrl+F9 para compilar. Obtenemos el siguiente mensaje:

El compilador se da cuenta de que estamos haciendo referencia a cosas declaradas en la unidad Unit2 y nos ofrece añadir dicha unidad en la cláusula uses de la unidad Unit1 que es la unidad en la que estamos codificando. Si contestamos sí, Delphi se encarga de añadir la unidad Unit2 en la cláusula uses de la sección implementation de la unidad Unit1. Ahora si presionamos Ctrl+F9 nuevamente la aplicación se compilará sin errores.

Page 8: Formularios

Lo que hicimos en el código anterior fue crear una instancia de la clase TForm2 y asignarle una referencia a la variable Form2. Luego ejecutamos el método Show de la clase TForm, la clase de la que desciende TForm2, para mostrar el formulario. Tenemos que llamar al método Show para mostrar el formulario porque por defecto la propiedad Visible tiene el valor "False". En realidad lo único que hace el método Show es asignarle el valor "True" a la propiedad Visible. También podemos mostrar el formulario llamando al método ShowModal, En este caso el comportamiento será diferente ya que mostraremos al formulario de manera modal. Un formulario modal no nos permite acceder a ningún otro formulario de la aplicación hasta que lo cerremos. Cuando cerramos un formulario el formulario no se destruye sino que se oculta, es decir, su propiedad Visible pasa a tener el valor "False". Esto mismo lo logramos haciendo una llamada al método Hide. Hagamos una prueba. Añadir el siguiente componente al segundo formulario:

• De la página Additional, un BitButton. Escribir el siguiente código para el evento OnClick del BitButton: procedure TForm2.BitBtn1Click(Sender: TObject); begin Hide; end;

Hacer clic en el BitButton tiene el mismo efector que cerrar el formulario desde el botón de Windows. Cada vez que un formulario se cierra se dispara el evento OnClose. En este evento podemos indicarle a Delphi que queremos que haga al cerrar el formulario. Esto lo hacemos a través del parámetro Action al que le podemos asignar uno de los siguientes valores:

• caNone: Delphi no hace nada. Es una forma de evitar que el usuario cierre el formulario desde el botón de Windows.

• caHide: el formulario se oculta. • caFree: el formulario se destruye. • caMinimize: el formulario se minimiza.

Como dije antes, la acción por defecto es caHide. Vamos a modificar el evento OnClose del segundo formulario para que al cerrarlo se destruya. El código es el siguiente: procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end;

Desde el punto de vista visual no hay ninguna diferencia. Pero podemos estar seguros de que al cerrarlo el formulario se ha destruido. Vamos a comprobarlo. Añadir el siguiente componente al formulario principal:

• De la página Additional, un BitButton. Escribir el siguiente código para el evento OnClick del nuevo BitButton: procedure TForm1.BitBtn1Click(Sender: TObject); begin Form2.Show; end;

¿Qué pasa con la variable Form2?

Page 9: Formularios

Luego de destruir el formulario tiene basura. Cuando creamos el formulario "Form2" asignamos una referencia a la variable Form2. Al destruir el formulario la variable hace referencia a un objeto que no existe. Cuando una variable no hace referencia a ningún objeto tiene el valor nil. Cada vez que destruimos el formulario se dispara el evento OnDestroy. Vamos a escribir código para este evento para evitar que la variable Form2 haga referencia a un objeto que no existe. El código es el siguiente: procedure TForm2.FormDestroy(Sender: TObject); begin Form2 := nil; end;

Como broche de oro ahora estamos en condiciones de controlar si ya existe una instancia de TForm2 y en ese caso en lugar de crear una nueva instancia simplemente podemos mostrar la que ya existe. Vamos a modificar el código del evento OnClick del SpeedButton del formulario para que se vea así: procedure TForm1.SpeedButton1Click(Sender: TObject); begin if Form2 = nil then Form2 := TForm2.Create(Self); Form2.Show; end;

Antes de crear una instancia de TForm2 nos aseguramos que la variable Form2 sea igual a nil. De esta forma podemos garantizar que sólo habrá una instancia de la clase TForm2. Este código es muy utilizado en aplicaciones Delphi. Aún cuando sólo ocultemos el formulario Form2 el resultado será siempre el correcto. Si ocultamos el formulario Form2 haciendo clic en el BitButton no se disparará el evento OnClose por lo que el formulario no se destruirá y por lo tanto tampoco se disparará el evento OnDestroy por lo que la variable Form2 tendrá un valor distinto de nil.

Destruyendo formularios Tenemos varias formas de destruir un formulario. Una de ellas acabamos de verla en el ejemplo anterior. También podemos destruir un formulario llamado directamente al método destructor, es decir, al método Destroy. Sin embargo esta no es la forma más segura ya que si la variable que utilizamos para hacer referencia al objeto en realidad está haciendo referencia a un objeto que no existe la llamada al método Destroy generará un mensaje de error. Para evitar este problema debemos llamar al método Free. El método Free es más seguro que el método Destroy ya que antes de llamar al método Destroy verifica que la referencia al objeto sea válida. Si destruimos explícitamente un formulario no debemos olvidarnos de asignarle el valor nil a la variable que lo referencia. Vamos a destruir explícitamente el formulario Form2. Modificar el código del evento OnClick del SpeedButton del formulario principal para que se vea así: procedure TForm1.SpeedButton1Click(Sender: TObject); begin if Form2 = nil then begin Form2 := TForm2.Create(Self); Form2.Show; end

Page 10: Formularios

else begin Form2.Free; Form2 := nil; end; end;

Cuando la variable Form2 es igual a nil creamos una instancia de TForm2 y la mostramos. Cuando la variable Form2 es distinto de nil destruimos el formulario y le asignamos a la variable Form2 el valor nil.