Está en la página 1de 10

Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.

Borland C++ Builder. Apéndice III. USO DE MÚTIPLES VENTANAS.

Los ejemplos que se han propuesto hasta ahora han sido tan simples que estaban compuestos
de tan sólo un formulario. En la práctica, la mayoría de los programas se sirven de múltiples
ventanas. Por regla general siempre existe una ventana principal, en la que se encuentra el
menú de opciones, y una serie de ventanas accesorias que pueden estar visibles siempre o
sólo cuando sean necesarias.

Un programa puede utilizar múltiples ventanas simplemente para solicitar una determinada
información, como puede ser el nombre del archivo que se desea abrir, o bien para facilitar la
edición de múltiples documentos. Las ventanas del primer tipo se suelen construir como
cuadros de diálogo, nombre con el que se conoce a aquellas ventanas que no pueden ser
redimensionadas, no cuentan con botones de maximizar o minimizar y que además son
modales, no permitiendo seguir trabajando con la aplicación hasta que no sean cerradas.

En el caso de que el programa sea capaz de permitir el trabajo con múltiples documentos de
forma simultánea, éstos pueden ser mostrados en ventanas totalmente independientes o bien
en una interfaz de documento múltiple, que se conoce como MDI.

Cuando una aplicación cuenta con varios formularios es normal hacer referencia a unos desde
otros. Para poder hacer referencia a un formulario desde un módulo de código que no es el
suyo, debemos añadir una directiva include facilitando el nombre del fichero de cabecera
correspondiente al módulo en el que está definido el formulario al que queremos acceder. La
forma más fácil es usar la opción Include Unit Hdr del menú File.

En un proyecto tan sólo puede existir un formulario principal, que es el que se muestra
inicialmente cuando se ejecuta el programa. Cualquiera de los formularios de nuestra
aplicación puede ser el formulario principal. Para establecerlo deberemos usar la opción
Options del menú Project, que da lugar a la siguiente ventana:

Juana Sánchez Rey 2002/2003 1


Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

En esta ventana se define cuál es el formulario principal, cuales son formularios autocreados
y cuáles estarán disponibles. En el ejemplo, Form1 es el formulario principal, Form2 es un
formulario autocreado, es decir, que podemos usarlo en cualquier momento sin ocuparnos de
ningún trabajo de preparación, y Form3 es un formulario disponible, por lo que no se crea
inicialmente, sino que el programa sólo cuenta con una plantilla o definición de sus
características. Para poder usar este último formulario tendremos que crearlo y liberar la
memoria ocupada por él cuando deje de sernos útil.

Creación de un formulario disponible

La creación de un formulario disponible se realiza mediante el operador new y un


constructor: un método especial que se encarga de asignar la memoria necesaria y llevar a
cabo todo el proceso de inicialización preciso. Este constructor se llama igual que la clase y
necesita como único parámetro una referencia a un componente, que será el dueño del
formulario que se está creando. Lo habitual es usar como parámetro el objeto this, que es
un puntero al propio formulario desde el que se está creando.

TForm3 * Formulario = new TForm3(this);

A partir de este momento podemos referenciar el nuevo formulario mediante el nombre que
le hemos dado, por ejemplo:

Formulario->Caption=”Soy el tercer formulario”;


Formulario->Show();

Destrucción de un formulario

Cuando el formulario ha sido creado automáticamente por el código del propio proyecto no
tenemos que preocuparnos por su destrucción. Sin embargo, cuando el formulario lo hemos
creado nosotros mismos, deberemos destruirlo liberando la memoria ocupada por éste. Para
ello se utiliza la función delete().

delete(Formulario);

Si lo que queremos es que el formulario se libere automáticamente cuando el usuario lo cierre


podemos usar el evento OnClose, asignando al parámetro Action el valor caFree.

Visualización de un formulario

Un formulario cuenta con los métodos Show(), que lo hace visible y Hide(), que lo
oculta. También podemos acceder directamente a la propiedad Visible, asignándole el
valor true o false.

Un formulario que se muestra por estos métodos es una ventana no modal, lo que significa
que el usuario puede acceder a otras ventanas del mismo programa sin necesidad de cerrar
ésta. Este es el tipo lógico de visualización cuando la ventana va a servir para mostrar
información. Sin embargo, si la ventana modifica en algún aspecto el entorno del programa
no debería permitirse el acceso al resto de las funciones hasta en tanto el formulario no sea
cerrado. Para conseguir esto bastará mostrarlo mediante el método ShowModal().
Juana Sánchez Rey 2002/2003 2
Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

Formularios MDI

Cada vez que se añade un nuevo formulario con la opción New Form, el formulario creado
tiene el estilo normal o por defecto, representado por la constante fsNormal. Este estilo de
formulario es el de una ventana independiente, que no actúa como contenedor de otras
ventanas ni está contenido dentro de ninguna y que, además, puede ser ocultada total o
parcialmente por otras ventanas. La propiedad FormStyle es la que determina el estilo del
formulario.

Si la propiedad toma el valor fsStayOnTop, la ventana siempre se visualizará sobre las


demás, no siendo posible ocultarla.

Los otros dos estilos permitidos son fsMDIForm y fsMDIChild, cuya finalidad es
permitir la creación de aplicaciones MDI (Multiple Document Interface / Interfaz de
Documento Múltiple). El término hace referencia a un tipo de aplicación compuesta por una
ventana principal, a la que denominamos padre, capaz de contener en su interior múltiples
ventanas a las que se denominan hijas. La ventana que actúa como padre tiene el estilo
fsMDIForm y cada ventana hija tendrá el estilo fsMDIChild.

La finalidad de la interfaz MDI (interfaz de documentos múltiples) es facilitar la edición


simultánea de varios documentos. En una interfaz MDI existe una ventana principal o marco,
que es la que cuenta con el menú de opciones, la barra de botones, la línea de estado, etc… y
una o más ventanas hija. Las ventanas hija MDI siempre se visualizan en el interior de la
ventana marco, no pudiendo extenderse más allá de ella.

Aquellos formularios que van a actuar como ventanas hija deberán ser definidos como
formularios disponibles y no como formularios autocreados.

La ventana principal cuenta con una serie de propiedades y métodos que facilitan la gestión
de las ventanas hija. La propiedad MDIChildCount contiene el número de ventanas hijas
existentes, mientras que ActiveMDIChild almacena una referencia a la ventana hija que
en ese momento tiene el foco de entrada.

Mediante los métodos Cascade() y Tile() podemos disponer las ventanas hijas
existentes en ese momento solapadas o en forma de mosaico, mientras que el método
ArrangeIcons() nos permite ordenar las ventanas que en ese momento estén
minimizadas.

EN LA PRÁCTICA
Con el fin de ver en la práctica cómo podemos utilizar múltiples formularios en un proyecto y
algunos de los cuadros de diálogo que nos aporta Windows, vamos a diseñar un programa
que nos permita editar ficheros de texto, con la posibilidad de tener abiertos varios de
ellos de forma simultánea en un entorno MDI.

El texto de cada ventana estará contenido en un control TMemo, por lo que no es posible el
uso de atributos de texto. Sin embargo, cada ventana puede tener un tipo de letra y color
diferentes al resto.

Juana Sánchez Rey 2002/2003 3


Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

Diseño del formulario principal

El formulario principal del proyecto actuará como ventana marco de la interfaz MDI. Este
formulario será el que existe inicialmente en el proyecto, al que vamos a darle el título
“Multi Editor”. Deberá tener el valor fsMDIForm en la propiedad FormStyle.

Se insertará un menú de opciones desde el cual será posible abrir un fichero, guardarlo, crear
nuevos ficheros, modificar el tipo de letra y el color de fondo y gestionar las múltiples
ventanas hija que puedan existir. El menú de opciones (componente TMainMenu) deberá
tener las siguientes opciones:

Juana Sánchez Rey 2002/2003 4


Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

Para permitir las diferentes opciones que se deducen de las opciones del menú, tendremos
que insertar un componente TSaveDialog, un TOpenDialog, un TFontDialog y un
TColorDialog. El formulario quedará en tiempo de diseño tal y como se muestra a
continuación:

Se asignará a la propiedad Title del componente TSaveDialog la cadena “Guardar


fichero de texto”, y a la misma propiedad del componente TOpenDialog la cadena
“Abrir fichero de texto”. A la propiedad Filter de ambos componentes abrá

Juana Sánchez Rey 2002/2003 5


Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

que asignar “Ficheros de Texto | *.txt”. También abrá que activar del
componente TSaveDialog las opciones ofOverwritePrompt y ofPathMustExist,
y en el componente TOpenDialog la propiedad ofFileMustExist.

Diseño del formulario hijo

El siguiente paso es añadir al proyecto un nuevo formulario, para lo que pulsaremos el botón
New Form. Este formulario actuará como hijo y en ejecución podrán existir múltiples copias
de él abiertas. Por ello esta ventana no deberá ser creada automáticamente, sino que habrá
que especificar que está sólo disponible.

Como este nuevo formulario va a ser una ventana hija habrá que darle el valor fsMDIChild
a su propiedad FormStyle.

Para visualizar y permitir la edición del texto insertaremos en este formulario un campo
TMemo que ocupará todo el espacio disponible en la ventana (Align=alClient), que
contará con una barra de desplazamiento vertical (ScrollBars=ssVertical) y que
permitirá la introducción del tabulador (WantTabs=true).

El código del formulario principal

Mediante la opción Include Unit Hdr del menú File añadiremos al primer formulario una
referencia al segundo, lo que nos permitirá acceder a la definición de TForm2.

Juana Sánchez Rey 2002/2003 6


Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

El título de cada ventana hija se establecerá en ejecución. En caso de que la ventana se cree
abriéndose un fichero existente el título será el nombre de éste. Si, por el contrario, la ventana
se ha creado vacía, asignaremos como título la palabra Nuevo seguida de un número que se
irá incrementando secuencialmente. Este contador se declarará en la parte private de la
definición del formulario, que se encuentra en el fichero de cabecera correspondiente:

Para crear un nuevo fichero, la opción Nuevo del menú Archivo tendrá que ser pulsada.
Por tanto habrá que seleccionar esta opción para abrir el editor de código por el método
correspondiente y escribir lo siguiente:

void __fastcall TForm1::Nuevo1Click(TObject *Sender)


{

Juana Sánchez Rey 2002/2003 7


Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

TForm2 * Formulario = new TForm2(this);


Contador++;
Formulario->Caption="Nuevo"+AnsiString(Contador);
}

La opción Guardar del menú Archivo no debe estar accesible en caso de que no exista
ninguna ventana hija abierta. Para ello deberemos hacer click en el manú Archivo para
abrir el método correspondiente al evento OnClick. En este caso asignaremos a la
propiedad Enabled de la opción Guardar los valores true o false, dependiendo de que
exista o no un formulario hijo activo en ese momento:

void __fastcall TForm1::Archivo1Click(TObject *Sender)


{
Guardar1->Enabled=ActiveMDIChild != NULL;
}

Si la opción Guardar está activa y la seleccionamos, tendremos que solicitar un nombre de


fichero, para lo que se uará el componente TSaveDialog. A continuación obtendremos una
referencia a la ventana hija que está activa en ese momento, mediante la propiedad
ActiveMDIChild, realizando la necesaria conversión al tipo TForm2. Esto nos permitirá
acceder al control TMemo y realizar una llamada al método SaveToFile(). A
continuación asignaremos a la propiedad Caption del formulario el nombre del archivo
seleccionado y daremos el valor false a la propiedad Modified de TMemo, indicando que
no se ha modificado desde la última grabación. El método completo sería:

void __fastcall TForm1::Guardar1Click(TObject *Sender)


{
if (SaveDialog1->Execute())
{
TForm2 * FormularioActivo = static_cast<TForm2 *>(ActiveMDIChild);
try
{
FormularioActivo->Memo1->Lines->SaveToFile(SaveDialog1->FileName);
FormularioActivo->Caption=SaveDialog1->FileName;
FormularioActivo->Memo1->Modified=false;
}
catch(...) {ShowMessage("Se produce error al guardar");}
}
}

La opción Recuperar es la complementaria a la anterior. Mediante ella podemos abrir una


ventana con el contenido de un archivo ya existente. Tras solicitar el nombre del fichero,
crearemos una nueva ventana hija a accederemos a su control TMemo para recuperar el
fichero. Asignaremos el título de la ventana, daremos el valor false a la propiedad
Modified y mostraremos la nueva ventana hija. El código correspondiente a su evento
OnClick será el siguiente:

void __fastcall TForm1::Recuperar1Click(TObject *Sender)


{
if (OpenDialog1->Execute())
{
TForm2 * Formulario = new TForm2(this);
try
{
Juana Sánchez Rey 2002/2003 8
Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

Formulario->Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
Formulario->Caption=OpenDialog1->FileName;
Formulario->Memo1->Modified=false;
Formulario->Show();
}
catch(...) {ShowMessage("Se produce error al recuperar");}
}
}

La opción Salir tendrá como finalidad cerrar la aplicación. Para ello bastará llamar al
método Close() de la forma:

void __fastcall TForm1::Salir1Click(TObject *Sender)


{
Close();
}

La primera opción del menú Estilo es Letra, una opción que nos permitirá seleccionar
cualquier tipo, tamaño y estilo de letra para mostrar el texto de la ventana que en ese
momento esté activa. Para ello obtendremos los valores actuales de la propiedad Font del
control TMemo, asignándolos a la propiedad Font del control TFontDialog. Mostraremos
el cuadro de diálogo y si se ha pulsado el botón Aceptar, realizaremos la asignación contraria.

void __fastcall TForm1::Letra1Click(TObject *Sender)


{
TForm2 * FormularioActivo = static_cast<TForm2 *>(ActiveMDIChild);
FontDialog1->Font=FormularioActivo->Memo1->Font;
if (FontDialog1->Execute())
FormularioActivo->Memo1->Font=FontDialog1->Font;
}

Del mismo modo, el código asociado a la opción Color será el siguiente:

void __fastcall TForm1::Color1Click(TObject *Sender)


{
TForm2 * FormularioActivo = static_cast<TForm2 *>(ActiveMDIChild);
ColorDialog1->Color=FormularioActivo->Memo1->Color;
if (ColorDialog1->Execute())
FormularioActivo->Memo1->Color=ColorDialog1->Color;
}

Por último encontramos en el menú una opción Ventana conteniendo tres opciones que nos
permiten disponer las ventanas hija en forma de cascada, en forma de mosaico o bien
organizar los iconos de aquellas ventanas que estén minimizadas. Los códigos asociados
serán los siguientes:

void __fastcall TForm1::Cascada1Click(TObject *Sender)


{
Cascade();
}

void __fastcall TForm1::Mosaico1Click(TObject *Sender)


{
Tile();
Juana Sánchez Rey 2002/2003 9
Borland C++ Builder. MÚLTIPLES VENTANAS. Fundamentos de Programación (Ciclo superior A.S.I.)

void __fastcall TForm1::OrganizarIconos1Click(TObject *Sender)


{
ArrangeIcons();
}

El código del formulario hijo

Mediante la opción Include Unit Hdr del menú File añadiremos al segundo formulario una
referencia al formulario principal mediante la opción Include Unit Hdr.

Como hemos podido ver en el punto anterior, los métodos que se encargan de guardar y
recuperar el texto de una ventana activa utilizan la propiedad Modified del control TMemo
para indicar que no se han realizado modificaciones. Esta propiedad tomará automáticamente
el valor true en el momento en que el usuario modifique el texto. Por lo tanto, en el
momento en que se desee cerrar una ventana habrá que comprobar esta propiedad y actuar en
consecuencia. Para poder hacer esto escribiremos el código siguiente correspondiente al
evento OnCloseQuery.

void __fastcall TForm2::FormCloseQuery(TObject *Sender, bool &CanClose)


{
if (Memo1->Modified)
switch(Application->MessageBox("¿Desea guardar los
cambios?",("Cerrar"+Caption).c_str(),MB_YESNOCANCEL))
{
case ID_YES: Form1->Guardar1Click(this); break;
case ID_CANCEL: CanClose=false;
}
}

Cuando se pulsa el botón de cierre de una ventana hija MDI lo que ocurre es que ésta se
minimiza, pero no se destruye, por lo que habrá que liberar la memoria usada por la ventana
según lo siguiente:

void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)


{
Action=caFree;
}

Ya sólo bastaría probar el programa. Esto es to, esto es to, esto es todo, amigos!!

Juana Sánchez Rey 2002/2003 10