Está en la página 1de 10

Formularios

Pablo Reyes
PReyes@excite.com

El Rinconcito de Delphi

El presente artículo se publica tal cual por su autor.


Cualquier manipulación o publicación sin la debida
autorización puede vulnerar los derechos de autor.
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
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.
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;
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.
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.
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.
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?


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
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.

También podría gustarte