Delphi Paso a Paso Por Vilma Algara

125
1 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS Delphi paso a paso (I) Por Vladimir Algara Cada vez menos, tratamos de discutir cómo o por qué tenemos que introducirnos en Windows, sólo queda por dilucidar cuándo y con qué herramienta. Para quienes trabajaban en Clipper el salto evidente parecía ser Visual Objects, para los eternos "xBaseros" la balanza se inclinaba hacia Visual FoxPro, para los de “Ceros” el Visual C++, etc. Actualmente, a mi entender, existen cinco grandes herramientas para trabajar bajo Windows, que enumero alfabéticamente para no levantar sospechas: Delphi , Visual Basic, Visual C++, Visual FoxPro y Visual Objects. Todas ellas podrían someterse a examen y, unas u otras saldrían bien o mal paradas según el baremo aplicado. No cabe la menor duda de que cada programador pretende conseguir unas metas concretas, pero lo que también es cierto es que para conseguir estas metas el camino debería ser lo menos escabroso posible. Según estas dos premisas se podría hacer una primera división de esas cinco herramientas; en dos grandes grupos, uno que simbolizara la potencia del producto (en el que se valoraría total potencia OOP -fundamental-, tipificación de variables, implementación DDE entre aplicaciones, manejo ágil de los datos almacenados en archivo de bases de datos, acceso sin trabas al API de Windows, manipulación de los 32 bits de Windows 95, así como otras de menos importantes, pero que vinieran a engordar la bonanza del producto, como accesibilidad a controles OLE y controles de Windows 95, etc.) y otro su facilidad de manejo. - Por potencia: Delphi , Visual C++ (ordenados alfabéticamente) y, un poquitín más alejado, Visual Objects. - Por facilidad de uso: Delphi , Visual Basic y Visual FoxPro (ordenados alfabéticamente por fabricante, Borland y Microsoft). Dado que se ordene como se ordene siempre es Delphi quien encabeza las listas, en esta primera entrega se comienza un deambular (por no decir curso) a través de las facilidades de uso y potencia que Delphi 2.0 (32 bits) ofrece a los desarrolladores que quieran programar bajo Windows y aún no sepan con qué hacerlo. Características de Windows Para empezar debemos dejar claros varios conceptos, no siempre evidentes para usuarios DOS. El Sistema Operativo El crecimiento del hardware ha marcado las pautas del software. Entornos de trabajo como Windows son impensables en máquinas de bajas prestaciones. DOS es un sistema operativo de pocos recursos y, por consiguiente, rápido. Sin embargo es excesivamente artesanal, pues todo control, toda validación, todo supuesto error ha de ser previsto por el programador, no por el sistema operativo (como por ejemplo Windows ). Además, Windows dota de grandes recursos con apenas codificación. La multitarea Windows es un sistema que permite realizar varias tareas a la vez; por ejemplo, una persona puede estar ejecutando una aplicación y mandando por módem un archivo a otra estación

Transcript of Delphi Paso a Paso Por Vilma Algara

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (I)Por Vladimir Algara

Cada vez menos, tratamos de discutir cómo o por qué tenemos que introducirnos en Windows,sólo queda por dilucidar cuándo y con qué herramienta. Para quienes trabajaban en Clipper elsalto evidente parecía ser Visual Objects, para los eternos "xBaseros" la balanza se inclinabahacia Visual FoxPro, para los de “Ceros” el Visual C++, etc.

Actualmente, a mi entender, existen cinco grandes herramientas para trabajar bajo Windows,que enumero alfabéticamente para no levantar sospechas: Delphi, Visual Basic, Visual C++,Visual FoxPro y Visual Objects. Todas ellas podrían someterse a examen y, unas u otrassaldrían bien o mal paradas según el baremo aplicado. No cabe la menor duda de que cadaprogramador pretende conseguir unas metas concretas, pero lo que también es cierto es quepara conseguir estas metas el camino debería ser lo menos escabroso posible. Según estasdos premisas se podría hacer una primera división de esas cinco herramientas; en dos grandesgrupos, uno que simbolizara la potencia del producto (en el que se valoraría total potencia OOP-fundamental-, tipificación de variables, implementación DDE entre aplicaciones, manejo ágil delos datos almacenados en archivo de bases de datos, acceso sin trabas al API de Windows,manipulación de los 32 bits de Windows 95, así como otras de menos importantes, pero quevinieran a engordar la bonanza del producto, como accesibilidad a controles OLE y controles deWindows 95, etc.) y otro su facilidad de manejo.

- Por potencia: Delphi, Visual C++ (ordenados alfabéticamente) y, un poquitínmás alejado, Visual Objects.

- Por facilidad de uso: Delphi, Visual Basic y Visual FoxPro (ordenados alfabéticamentepor fabricante, Borland y Microsoft).

Dado que se ordene como se ordene siempre es Delphi quien encabeza las listas, en estaprimera entrega se comienza un deambular (por no decir curso) a través de las facilidades deuso y potencia que Delphi 2.0 (32 bits) ofrece a los desarrolladores que quieran programar bajoWindows y aún no sepan con qué hacerlo.

Características de Windows

Para empezar debemos dejar claros varios conceptos, no siempre evidentes para usuariosDOS.

El Sistema Operativo

El crecimiento del hardware ha marcado las pautas del software. Entornos de trabajo comoWindows son impensables en máquinas de bajas prestaciones.

DOS es un sistema operativo de pocos recursos y, por consiguiente, rápido. Sin embargo esexcesivamente artesanal, pues todo control, toda validación, todo supuesto error ha de serprevisto por el programador, no por el sistema operativo (como por ejemplo Windows ).Además, Windows dota de grandes recursos con apenas codificación.

La multitarea

Windows es un sistema que permite realizar varias tareas a la vez; por ejemplo, una personapuede estar ejecutando una aplicación y mandando por módem un archivo a otra estación

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

remota. Esta potencia hay que tenerla en cuenta a la hora de diseñar las aplicaciones, pues esasegunda tarea puede ser, nuevamente, la propia aplicación. Incluso dentro de una aplicación, sepuede hacer que un proceso se lance un número indefinido de veces (aplicaciones MDI), sinmenoscabo de lo ya ejecutado.

La comunicación entre aplicaciones y/o procesos se puede efectuar a través de cuatro grandescaminos: el uso del portapapeles (cortar y pegar), la técnica de Drag&Drop (arrastrar y soltar),el intercambio dinámico de datos (enlaces DDE) y las incrustaciones OLE.

Los eventos

Por medio de los sucesos acaecidos en la vida de una aplicación, ésta le brinda al sistema unainformación; el sistema los lleva a cabo. Esta comunicación aplicación-sistema es fácilmenteasimilable por una mente DOS, pero lo que puede no serlo tanto es la información que genera elsistema y que nuestra aplicación tiene la potestad de utilizar o no. Como se aprecia es unacomunicación de dos direcciones y, además, constante.

La gestión de memoria

Windows reutiliza y compacta la memoria según sus necesidades. Cuando se cierra alguno delos programas que se mantienen abiertos Windows se encarga de liberar la memoria ésteutilizaba, manipulando los datos y código a su conveniencia y optimizando al máximo la ubicaciónde los segmentos de memoria. Asimismo, el uso de las librerías de enlace dinámico (DLL)permite:

- Cargar y descargar la librería en tiempo de ejecución (enlace dinámico)

- Que varios programas distintos accedan a una misma DLL sin que ocurran conflictos(independencia del ejecutable).

- Que una DLL esté confeccionada en un lenguaje y sea aprovechada desde otro(independencia del lenguaje).

Las herramientas visuales

Dado que Windows es un entorno gráfico, el diseño de ventanas (con una lógica similar en todoslos lenguajes), iconos, cursores, etc., se realiza por medio de las herramientas que los distintoslenguajes proporcionan, por lo que los programadores pueden dedicar menos tiempo al diseñofinal y más al buen funcionamiento del algoritmo interno.

Por qué Delphi

La elección de un lenguaje u otro, lo expuse al principio, dependen de muchos factoresdeterminantes.

Entre toda la oferta, y teniendo en cuenta que yo previamente trabajaba en Clipper y que sabíaalgo (no gran cosa) de Turbo Pascal, tenía dos claros candidatos, Visual Objects (con el queefectivamente empecé debido a mi herencia) y Delphi. Con Visual Objects he pasado alegrías ypenurias, y si bien es una de las arquitecturas más sólidas, no posee un entorno amable. Con éltengo la seguridad de que cualquier cosa la voy a poder llevar a cabo, pero no sé cuánto voy atener que invertir en tecnología ni en tiempo. Con Delphi y Visual C++ tengo esa misma potencia,pero con ambos dispongo de infinita más flexibilidad, tengo la posibilidad de no ser artesano

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

(aunque puedo serlo), de no depender de la feliz idea, de la posibilidad de ser más tonto, ¡quégusto!

Como no sé C, ni BASIC y quería olvidarme del @...SAY, mi suerte estaba echada. Voy aintentar comentar las facilidades de uso de Delphi 2.0 e intentar abordar, a lo largo de lasdistintas entregas, las operaciones más comunes a la hora de desarrollar una aplicación paraWindows.

El entorno

Los requerimientos

De todos los paquetes, también Delphi es el que menos requerimientos hardware necesita.Aunque puede realizar una instalación compacta de unos 20Mb cuando se quiere cargar todo(aunque no se utilice en la vida) se necesitan alrededor de 60 Mb.

Al producto, en tiempo de desarrollo, le hacen falta un mínimo de 4 Mb de memoria (lo que esjusto y necesario para Windows 95) aunque con más va mejor, claro; 8 Mb es una cifra con laque nos podemos empezar a sentir a gusto. Visual Objects, Visual FoxPro, etc. necesitan 8 Mbpara empezar a hablar y 16 para empezar a sentirse a gusto, Visual C++ fija el mínimo en 16Mb y 20Mb para un buen hacer.

La mesa de trabajo

Se entiende por mesa de trabajo el lugar donde se encuentran todas las piezas disponibles parala confección de una aplicación. En Delphi se advierten cinco grupos de elementos claramentediferenciados.

- El menú principal. En él se recogen todas las opciones que se pueden realizar desdeDelphi: empezar un nuevo trabajo, imprimir el que está en curso, compilar, ejecutar, accedera la ayuda dependiendo del contexto en el que nos encontremos, etc.

- La barra de herramientas. Conjunto de iconos que simbolizan las operaciones máscomunes. La que se ve en la figura 1 no es la que ofrece Delphi por defecto, sino unaconfigurada al gusto del consumidor (o sea, al mío).

- La paleta de controles. Colección de los elementos que se pueden ubicar en las ventanasde una aplicación. Se trata de una colección de carpetas (tabs), cada una de ellas, a suvez, con una colección de controles al uso. Este elemento y los dos anteriores se puedenver en la figura 1.

Figura 1: Menú principal, barra de herramientas y paleta de controles.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

- El inspector de objetos. Ventana donde se permite modificar en fondo y forma el controlubicado en la ventana, o la propia ventana. Dado que un control (o la ventana) está sujeto asus características y a los eventos que se pueden producir en él, el inspector de objetosposee dos carpetas (tabs) para cada una de las operativas. Ver figura 2.

Figura 2: El inspector de objetos

- Ventana de trabajo (la que se verá en la aplicación una vez que la echemos a andar) ycódigo asociado a esa ventana. Ver figura 3

Figura 3: Ventana Form1

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Los archivos y la OOP

La unidad de trabajo en cualquier aplicación hecha desde Delphi es el proyecto (Project). Dentrode un proyecto se almacenan los formularios (cada ventana de la aplicación es un formulario) ounidades (units) que utilizará nuestra aplicación. Dada la arquitectura de Delphi todo se entiendecomo OOP, incluso los mencionados formularios son una instanciación de la clase TForm.

Pero, antes de asustar al personal con tanto OOP para arriba y para abajo, veamos en qué setraduce eso a la hora de implementar un programa. La respuesta es sencilla, una ventana semanipulará como un objeto, y, como tal, llevará un nombre puesto por mí. Por ejemplo, si laventana queremos que se llame Lucero, acudiremos a la propiedad Name en el inspector deobjetos y cambiaremos Form1 por Lucero. Este cambio provoca la siguiente modificaciónautomática:

Lucero: TLucero

Añadiéndose una línea donde se dice:

TLucero = class(TForm). . .var Lucero: Tlucero;

Eso quiere decir que la ventana creada, se llame como se llame, es una instanciación de la clasepadre TForm, que, como tal, tiene todas sus características. Seguidamente se define la variableLucero de esa clase recién creada

Una vez en este punto, sabemos que el objeto Lucero (la ventana) se compondrá de un ancho,de un alto, de una posición de comienzo, de un título (caption), de un botón de cierre (aspa enWindows 95) o no, etc.

Todos estos datos de los objetos vienen meridianamente explicados en la ayuda que acompañaa Delphi; así, por ejemplo, si se desea saber más sobre el dato caption de la ventana Lucero,buscaremos caption en la ayuda en línea (Contents en Help). Aparecerá la descripción de lafigura 4.

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 4: Ayuda sobre el dato caption

La información aportada en esta ventana del Help, además de la descripción, es el componenteal que pertenece (TForm), un ejemplo de uso (zona sensible example), temas relacionados(zona sensible see also) y tipo de dato que contiene (property caption: string; ) en nuestro casouna cadena de caracteres.

El caption, como ya se sabe, corresponde al título de la ventana. Existen dos formas de alterarsu contenido, una por medio de la propiedad caption del inspector de objetos y otra medianteprograma:

Lucero.caption := ‘Título de ventana’

Intuitivamente se puede interpretar este código de la siguiente manera. El objeto Lucero tiene undato llamado caption (al cual accedo mediante la sintaxis Lucero.caption) que modifico sin másque asignar un dato acorde a su tipo (según rezaba la ayuda, este tipo es carácter). Usando eloperador de asignación (:=) almaceno la cadena entrecomillada.

El código fuente

Una interesante posibilidad, sobre todo al comienzo, consiste en ver qué código se va generandopara la ventana en curso, además del siempre disponible del proyecto. Para ello pinchamos endicha ventana con el botón derecho del ratón (izquierdo para los zurdos) y elegimos, en el menúlocal, verlo en forma de código fuente. Allí habrá una línea similar a la de más arriba. Es más,cualquier cambio en esta línea también afectará al contenido que visualiza el inspector deobjetos. Para regresar a la ventana se vuelve a pinchar con el botón izquierdo y elegimos en elmenú local verlo en forma gráfica.

Para aquellos que no conozcan nada de Pascal y vengan de un entorno xBase, decir que, comotodo lenguaje, uno se empieza a encontrar a gusto cuando domina su sintaxis. La de Pascal esestructuralmente diferente, por ejemplo, a la de Clipper. A saber.

Estructura de los programas

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

En Clipper se comienza con una función principal en un archivo PRG principal que va llamandosucesivamente a otras funciones y/o procedimientos. En Delphi el módulo principal es el delproyecto y tiene una estructura como la del fuente 1:

// --- Fuente 1 -----------------------------------------------------------program Project1;

uses Forms, Unit1 in 'UNIT1.PAS' {Lucero}, Proy in 'PROY.PAS';

{$R *.RES}

begin Application.CreateForm(TLucero, Lucero); Application.Run;end.

En primer lugar la palabra reservada program, que indica el comienzo de un programa Delphi,luego los módulos que intervienen en el proceso (uses), indicadores útiles para el compilador({$R *.RES}) y comienzo de los elementos ejecutables (entre las palabras reservadas begin yend) y punto final (.). Lo que se ejecuta, en este caso, es la creación de un formulario, tomandocomo base la clase Tlucero y su variable Lucero. Esto mismo, en Clipper sería algo idéntico a:

Lucero := Application.CreateForm(TLucero);

Por otra parte, las distintas unidades presentan la estructura del fuente 2.

// --- Fuente 2 -----------------------------------------------------------unit Unit1;

interface

uses SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type TLucero = class(TForm)private { Private declarations } public { Public declarations } end;

var Lucero: TLucero;

implementation

{$R *.DFM}

end.

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Encontramos una primera sección para el interfaz a usar (Interface), seguida de los módulosnecesarios para echar a andar la aplicación (uses), otra sección para la definición de nuevostipos de datos (type) donde se declara la nueva clase TLucero (que hereda de TForm), ladeclaración de variables privadas y públicas a nivel de aplicación (luego habrá locales a nivel defunción y/o método), las variables de aplicación (sección var) donde se especifica Lucero comotipo TLucero. Tanto el fuente 1 como el 2 se generan de forma automática.

Separación de líneas

Las líneas se separan por punto y coma (;) como en C. Si hubiera una instrucción con más deuna línea bastaría con escribir una detrás de otra y, al final, poner el punto y coma.

nValor1 := 1;nValor2 := 1;nValor3 := 1;nValor4 := 1;nSuma := nValor1 + nValor2 nValor3 + nValor4;

Declaración de variables

Se usa la palabra reservada var para definir una o un conjunto de variables. Para ello se da elnombre de la variable, el identificador dos puntos (:) y se define un tipo válido.

varnValor1 : integer;nValor2, nValor3, nValor4 : integer;cCadena : string[10];oVentana : Tlucero;

Estructuras begin...end

Cuando en una estructura se especifica una sola acción no es necesario el uso de agrupacionesbegin...end, pero si se van a realizar dos o más sí lo será:

if nValor1=1 then nValor2:=0else nValor2:=1000

if nValor1=1 thenbegin

nValor2:=0;nValor3:=0;nValor4:=0;

endelsebegin

nValor2:=1000;nValor3:=1000;nValor4:=1000;

end;

Valores de funciones

9Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Existen funciones que devuelven valores que pueden ser almacenados en variables auxiliares(como en Clipper), pero hay otras que reciben como parámetro dicha variable auxiliar y esdentro de la función donde se altera su contenido (se pasan internamente como referencia).

dFecha := Date() // Correcto

cValor1 := Str( nValor1 ) // No correctoStr( nValor1, cValor1 ) // Correcto

Comentarios

Los comentarios a los programas comienzan abriendo un paréntesis y un asterisco y finalizancon un asterisco y un paréntesis cerrado. En la versión 2 también se pueden utilizar las dosbarras (como en C o en Clipper).

(* Comentario a programa Delphi *)// Comentario a programa Delphi

Ubicación de funciones

Todas las funciones, procedimientos y métodos han de estar al principio del programa, antes delbegin...end principal. El begin...end principal debe acabar en punto.

procedure ...(...)var

...begin

...end;

procedure ...(...)var

...begin

...end;

begin...

end.

Antes de terminar

De aquí en adelante todo va seguir la misma sintaxis expuesta. Por ejemplo, para codificar cadaevento basta con hacer doble clic en alguno de los de la relación mostrada en el inspector deobjetos. Automáticamente se escribe el correspondiente código, el cual, como se puedeobservar, se ciñe a la estructura de más arriba; eso sí, orientado a objetos.

Como empezar a modificar todos los datos de la ventana o los eventos que en ella se puedenmanifestar haría que me extendiese en demasía, postergo toda esta básica maquinaria para unasegunda entrega, en la que empezaremos a sacarle más jugo al lenguaje. En este momento, sise quiere ver cómo funciona la ventana Lucero con su caption modificado, bastará con pincharsobre el icono de ejecución (triángulo verde) y, efectivamente, verla.

10Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (II): MenúsPor Vladimir Algara

Exponer de nuevo el tema de los menús en Algoritmo (o en clippeRmanía ) es algo que no va adeslumbrar a nadie, pero hacerlo desde Delphi sí puede interesar a más de uno.

Como ya se comentó en la entrega anterior, Delphi posee un editor donde implementarcualesquiera de los elementos habituales de una ventana; en este número vamos a ver cómoutilizar el específico de menús.

Cualquier editor de menús de cualquier lenguaje suele ser fácil de manejar, y con ellosimplementamos un elemento omnipresente en las ventanas principales de las aplicaciones.

Con este artículo describiremos el uso genérico de este editor, así como aquellascaracterísticas más peculiares.

Dado que un menú va asociado a una ventana (principal o secundaria), la lógica de creaciónconsiste en la implementación de uno de estos menús y, una vez terminado, su asociaciónposterior a la ventana. Una vez que el menú se crea está disponible para cualquier ventana.

Editor de Menús

Una vez dadas estas nociones generales pasaremos a describir el funcionamiento y loselementos del editor de menús (ver figura 1).

Figura 1.

1.- Línea donde se escribirá cada opción principal del menú.

En esta línea se especifican las distintas opciones principales que componen el cuerpocentral del menú.

2.- Líneas donde se escribirán cada una de las opciones secundarias.

En esta línea se especifican las distintas subopciones de las opciones principales.

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

3.- Líneas donde se escribirán cada una de las subopcines.

En esta línea se especifican las distintas subopciones anidadas.

En cualquiera de los tres casos anteriores (o cualquier otro nivel de anidación desubopciones), si quisiéramos activar un Hot-Key para cada opción, bastaría con anteponera la letra en cuestión el símbolo &:

&Salir crearía como Hot-Key la letra S

Para crear subopciones a una opción, lo único que tendremos que hacer es pulsar el botónizquierdo del ratón sobre la opción secundaria seleccionada, eligiendo Create Submenu (opulsando [Ctrl-flecha derecha] como se informa en la figura 2) en el menú local asociado.

4.- Separadores entre subopciones. Para añadir este elemento visual basta con escribir unguión y pasar a la siguiente línea. Aparecerá una línea separadora.

5.- Abreviatura de teclado para acceso rápido a opciones.

Figura 2

Como podemos observar en la figura 1, el menú creado posee todos estos elementos. Esbastante intuitivo y en él podemos observar distintos subniveles, así como los hot-keysasociados a cada opción del mismo.

El inspector de objetos

El cometido de esta ventana, como ya se adelantó algo en la entrega anterior, es el de laespecificación de cada uno de los elementos que, a lo largo del ciclo de desarrollo, vamos dandoa las partes que completan la aplicación.

En los menús, podremos declarar los siguientes conceptos:

Propiedades del menú

Las características del menú como un todo, como algo que engloba a los distintos ítems. Lafigura 3 se muestra cuando pinchamos sobre un elemento de tipo menú.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 3.

AutoMerge: Premite que el menú se muestre nada más arrancar la ventana, o no.

Items: Permite entrar en el editor de menú (pinchando en los puntos suspensivos asociados), y,por tanto, modificarlo y/o consultarlo. Tiene el mismo efecto que hacer doble clic sobre unelemento de tipo menú.

Name: Nombre simbólico a través del que nos referiremos al menú (nombre de la clase que loalberga).

Tag: Valor numérico por el cual conocemos el orden de tabulación de un elemento. Aplicable atodos los componentes visuales de una ventana.

Propiedades de los ítems

La relación de cada característica de los ítems de un menús (ver figura 4).

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 4

Break: Fija uno o varios puntos de ruptura. Esta característica permite obtener menús en formade caja, en la que cada ruptura (agrupación) engloba elementos de un mismo tipo para unadeterminada opción. Por ejemplo, si queremos obtener la figura 5, bastaría con decir que elBreak de los ítems "Base de datos" estuviera a mbBreak, en vez de a mbNone (opción pordefecto). Si como en la figura 5 se desea, además, separar cada una de estas rupturas, lo quese tendrá que especificar será mbBarBreak. El resto las dejaremos en mbNone.

Figura 5: Menú con agrupaciones de opciones

Caption: Literal descriptivo del ítem. En este punto es donde se puede añadir el carácter & parasubrayar una letra del caption.

Checked: Añade una marca al ítem del menú. Este tipo de símbolos se utiliza cuando deseamosque una cierta operación este siempre sujeta, o no, a un comportamiento estándar. Por ejemplo,habilitar que las ventanas siempre aparezcan expuestas en mosaico, o que los iconos aparezcan

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

organizados, etc. La marca también aparece en el propio editor de menús.

Enabled: Muestra atenuado (valor false) o no el ítem afectado. Se muestra así en el editor demenús, no sólo en la prueba ejecutable.

GroupIndex: Establece el número de iconos (asociados a los ítems) que pertenecerán a unasola agrupación.

HelpContext: Especifica, a través de un número, el tipo de ayuda que se visualizará cuando elusuario pulse la tecla [F1] en algún lugar de la aplicación. HelpContext es un aspecto aplicado nosólo a menús, sino a otros contextos; debido a su especificidad y a lo prodigado de su uso, se lededicará, en otra entrega, un estudio pormenorizado.

Name: Como ocurría con el menú, nombre simbólico del ítem.

ShotCut: Combinación de teclas para la llamada rápida al proceso asociado a un ítem del menú.Están contempladas las teclas [Ctrl-tecla], las teclas de función hasta la [F12], en combinación ono con Ctrl y/o Shift y demás teclas auxiliares como [Supr], [Inicio], etc., también con Ctrl y/oShift. El literal de la abreviatura de teclado aparece a continuación del ítem al que se le haasociado.

Tag: Valor numérico para el orden de tabulación.

Eventos

Los menús, y más concretamente los ítems que componen un menú, sólo tienen un evento;aquél que sucede cuando se elige alguna de las opciones. Por eso la pestaña Events sólodispone del evento OnClick().

Si queremos especificar un evento concreto, basta con escribir en el combo asociado aOnClick() el nombre del procedimiento que queremos ejecutar cuando hagamos Clic, y pulsar[Intro]. Esto hará que el nombre de dicho evento se registre en el bloque type, y que se escriba,automáticamente, las líneas esenciales de ese método. Por ejemplo si escribimos Salir veremosel código del fuente 1:

// --- Fuente 1 --------------------------------------------------------type. . .. . . procedure Salir(Sender: TObject);. . .. . .procedure TForm1.Salir(Sender: TObject);begin

end;

Ahora sólo hay que escribir entre el begin y el end lo que queremos ejecutar. En este caso,como lo que queremos es salir, diremos Close.

// --- Fuente 2 --------------------------------------------------------procedure TForm1.Salir(Sender: TObject);

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

beginClose;

end;

Otra posibilidad es dejar a Delphi que ponga nombre al método asociado a OnClik(). Como seráhabitual, esto lo haremos haciendo doble clic con el ratón en vez de escribir nosotros el nombre.

Si ya existen método definidos, podremos elegir, si ha lugar, algunos de los ya escritos.

Si más de un ítem ejecuta el mismo método, dentro de éste podremos distinguir si es uno u otro,por medio del parámetro Sender. Supongamos que el ítem Cortar y el ítem Copiar ejecutan unmétodo común que realiza una determinada cantidad de operaciones, al final de las cuales,efectivamente, queremos que lo que esté marcado en ese momento sea Cortado o Copiado.Para resolverlo nada más fácil que definir (en Cortar, por ejemplo) el método CortaryCopiar() ypulsar [Intro]; se generará el código asociado y comenzaremos a introducir el código común aambos, luego, por medio de Sender, determinaremos cuál se pulsó y actuaremos enconsecuencia (ver fuente 3).

// --- Fuente 3 --------------------------------------------------------procedure TForm1.CortaryCopiar(Sender: TObject);begin

// Código comúnForm1.caption := 'Se está usando el Cut&Paste';. . .. . .// Fin del código común

if Sender==Cortar then Edit1.cutelse Edit1.copy;

end;

Donde Edit1 es el Name de un hipotético control de edición y Cortar el Name asociado al ítemCortar del menú. Se supone que el método CortaryCopiar sólo es llamado por Cortar o Copiar,de ahí que si el que llama (Sender) no es Cortar no quede más remedio que sea Copiar. Sihubiera más posibilidades habría bastado con proteger cada operación particular con su ifcorrespondiente, o con una estructura case que tuviera en cuenta la casuística.

Ejemplo

Pongamos un ejemplo en el que vamos a jugar con dos controles de edición, en los que vamos apermitir cortar y/o copiar, así como pegar. También se permitirá, cuando tenga algo, vaciar elcontenido del portapapeles. Para que el efecto visual sea aparente, inhabilitaremos las opcionesdel menú no permitidas.

La ventana que recoge este proceso es el la de la figura 6 y el código que hay que ensayar eldel fuente 4.

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 6: Aplicación para uso de menús

// --- Fuente 4 --------------------------------------------------------unit menu2;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus, Buttons, ExtCtrls, OleCtrls, ChartFX, StdCtrls;

type TForm1 = class(TForm) MainMenu1: TMainMenu; Editar: TMenuItem; Cortar: TMenuItem; Copiar: TMenuItem; Pegar: TMenuItem; Vaciar: TMenuItem; N2: TMenuItem; SBCortar: TSpeedButton; SBCopiar: TSpeedButton; SBSalir: TSpeedButton; Edit1: TEdit; Edit2: TEdit; SBPegar: TSpeedButton; SBVaciar: TSpeedButton; procedure Salir(Sender: TObject); procedure CortaryCopiar(Sender: TObject); procedure PegarDesde(Sender: TObject); procedure VaciaPapelera(Sender: TObject); private { Private declarations } public { Public declarations } end;

var Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Salir(Sender: TObject);begin Close;end;

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

procedure TForm1.CortaryCopiar(Sender: TObject);begin Form1.caption := 'Se está usando Cut&Paste'; Pegar.Enabled := True; Vaciar.Enabled := True; SBPegar.Enabled := True; SBVaciar.Enabled := True; if Sender=Cortar then Edit1.CutToClipboard else Edit1.CopyToClipboardend;

procedure TForm1.PegarDesde(Sender: TObject);begin Edit2.PasteFromClipboardend;

procedure TForm1.VaciaPapelera(Sender: TObject);begin Pegar.Enabled := False; Vaciar.Enabled := False; SBPegar.Enabled := False; SBVaciar.Enabled := False;end;

end.

Porciones de código a resaltar

En la porción identificada como type se declara la variable TForm1, que se instancia desde laclase TForm

TForm1 = class(TForm)

Una relación de todos los ítems del menú (clase TMenuItem), de los botones de acceso rápido(TSpeedButton), de los controles de edición (clase TEdit) y de los métodos llamados alproducirse un cierto evento (ver fuente 5).

// --- Fuente 5 --------------------------------------------------------type TForm1 = class(TForm) MainMenu1: TMainMenu; Editar: TMenuItem; Cortar: TMenuItem;

. . . SBCortar: TSpeedButton; SBCopiar: TSpeedButton; SBSalir: TSpeedButton; Edit1: TEdit; Edit2: TEdit; SBPegar: TSpeedButton; SBVaciar: TSpeedButton;

procedure Salir(Sender: TObject); procedure CortaryCopiar(Sender: TObject); procedure PegarDesde(Sender: TObject); procedure VaciaPapelera(Sender: TObject);

9Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

En lo que a declaración de variables se refiere, se define Form1 como representación de laventana.

var Form1: TForm1;

Los procedimientos

Salir

Se invoca al pulsar sobre el botón de acceso rápido Salir. Su cometido: cerrar la ventana y, porende, la aplicación.

procedure TForm1.Salir(Sender: TObject);begin

// Cierra la ventana Close;end;

CortaryCopiar

Como se ha comentado un poco más arriba, este procedimiento es común a las opciones decortar y copiar (en lo que se refiere a la habilitación y deshabilitación de controles) y particular ala hora de almacenar en el portapapeles y respetar el texto seleccionado o eliminarlo. Verporción de código en el fuente 6.

// --- Fuente 6 --------------------------------------------------------procedure TForm1.CortaryCopiar(Sender: TObject);begin

// Parte común a Cortar y a Copiar Form1.caption := 'Se está usando Cut&Paste';

Pegar.Enabled := True; Vaciar.Enabled := True; SBPegar.Enabled := True; SBVaciar.Enabled := True;

// Parte diferenciada // Si el que ha llamado al procedimiento ha sido Cortar se hace Cut // Si no se hace Copy

if Sender=Cortar then Edit1.CutToClipboard else Edit1.CopyToClipboardend;

PegarDesde

Este otro se encarga de traer del portapapeles lo que haya allí grabado, almacenándolo en elcontrol de edición cuyo Name es Edit2.

procedure TForm1.PegarDesde(Sender: TObject);

10Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

begin Edit2.PasteFromClipboardend;

VaciarPapelera

Por último el método que vacía el portapapeles. Realmente el portapapeles no se vacía, lo únicoque se hace es inhabilitar la opción de Pegar, para impedir traerse, al menos desde las opcionesde la ventana, el contenido del portapapeles. También se inhabilita la opción de Vaciar, pues esilógico vaciar más de una vez el portapapeles ya vacío (ver fuente 7).

// --- Fuente 7 --------------------------------------------------------procedure TForm1.VaciaPapelera(Sender: TObject);begin Pegar.Enabled := False; Vaciar.Enabled := False; SBPegar.Enabled := False; SBVaciar.Enabled := False;end;

Conclusión

Aunque el programa es susceptible de muchas mejoras, como vaciar efectivamente elportapapeles, detectar que existe algo marcado en el control de edición Edit1 y sólo entoncespermitir el cortado o el copiado, etc.; se da una idea de cómo interactuar desde los ítems delmenú con ellos mismos, con otros controles de la ventana (botones de acceso rápido y deedición) y con elementos de Windows (como es el portapapeles).

Además, se ha intentado ir introduciendo algunos elementos de programación que enseñencómo especificar una orden desde el código fuente (inhabilitar botones, cerrar la ventana, etc.).

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (III): Controles (I)Por Vladimir Algara

Debido a un desliz lamentable en la entrega anterior, además, ni siquiera puedo culpar a otropues lo cometí yo, dejé de comentar la forma que tiene Delphi de implementar los menús localesa una ventana/control. Esos menús que aparecen cuando pinchamos con el botón derecho delratón (izquierdo en los zurdos) y que tan de moda ha puesto Windows 95.

En esta entrega, y antes de empezar a mover controles de arriba a abajo, dedicaré unas líneasa la forma de crear menús locales y que, de haberme percatado en su momento, debieran haberaparecido en el monográfico de menús del mes pasado. Ustedes sabrán disculparme.

Propiedades de los menús locales

En la figura 1 vemos un ejemplo del menú en el editor de menús y el aspecto que adquiere laventana de propiedades (o ventana del inspector de objetos) de dicho menú. Como recordará ellector, los menús se elegían en la paleta de herramientas estándar y se ubicaban en cualquierparte de la ventana; si hacíamos doble clic en este elemento entrábamos en el editor de menús,donde el inspector de objetos recogía las características y eventos posibles que un determinadoítem (o todo el menú) podía entender y ejecutar. Una vez terminada la fase de diseño, y graciasal nombre simbólico (Name) dado al menú, asociábamos éste a la ventana. El proceso para losmenús locales no es similar al de los menús generales, es idéntico; así que no haré perder mástiempo en explicar cómo operar, y me limitaré a poner un sencillo ejemplo.

Figura 1: Ejemplo de menú local

Ejemplo de menú local para salir de una aplicación

1.- Selecciónese el icono conveniente en la paleta de herramientas (ver figura 2)

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 2: Icono de la paleta de herramientas

2.- Ubíquese a discreción en la ventana elegida.

3.- Edítese y comiéncese a añadir ítems y subítems entre 1 y 200 (o más). Añadir uno cuyoevento OnClick (en Events) se llame Salir.

4.- Púlsese [Intro] para entrar en el editor de código fuente y poder especificar Close según seaprecia en el fuente 1.

// --- Fuente 1 ----------------------------------------------------------procedure TForm1.Salir(Sender: TObject);begin Close;end;

5.- Hágase clic en la ventana para poder manipular su inspector de objetos.

6.- Selecciónese la entrada PopUpMenu y asóciesele el PopUp creado.

7.- Ejecútese a fuego rápido la "aplicación".

8.- Sírvase pulsar el botón derecho del ratón sobre la ventana y elegir el ítem encargado deacabar.

De nuevo, disculpas por el parche mal ubicado y dedicado a menús locales. Aunque, bienpensado, podría haber recurrido a la coartada de explicarlo como un control más, pues eso,precisamente, es lo que es.

Controles varios

Los controles por excelencia son los PushButton. Estos botones son los encargados de lanzarlos procesos, cerrar las ventanas secundarias, abrir otras, dar conformidad a los datos, etc.;motivo principal por el que están presentes en casi cualquier aplicación, por sencilla que éstasea.

Los iconos que en la paleta de herramientas (no seleccionado y seleccionado) representan a losbotones "normales" (los llamo así en contraposición a los botones con un gráfico en su interior)son los de la figura 3.

Figura 3: Iconos para botones

Una vez seleccionados se llevan a la ventana y allí pasamos a darles especificidades.

Nota: Hay que recordar que cada vez que se pincha alguno de los elementos en la paleta deherramientas y se lleva a la ventana, éste se deselecciona automáticamente, debiendo pincharsobre el control deseado tantas veces como controles se quieran poner. Esto se soluciona si, en

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

vez de pinchar directamente, pinchamos manteniendo pulsado [Shift]. El icono, al contrario de loque ocurría con anterioridad, permanecerá hundido hasta que no lo pinchemos nuevamente, lohagamos en otro distinto, o sobre el icono que representa al cursor del ratón (presente encualquier pestaña de la paleta de herramientas).

El nombre (Name) dado a un control de tipo botón, así como su literal inicial es Button1, Button2,etc., si bien éstos se pueden rebautizar como nos venga en gana mediante sus propiedadesName y Caption, respectivamente, en la ventana del inspector de objetos.

Un control puede ser Marcado, Copiado/Cortado y Pegado hacia y desde el portapapeles. Todoellos con la secuencia de teclas habituales en la que [Ctrl-C] copia, [Ctrl-X] corta y [Ctrl-V] pega.Además, la barra de herramientas se puede configurar (opción Configure de su menú local) yagregarle aquellos iconos que se necesiten, entre los cuales se encuentran los que manipulan lainformación del portapapeles mencionada. En la figura 4 se puede ver una barra de herramientasconfigurada, que ofrece un aspecto distinto al ofrecido cuando arrancamos Delphi por primeravez.

Figura 4: Configuración de la barra de herramientas

Movilidad de controles

En este subapartado quiero contar, someramente, qué tipo de operaciones se pueden realizarcon los controles que se añaden a una ventana; entendidas éstas como la posibilidad de mover,redimensionar, borrar, alinear, etc. los controles disponibles. Para ello podemos partir de unaventana que albergará tres botones, del mismo tamaño que uno de ellos, alineados respecto delprimero y colocados en la esquina inferior derecha de una ventana según una rejilla (grid) dereferencia cuyos puntos equidistan 6 píxels entre sí.

Gráficamente se puede comprender mejor. Se quiere partir de una ventana como la de la figura5 y se quiere llegar a la de la figura 6.

Figura 5: Controles descolocados

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 6: Controles colocados según Grid

Para esta maniobra habremos de:

1.- Elegir un Grid para la ventana. Accedemos a la opción Options del menú Tools. En laventana de diálogo que aparece marcaremos los CheckBox correspondientes a DisplayGrid y Snap To Grid (el primero visualiza la rejilla, el segundo fuerza que los elementos seadapten a su contorno -dése cuenta que podemos tener un Grid que sirva de referencia,pero, al tiempo, permitir que los controles no se constriñan a él-) y se fijará la separaciónentre los puntos horizontales y verticales (6 para ambos).

2.- Elegir el botón de referencia. Por ejemplo nos puede servir el Botón 1, lo pinchamos y lohacemos tan grande como queramos.

3.- Amoldar el tamaño del resto de los botones al de referencia. Para ello se seleccionan todoslos botones, bien marcando un área en cuyo interior se encuentren todos los controlesdeseados, bien eligiéndolos uno a uno mientras se mantiene presionada la tecla [Shift]. Sepincha con el botón derecho del ratón sobre la ventana y se elige la opción Size...;aparecerá una ventana en la que se permite:

1.- No realizar cambios2.- Reducir todos los tamaños adaptándolos al del más pequeño3.- Ampliar todos los tamaños adaptándolos al del más grande4.- Fijar una dimensión

Cualquiera de esas operaciones se puede aplicar al redimensionamiento a lo ancho o a loalto del control. Si nos fijamos, no se puede especificar que todos los controles se adecúenal mismo tamaño del "mediano", sino al del más grande o al del más pequeño; pero esto essuficiente para contemplar todo los casos, pues lo habitual es elegir un conjunto decontroles (con su tamaño por defecto), manipular uno haciéndolo más grande o máspequeño, y adaptar el resto.

4.- Alinear los botones. Esta operación se puede llevar a cabo por medio del la opción Aling...del menú local a la ventana, o haciendo aparecer la ventana de diálogo de la figura 7. Através del menú local aparecerá una ventana de diálogo con la misma lógica que la queaparecía cuando intentábamos ajustar el tamaño (Size...), en la que se permite:

1.- No realizar cambios2.- Alinear a la izquierda y/o arriba3.- Centrar4.- Alinear a la derecha y/o abajo5.- Espaciar idénticamente los controles6.- Centrar en la ventana

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Todo ello según dos criterios, uno hacerlo horizontal y/o verticalmente, y el otro hacerlo respectoa un control de referencia. La forma que tiene Delphi de determinar cuál es el control dereferencia consiste en la manera en que son elegidos dichos controles. Por ejemplo, cuandoelijamos los tres controles de nuestro ejemplo, aquél que se pinche en primer lugar será el quesirva de referencia.

A través de las distintas opciones de la ventana, que se aprecia en la figura 7 (a la que seaccede tras elegir la opción Alignment Palette del menú View), se pueden hacer las mismascosas que con la ventana de diálogo recién comentada.

Figura 7: Controles colocados según Grid

Un control, cómo no, se puede borrar sin más que seleccionarlo y pulsar la tecla [Supr].Además, se puede ensayar cualquiera de las opciones que aparecen en el menú local, algunasde las cuales hemos repasado hace un momento.

- Aling To Grig: Forzar a que los controles se ciñan a la rejilla visualizada.

- Bring To Front: Traer delante el control elegido. Cuando dos o más controles ocupan un áreacomún (por ejemplo un RadioButton dentro de un GroupBox) se permite fijar cuál seencontrará por encima de cuál.

- Send To Back: Llevar detrás (la explicación es análoga a la de arriba).

- Aling...: Ya comentada

- Size...: Ya comentada

- Scale...: Fijar un tamaño proporcional según el tamaño actual del control. Un valor de 100equivale al mismo tamaño, un valor inferior a 100 (hasta un mínimo del 25%) provoca ladisminución proporcional del tamaño actual del control y un valor superior (hasta un 400%) unaumento proporcionado. Este escalado es aplicable, no solo a un control específico, sino a unconjunto de ellos previamente seleccionados.

- Tab Order... y Creation Order...: Se permite fijar la prioridad de los elementos visuales y novisuales de una ventana, con el fin de que en ejecución unos tengan preferencia sobre otros.

- Add To Repository: Añadir la ventana recién configurada a la relación de las ventanas pordefecto.

- View as Text: Una ventana lleva asociado un código fuente encargado de ponerla en marcha.Dicho código se permite hojearlo mediante esta opción. Dada la correspondencia entre lo quese ve en la ventana y su código fuente, como el contenido del código fuente y lo que éstegenera, cualquier modificación en uno conllevará la actualización del otro, y viceversa. Cuandose está inspeccionando el código fuente, habrá que volver a pulsar el botón derecho del ratóny elegir la opción View as Form (o pulsar [Ctrl-F12]) para volver a disponer de la ventanaoriginal.

Propiedades de los botones

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Como elemento añadido, decir que no hay por qué conformarse con el tamaño que el editor leda por defecto a los botones para luego redimensionar, sino basta con pinchar en la ventana y,sin soltar, arrastrar el ratón hasta conseguir el tamaño deseado, soltar, y, acto seguido,disponer de un botón con las proporciones perfiladas.

Las propiedades y eventos que un control tiene se pueden consultar y alterar en el inspector deobjetos. los controles poseen propiedades comunes (como Caption, Name, Left, Hint, etc.),aunque hay otras que son particulares para cada uno. En este apartado se verán laspropiedades comunes y particulares de los controles de tipo botón, con el objetivo de que lascomunes queden zanjadas para ulteriores explicaciones. Someramente, y según aparecen en elinspector de objetos, son:

- Cancel. Permite ejecutar el código asociado al botón cuando se pulsa la tecla [Esc]. Sueleser habitual asociarla al botón de cerrar la ventana; de esa forma pulsando [Esc] se da laimpresión de abandono de la ventana actual para ir a la anterior. Esta propiedad es particularde los controles de tipo botón y se ha de poner a True cuando queramos activarla y False encaso contrario.

- Caption. Contenido del control. Es el literal que se visualiza y al que podemos añadir elcarácter & para convertir en acceso rápido la tecla que lo precede. &Aceptar se traduciría enel botón como Aceptar, lo cual permitiría pulsar la tecla A para que la acción asociada albotón se ejecutara.

- Cursor. Aspecto que adoptará el cursor del ratón cuando lo paseemos sobre el control. Eltipo de cursor puede ser definido por el usuario, pero lo más rápido es asignarle alguno delos predefinidos (definidos por medio de identificadores, tal y como se ve en la figura 8).

Figura 8: Cursores predefinidos

- Default. Permite ejecutar el código asociado al botón cuando se pulsa la tecla [Intro]. Sueleser habitual asociarla al botón de cerrar la ventana; de esa forma pulsando [Intro] se da laimpresión de acceder de la ventana actual para ir a la siguiente. Esta propiedad es particularde los controles de tipo botón y se ha de poner a True cuando queramos activarla y False encaso contrario.

- DragCursor. Aspecto del cursor cuando se pasea el puntero sobre el control si se estápinchando y arrastrando (Drag&Drop) elementos entre ventanas de una aplicación y/o entre

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

aplicaciones Windows.

- DragMode. Forma en la que se va a controlar la técnica de arrastrar y soltar. Admite dosvalores: automático y manual.

- Enabled. Determina el estado habilitado o inhabilitado (color gris y no accesible) de uncontrol.

- Font. tipo de letra, tamaño, color etc. del literal que aparecerá en el control.

- Heigth y Width. Alto y ancho, en píxels, del control.

- HelpContext. Ayuda asociada al control

- Hint y ShowHint. Literal del tip (etiqueta) que se mostrará cuando paremos el puntero delratón sobre el control. Cuando ShowHint está a true el Hint se muestra, si no, no.

- Left y Top. Coordenadas izquierda y superior que el control ocupa en la ventana.

- ModalResult. Valor devuelto por el control cuando se pulsa.

- Name. Nombre de control. Este nombre simbólico es el que se usa a lo largo de toda laaplicación para modificar propiedades del control en tiempo de ejecución.

Boton1.Enabled := true ;

- PopUpMenu. Menú local asociado al control. Se trata de un menú particular del control queaparece cada vez que pulsamos el botón izquierdo del ratón sobre el control específico, y noen otra parte.

- TabOrder. Secuencia en que se desplaza el foco al pulsar tabulador en la ventana. Delphimantiene un orden secuencial a medida que se van añadiendo controles a la ventana. Esteorden empieza en 0 y se va incrementando de 1 en 1, pero es modificable por el diseñadorde dicha ventana.

- TabStop. Habilita o no que la pulsación del tabulador tenga efecto sobre el control.

- Tag. Número identificativo del control.

- Visible. Hace desaparecer el control de la ventana, o no.

Eventos posibles, o procedimientos que se llaman cada vez que...

- OnClick: ...se pulsa un botón.

- OnDragDrop, OnDragOver, OnEndDrag, OnStartDrag: ...se realiza una operación dearrastrar y soltar (Drag&Drop)

- OnEnter: ...un control pasa de estar inactivo a estar activo.

- OnExit: ...el foco abandona el control en curso.

- OnKeyDown, OnKeyPress, nKeyUp: ...se presiona, se mantiene pulsada o se suelta unatecla.

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

- OnMouseDown, OnMouseMove, OnMouseUp: ...se presiona el botón del ratón, se mueve elpuntero o se suelta el botón del ratón.

Tanto las propiedades como los eventos se pueden asociar masivamente a un conjunto decontroles. Para ello seleccionamos aquéllos que se desean alterar, y se cambia alguna de suspropiedades. Por ejemplo si queremos que todos los botones tengan el literal "Botón",modificaríamos la propiedad Caption de todos los que estuvieran seleccionados; seguidamentehabrá que ir añadiendo 1, 2, 3, etc. para cada uno de ellos, pero no Botón 1, Botón 2, etc.Operaciones como poner el mismo Name a una colección de controles no están permitidas,pues el nombre es unívoco.

Propiedades de los textos

Para ver que muchas de las propiedades y muchos de los eventos son comunes entre controles,echemos un vistazo al inspector de objetos correspondiente a un texto (ver figura 9)

Figura 9: Propiedades de un control de texto

Como se puede ver, algunas nuevas respecto a los botones, pero también muchas comunes.Entre las nuevas cabe destacar las encargadas de alinear el texto, bien respecto a susdimensiones, bien respecto a la ventana, tamaños y aspectos del literal escrito, si se tiene encuenta o no el símbolo &, etc. Por otra parte, entre los eventos, aparece como nuevoOnDblClick, que se invoca cuando se hace doble clic sobre el texto implicado; pero no aparecenlos relacionados con perder o ganar el foco (pues no se puede dar el foco a un texto) o pulsar ylevantar una tecla.

9Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Ejemplo

Pongamos un ejemplo en el que jugaremos con los dos controles vistos. La idea seguida ha sidohabilitar el botón 1 con la propiedad Cancel a True (para que se invoque al pulsar [Esc]), elbotón 2 con Default y asociar a ambos un procedimiento, llamado Texto, para que lo ejecutencuando se pulsen. En dicho procedimiento se distingue si se llama con uno u otro botón, y sepinta uno u otro texto. El procedimiento Texto es el que se ve en el fuente 2.

Sea cual sea el botón pulsado, se habilita un tercer botón, el de Salir, inicialmente inhabilitadopor tener su propiedad Enabled a False. Su misión es permitir salir de la aplicación. Cada botónlleva un tipo de letra, un tamaño, etc. particular.

// --- Fuente 2 ----------------------------------------------------------procedure TForm1.Texto(Sender: TObject);begin if Sender=Boton1 then Label1.caption := 'Se ha pulsado el botón 1 o [Esc]' else Label1.caption := 'El botón 2 se pulsó o [Intro]';

Boton3.Enabled := Trueend;

La ventana que recoge este proceso es el la de la figura 10 y el código completo que hay queensayar el del fuente 3.

Figura 10: Aplicación para uso de botones y texto

// --- Fuente 3 --------------------------------------------------------unit Boton1;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type TForm1 = class(TForm) Boton1: TButton; Label1: TLabel; Boton2: TButton; Label2: TLabel; Boton3: TButton; procedure Texto(Sender: TObject);

10Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

procedure Salir(Sender: TObject); private { Private declarations } public { Public declarations } end;

var Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Texto(Sender: TObject);begin if Sender=Boton1 then Label1.caption := 'Se ha pulsado el botón 1 o [Esc]' else Label1.caption := 'El botón 2 se pulsó o [Intro]'; Boton3.Enabled := Trueend;

procedure TForm1.Salir(Sender: TObject);begin Close;end;

end.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Delphi paso a paso (IV): Controles (II)Por Vladimir Algara

Con esta entrega continuamos en el punto y seguido dejado en la anterior.

Recordando un poco lo visto hasta ahora, diremos que estamos en disposición de crearventanas con botones (no gráficos) y menús, que permiten lanzar los distintos procesos que unaaplicación puede contemplar. Tampoco supone ningún problemas la utilización de los textos fijosen una ventana, pero lo más importante, base esencial para poder huir hacia adeleante en elproceso creativo e investigativo, es el conocimiento del lenguaje y del comportamiento de lasaplicaciones Windows.

Tener claro cómo se comportan, por ejemplo, los eventos, supone tener recorrido gran parte delcamino de aprendizaje, pues todas las situaciones que una ventana, aplicación, etc. puedeadvertir vienen recogidas en la gestión de eventos en Windows.

Cómo funciona un conjunto de botones de radio (RadioButtons), cómo se rellena una lista(ListBox o ComboBox), etc. podrá ser un camino más o menos costoso, pero se limitará aconsultar la ayuda en línea, los manuales de producto o algún que otro artículo de los muchosarticulistas pesados que hay por ahí. La filosofía del asunto es lo que realmente importa, y no elconocer al dedillo los métodos y datos que una clase posee para hacer lo que se pretende en unmomento dado. Por otra parte, y esto lo podremos comprobar en el ejemplo final de estaentrega, Delphi automatiza los procesos más comunes, de manera que la labor del programadorse limita a generar la aplicación desde los editores visuales, siendo su aportación (en lo que atirar líneas de código se refiere) mínima.

Botones gráficos

Ya dijimos que los controles por excelencia eran los PushButton. Estos botones eran, y son, losencargados de lanzar los procesos, cerrar las ventanas, dar conformidad a los datos, etc.; losbotones gráficos no son más que meros botones, pero con una imagen en su interior, la cual nole da más funcionalidad, sino mejor aspecto.

Los botones gráficos se encuentran en la carpeta Aditional y los iconos que los representan (noseleccionado y seleccionado) son los de la figura 1.

Figura 1: Iconos para botones gráficos

Como ocurre y ocurrirá con cualquier control de la paleta de herramientas, una vezseleccionados se llevan a la ventana y allí pasamos a darles especificidades.

Nota: Vuelvo a recordar que cada vez que se pincha alguno de los elementos en la paleta deherramientas, éste se deselecciona automáticamente, a no ser que mantengamos pulsada latecla [Shift] al pincharlo.

El nombre (Name) dado a un control de tipo botón, así como su literal inicial es BitBtn1, BitBtn2,etc.

Propiedades de los botones

2Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Las propiedades y eventos generales que un control tiene se pueden consultar en la entregaanterior, en esta nos limitaremos a exponer las características específicas de los botonesgráficos, todas ellas alterables desde el inspector de objetos.

- Glyph. La más elemental de las propiedades que un botón gráfico es, propiamenste, elgráfico que lleva en su interior. A través de la propiedad Glyph se asocia un archivo demapa de bits al botón gráfico. Cuando elegimos Glyph en el Inspector de Objetos aparecenunos puntos suspensivos que, al pulsarlos, nos llevan a la ventana de lectura y salvaguardade gráficos (ver figura 2).

Figura 2: Ventana de edición de BitMaps

Cuando en la ventana de la figura 2 se pulsa sobre el botón Load, aparece una cajaestándar de directorios y ficheros destinada a elegir alguno de los bitmaps existentes.Delphi trae una serie de ellos predefinidos en el directorioC:\...\BORLAND\DELPHI32\IMAGES. Se trata de botones de dos estados, uno para elestado activado y otro para el desactivado.

Como es habitual en Delphi, estos botones pueden ser diseñados por nosotros mismos,con cualquier cantidad de estados, desde aplicaciones destinadas a tal fin, por ejemplo,PaintBrush.

Pulsando el botón Ok de la ventana de la figura 2 veremos que el gráfico se asocia albotón. Otra posibilidad es salvarlo (botón Save) con otro nombre o en otro destino distintoal actual. Clear rompe el vínculo entre imagen y botón, dejando Glyph en None; Cancel yHelp son autoexplicativos.

Kind. Aparte de poder elegir libremente el gráfico que se incluirá dentro del botón, Delphiofrece toda una colección de los botones gráficos más habituales, así como la acciónasociada por defecto. Esta propiedad es muy importante, pues operaciones como la decerrar una ventana, cancelar un proceso, llamar a la ayuda sensible al contexto, etc., estánimplementadas; otra vez, automáticamente. Los botones y sus características se puedenver en la tabla 1 y su aspecto en la figura 3.

Valor SignificadobkCustom Son los botones configurables, es decir, los que hay que especificar gráfico, un

texto, una acción, etc. Es el valor por defecto.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

bkOK Tiene una marca de Check en verde y el texto "OK" (el texto corresponde a lapropiedade Caption, y por lo tanto se puede modificar sin problemas, al igualque las demás). La propiedad Default la tiene puesta a True y cuando espinchado (o se pulsa la tecla [Intro]) devuelve el valor mrOK (constante que sealmacena en el dato ModalResult del formulario (clase TForm).

bkCancel Tiene un Aspa en rojo y el texto "Cancel". La propiedad Cancel la tiene puesta aTrue y cuando es pinchado (o se pulsa la tecla [Esc]) devuelve el valormrCancel.

bkYes Tiene una marca de Check en verde y el texto "Yes". La propiedad Default latiene puesta a True y cuando es pinchado (o se pulsa la tecla [Intro]) se daconformidad a lo tecleado y devuelve el valor mrYes.

bkNo Tiene una señal de prohibido en rojo y el texto "No". La propiedad Cancel latiene puesta a True y cuando es pinchado se omite lo tecleado en la ventana yésta se cierra; devuelve el valor mrNo.

bkHelp Tiene una interrogación en azul y el texto "Help". Cuando es pinchado apareceuna ventana de ayuda con el contenido del archivo especificado en la propiedadHelpFile.

bkClose Tiene una puerta indicando salida de la aplicación y el texto "Close". Lapropiedad Default la tiene puesta a True y cuando es pinchado se cierra elformulatio

bkAbort Tiene un Aspa en rojo y el texto "Abort". La propiedad Cancel la tiene puesta aTrue.

bkRetry Tiene dos flechas circulares verdes y el texto "Retry".bkIgnore Tiene un caminante de color verde y el texto "Ignore". Se usa para persistir en

una operación cuando ocurrió algún error.bkAll Tiene una doble marca de Check en verde y el texto "All". La propiedad Default

la tiene puesta a True.Tabla 1: Botones predefinidos.

Figura 3: Botones gráficos por defecto.

Layout. Posición del gráfico respecto al texto. Arriba, abajo, a la izquierda o a la derecha.La posición por defecto es el gráfico a la izquierda del texto (blGlyphLeft).

Margin. Determina la cantidad de píxels que se van a poner entre el gráfico y el borde elbotón. Un valor igual a 0 hace que el texto quede pegado al borde. Un valor de -1 (valor pordefecto) centra texto y grafico respecto el ancho total del botón.

ModalResult. Valores numéricos devueltos por el botón. Ver propiedad Kind.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

NumsGlyph. Cantidad de estados que un bitmap va a tener. Un bitmap con dos estadoscontiene, en un único archivo, los estados de habilitado y deshabilitado. Para que puedanser interpretados correctamente por Delphi ambos deben tener las mismas dimensiones yestar uno a continuación del otro. Los valores posibles oscilan ente 1 y 4, siendo 1 el valorpor defecto. Los valores posibles y sus significados se pueden consultar en la tabla 2.

Posición Estado Descripción1 Levantado Aspecto cuando el botón está no presionado y no es valor por

defecto.2 Inhabilitado Aspecto cuando el botón no puede pincharse (disabled)3 Hundido Aspecto cuando el botón está presionado.4 Por defecto Aspecto cuando el botón está no presionado y es valor por

defecto.Tabla 2: Número de imágenes por bitmap

Spacing. Sigue la misma lógica que Margins, esta vez para fijar el número de píxels quesepararán texto e imagen. El valor por defecto es 4.

Los eventos posibles son idénticos a los que se comentaron para los botones no gráficos.

Recordar que tanto las propiedades como los eventos se pueden asociar masivamente a unconjunto de controles, seleccionando los deseados y modificando las pertinentes propiedades.

Listas deslizantes ( ListBox )

Como hay que seguir por algún otro control, he elegido éste porque da mucho juego. Los ListBoxson una colección de elementos en forma de lista que el usuario dispone para elegir allí algúnelemento. En Delphi existen distintos tipos de ListBox, o más bien existen distintas clasesreferentes a este tipo de controles. Usaremos una u otra dependiendo de los datos con los quelos rellenemos.

Por ejemplo, la clase TListBox se rellena con un tipo especial de cadena denominado TString, obien desde un fichero de texto que la propia clase TString se encarga de interpretar. Tambiénexisten la clase TFileListBox (una clase específica para rellenar con los archivos de undeterminado directorio), TDirListBox (para estructuras de directorio), TDBListBox (paraconectar a bases de datos) y otras más para los ComboBox (híbrido entre control de edición yListBox).

En esta entregra se abordará el ListBox sencillo, dejando para más adelante el de estructurasde ficheros y directorios y el de conexión con bases de datos.

Propiedades de los ListBox

Nuevamente, hacemos un rápido recorrido por eventos y propiedades que están íntimamenteligados a lo ListBox.

Alignt. Esta propiedad, si bien no es específica de los ListBox, sí ha aparecido en nuestraserie por primera vez. Con ella se determina cómo se va a mostrar el control respecto a laventana. Por ejemplo, si se opta por alClient, se estará diciendo que el ListBox se amoldeal contorno absoluto de la ventana, por lo que a medida que crezca o disminuya ésta,también lo hará su ListBox asociado.

5Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

BorderStyle. Determina si el ListBox llevará o no borde, dando así aspecto tridimensional oplano.

Columns. Cantidad de columnas del ListBox.

ExtendedSelect. En un ListBox se pueden elegir un elemento o varios (para ello hay quemodificar la propiedad MultiSelect), y se puede hacer sin más que ir pinchando aquéllos quequeramos seleccionar o deseleccionar, o bien siguiendo las técnicas estándar de Windows,en las que pulsando la tecla [Ctrl] se eligen elementos dispersos y presionando [Shift] unrango de ellos. ExtendedSelect permite definir cómo se va a comportar este modo deselección.

Items. Elementos con los que el ListBox será rellenado. Como ya se adelantó, este datoalberga un objeto de la clase TString, y, como tal, lo podemos manipular artesanalmente,pero Delphi brinda la posibilidad de ir añadiendo en forma de lista la relación de ítems quese quieren mostrar. Esta relación se teclea en una ventana como la de la figura 4. Ennuestro ejemplo se especifican los meses del año.

Figura 4: Definición de los elementos de un ListBox.

Una posibilidad, no obligada, consiste en transformar esta lista en un archivo (archivo planode texto ASCII, editable desde cualquier parte), para después poderlo recuperar y asignaren tiempo de ejecución.

MultiSelect. Permite que se pueda elegir más de un ítem entre los de una relación. Laopción por defecto es false.

Sorted. Parámetro que, de estar a true, fuerza una ordenación alfabética del contenido delListBox y así lo muestra. Esta opción ordena los ítems y los reasigna, perdiendo cualquierreferencia a las posiciones originales.

Ejemplo de ListBox

Pongamos un ejemplo en el que jugaremos con los dos controles vistos. La idea seguida ha sido

6Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

habilitar dos botones configurables (propiedad Kind a bkCustom) a los que se le quita el texto desu interior y cuyo cometido es ordenar alfabéticamente, o no, el ListBox de los meses del año,un botón estándar para cerrar la ventana (propiedad Kind a bkClose) al que cambiamos lapropiedad Caption para que ponga "Cerrar" en vez de "Close", y un ListBox con los meses delaño correctamente rellenados.

1.- Pinchamos en el control ListBox y accedemos a su propiedad Items, aparecerá la ventanade la figura 4, donde se rellenará convenientemente. Para que sirva para el ejemplosiguiente, podemos salvar esa relación bajo el nombre de archivo: MESES.TXT.

2.- Elegimos los botones gráficos y colocamos tres. Dos de ellos serán bkCustom (opción pordefecto) y el otro bkClose. Entramos en la propiedad Glyph de los botones configurables yelegimos alguno de los bitmap de ejemplo. La ventana diseñada se puede parecer a la dela figura 5.

Figura 5: Ejemplo de prueba

3.- Accedemos a los eventos de los botones para que, al pulsar uno u otro, el ListBox seordene o no. Ambos pueden llamar al mismo procedimiento, por lo que en el OnClick deambos escribiremos OrdenDesorden; automáticamente se generará el códigocorrespondiente donde ensayaremos lo que se ve en le fuente 1.

// --- Fuente 1 ----------------------------------------------------------procedure TForm1.OrdenDesorden(Sender: TObject);begin if Sender=PbSiSort then LBMeses.Sorted := true else LBMeses.Sorted := falseend;

Lo que aquí se especifica es que si el control que ha ejecutado el procedimiento se llamaPbSiSort (nombre dado al botón de la bombilla encendida), entonces se modifica la propiedadSorted del control LBMeses (nombre dado a nuestro ListBox) a true, y si no, se pone a false.

Este código funciona bien al ordenar, pero como ya se comentó algo más arriba, una ordenaciónsupone la reasignación de los elementos y la pérdida de la secuencia original, por lo que nobasta con cambiar a false la propiedad Sorted, ya que los elementos no se vuelven a asignarcorrectamente. Aunque la única línea de programación escrita ha sido la del procedimientoOrdenDesorden, no hemos conseguido nuestro propósito final, aunque eso sí, ha faltado muypoco.

Para resolverlo, basta con asignar correctamente los distintos ítems, y para ello hay dos

7Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

caminos. Primero voy a empezar por el menos óptimo de los dos, luego intentaré hacerlo máselegante y general. En el fuente 2 se añaden los elementos, uno a uno, al ListBox implicado.

// --- Fuente 2 ----------------------------------------------------------procedure TForm1.OrdenDesorden(Sender: TObject);begin if Sender=PbSiSort then LBMeses.Sorted := true else begin LBMeses.Sorted := false ; LBMeses.Items.Strings[ 0 ] := 'enero' ; LBMeses.Items.Strings[ 1 ] := 'febrero' ; LBMeses.Items.Strings[ 2 ] := 'marzo' ; LBMeses.Items.Strings[ 3 ] := 'abril' ; LBMeses.Items.Strings[ 4 ] := 'mayo' ; LBMeses.Items.Strings[ 5 ] := 'junio' ; LBMeses.Items.Strings[ 6 ] := 'julio' ; LBMeses.Items.Strings[ 7 ] := 'agosto' ; LBMeses.Items.Strings[ 8 ] := 'septiembre' ; LBMeses.Items.Strings[ 9 ] := 'octubre' ; LBMeses.Items.Strings[ 10 ] := 'noviembre' ; LBMeses.Items.Strings[ 11 ] := 'diciembre' endend;

Como se puede apreciar, se accede al dato (o propiedad) Items, el cual dispone de otro dato,Strings de la clase TString, donde se almacena la cadena. Dado que TString es una grancadena alfanumérica, tenemos la posibilidad de referirnos a sus distintos elementos a través deun índice (idéntico a los arrays). Así, por ejemplo, para invocar al elemento 7 haremos losiguiente:

LBMeses.Items.Strings[ 7 ] := 'agosto' ;

Basta con recorrérnoslos todos reasignando correctamente para que funcione como queremos.

El abordaje menos artesanal pasa por disponer de un archivo de texto donde tener almacenadoslos ítems correctos en sus correctas posiciones; concretamente ese archivo que almacenamoscon el nombre de MESES.TXT y que ahora vamos a utilizar.

En el fuente 3 se ve cómo quedaría según este nuevo camino (las líneas introducidasmanualmente llevan comentarios para distinguirlas de las que se insertan automáticamente).

Además se deshabilita el botón recién pulsado (habilitando el otro), para no permitir efectuar lamisma operación dos veces y ver el aspecto de los botones gráficos deshabilitados (2 estadosen el bitmap). También se añade un elemento nuevo de programación, la declaración devariables a nivel de aplicación y su inicialización óptima.

// --- Fuente 3 ----------------------------------------------------------. . .var Form1: TForm1; oMeses: TStringList // Variable auxiliar;

8Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

implementation

{$R *.DFM}

procedure TForm1.OrdenDesorden(Sender: TObject);begin

// Código tecleado if Sender=PbSiSort then begin PBSiSort.Enabled := false; // Inhabilitar botón pulsado PBNoSort.Enabled := true; // Habilitar el otro botón LBMeses.Sorted := true; // Ordenar end else begin PBSiSort.Enabled := true; // Habilitar el otro botón PBNoSort.Enabled := false; // Inhabilitar botón pulsado LBMeses.Sorted := false; // No ordenar // Leer contenido del archivo meses oMeses.LoadFromFile('MESES.TXT'); // Borrar contenido del ListBox LBMeses.Clear(); // Reasignar ítems al ListBox LBMeses.Items.AddStrings(oMeses); endend;

// Instanciación de la variable global (creación)initialization oMeses := TStringList.Create;

end.

Por una parte se ha creado la variable oMeses de la clase TStringList, que albergará elcontenido del archivo MESES.TXT. Para no inicializar esta variable cada vez que se entra en elprocedimiento OrdenDesorden, se hace en el módulo principal, y bajo la palabra reservadaInitialization. En ese punto se crea el objeto mediante:

oMeses := TStringList.Create;

A partir de este momento la variable oMeses se podrá utilizar como un objeto de la claseTStringList en todos los procedimientos y funciones de la unidad (Unit) correspondiente a estaventana, y en concreto en el procedimiento OrdenDesorden.

Para leer el contenido del archivo MESES.TXT se recurre al método LoadFromFile() de la claseTStringList, para que quede almacenado en forma de cadena de ítems entendible por nuestroListBox.

oMeses.LoadFromFile('MESES.TXT');

A continuación añadimos la cadena completa mediante el método AddStrings(), que recibe como

9Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

parámetro la cadena a añadir; no sin antes vaciar el ListBox (de otra forma añadiría 12 meses alos 12 ya almacenados, con lo que dispondríamos de un ListBox que iría creciendo según unaprogresión aritmética de razón 12).

LBMeses.Clear();LBMeses.Items.AddStrings(oMeses);

Antes de acabar, hacer una sola observación. La variable oMeses no sería necesaria, sólo se haintroducido para practicar con elementos del lenguaje. Su omisión, y por tanto su declaración ysu instanciación, se podría haber resuelto de la siguiente forma

LBMeses.Clear();LBMeses. Items. AddStrings( TStringList. Create. LoadFromFile('MESES.TXT') );

Pero, realmente, queda algo farragoso y altamente ilegible.

En la próxima entrega se seguirá la senda entreabierta de las listas deslizantes, aplicando susfuncionalidades a la visualización de bitmaps e intentando, por todos los medios, que la mayoríadel trabajo lo haga Delphi por nosotros, que eso, después de las vacaciones de verano (en elHemisferio Norte), es harto agradecido

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (V): Controles (III)Por Vladimir Algara

Como ya se anunció en el número anterior de Algoritmo , en este artículo se abordará la formade manipular las funcionalidades de los ListBox y así solucionar muchas de las necesidades quese presentan a la hora de perfilar una aplicación.

Recordando lo visto hasta ahora acerca de ListBox, saber que partimos de una colección dedatos almacenados en forma de tabla, que podemos rellenar a nuestro gusto (manualmente oleyendo los datos almacenados en un archivo). Esta fuente de la que obtener los datos, comoveremos más adelante, no se va a quedar anclada aquí, todo lo contrario, se va a caracterizarpor su diversificación.

Una vez relleno el ListBox, podía ser ordenado o restaurado según el original, así como aplicarlecualesquiera de las propiedades y acciones comunes a los controles, a saber, inhabilitar,redimensionar, ocultar, etc.

Resumiendo, no hemos visto gran cosa de lo controles tipo ListBox.

A partir de que se conozcan más formas de rellenar controles de tipo ListBox, se irán viendomás funcionalidades y más ventajas, y ése, concretamente, es el cometido del presente artículo,por lo tanto, me voy a dejar de zarandajas y me meteré en el meollo de la cuestión, porque ya lodijo..., bla, bla, bla..., etc.,...bla, rebla.

Tipos de ListBox

Los ListBox, o sus hermanos los ComboBox, se encuentran en la carpeta Standard y los iconosque los representan (no seleccionados y seleccionados) son los de la figura 1.

Figura 1. Iconos para ListBox y ComboBox

El nombre (Name) que por defecto se da a un control de tipo ListBox es ListBox1, ListBox2,etc., y esto será lo primero que iremos cambiando, pues ello permitirá una mejor localización eidentificación posterior (en el ejemplo final vamos a manipular hasta un total de tres listasdeslizantes).

Existen otros tipos de ListBox, diferentes a los que vienen en la paleta Standard, de ellos vamosa ver dos, uno destinado a mostrar un árbol de los directorios del disco y otro para ver losarchivos de un directorio específico.

Con ambos dos y con uno estándar, construiremos una ventana en la que se podrá teclear elnombre y/o la extensión de los archivos que queremos visualizar (será en un control de edición).De acuerdo al nombre escrito allí se almacenará en un ListBox de la clase TFileListBox larelación de todos los archivos que cumplan la condición de la máscara, para facilitar lanavegación por los distintos directorios se recurrirá a un ListBox de la clase TFileListBox y paratener una relación de los archivos que se vayan seleccionando se utilizará un ListBox de la claseTListBox (el estándar). Dado que este ListBox final va a tener un filtro de todos aquellos archivosque han merecido nuestra atención, si partimos de que los archivos a tratar van a ser BitMaps,podemos completar el ejemplo haciendo que el BitMap elegido se visualice por pantalla.

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Para resumir, y como soy consciente de que la explicación escrita no ha quedado nada clara,muestro en la figura 2 la pantalla a la que quiero llegar.

Figura 2. Pantalla ejemplo de ListBox

Nota : Los distintos elementos disponibles son: Un control de edición donde especificar el tipo dearchivos que se quieren visualizar (opción por defecto *.bmp), un ListBox con la estructura dedirectorios de nuestra unidad, otro ListBox con los archivos que cumplen la máscara (*.bmp) deldirectorio elegido (existe también una parte donde se especifica en qué directorio nosencontramos -en verde en la ventana-), otro ListBox con los elementos elegidos en el anterior yuna pantalla de visualización del bitmap elegido.

Propiedades de los ListBox

Como viene siendo habitual, hacemos un rápido recorrido por eventos y propiedades no vistospor el momento, y que están íntimamente ligados a los ListBox. En primer lugar se van acomentar las propiedades de los controles TFileListBox y a continuación los deTDirectoryListBox.

Propiedades de TFileListBox

FileEdit. Es una propiedad (dato) con la que se asocia, de manera automática, un ListBox aun control de edición. El objetivo de esta operación es que, a medida que nos vamosmoviendo por el ListBox o seleccionamos alguno de sus datos, el contenido del control deedición se amolda a la operación realizada en el ListBox, quedando ambos controles, pues,sincronizados. Dado que a medida que añadimos controles en una ventana éstos se vanregistrando, si pinchamos sobre la propiedad comentada (FileEdit) aparecerá una relaciónde los controles de edición disponibles hasta el momento, nuestra misión es elegir algunode ellos y echarlo a andar; nada más sencillo.

FileType. Tipos de archivos a visualizar según sus atributos. Se trata de una estructura enla que se puede elegir entre los distintos tipos de la tabla 1.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Valor SignificadoftReadOnly

Atributo de Sólo Lectura

ftHidden Atributo de OcultoftSystem Atributo de Archivo del SistemaftVolumeID Muestra la etiqueta del volumen

seleccionadoftDirectory Atributo de directorioftArchive Atributo de ArchivoftNormal Archivo sin atributos

Tabla 1. Tipos de archivos según los atributos

Mask. Máscara por defecto de los archivos que se van a mostrar en el ListBox. Si se dejaen blanco no se produce filtrado y, por lo tanto, se manipulan todos los archivos. Paranuestro ejemplo se puede rellenar con *.bmp.

ExtendedSelect. En un ListBox se pueden elegir un elemento o varios (para ello hay quemodificar la propiedad MultiSelect), y se puede hacer sin más que ir pinchando aquellos quequeramos seleccionar o deseleccionar, o bien, siguiendo las técnicas estándar de Windows,en las que pulsando la tecla [Ctrl] se eligen elementos dispersos y presionando [Shift] unrango de ellos. ExtendedSelect permite definir cómo se va a comportar este modo deselección.

MultiSelect. Permite que se pueda elegir más de un ítem entre los de una relación. Laopción por defecto es false.

Propiedades de TDirectoryListBox

DirLabel. De la misma manera que un control de tipo TFileListBox es posible asociarlo a uncontrol de edición (a través del dato FileEdit), un control de tipo TDirectoryListBox se puedeasociar a un control de tipo TLabel (texto fijo en pantalla). Como antes, el objetivo de estaoperación es tener ambos controles sincronizados, y que la operación producida en elprimero repercuta en el segundo. En el ejemplo de la figura 2 se ha incluido el texto dentrode un recuadro verde, con la intención de que quede más bonito, objetivo que no seconsigue.

FileList. A través de esta propiedad se permite otra sincronización más. En este caso elcontrol objeto de sincronización es uno del tipo TFileListBox. Dado que un TFileListBoxposee una máscara de entrada (visto más arriba), si sincronizamos un TDirectoryListBoxcon un TFileListBox, éste se rellenará automáticamente cada vez que naveguemos poraquél.

Obsérvese lo fácil que resulta realizar una ventana que vaya dando cuenta de cada uno denuestros movimientos por los directorios, todo sin trazar una línea de código.

Ejemplo de visualización de BitMaps

Los pasos a seguir para obtener la ventana del ejemplo han sido:

1.- Pinchamos en los controles TLabel para poner el literal deseado en cada uno y, en el casodel que está enmarcado en la caja verde, para cambiar su fuente (Arial, 8 Bold y color

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

clWhite) y su color de fondo (clGreen).

2.- Elegimos los controles de edición TEdit para habilitar un lugar donde especificar la máscarade los archivos a visualizar. Si bien este control lo podríamos asociar al ListBox de archivos,no lo haremos, pues la funcionalidad que se le dará será, a mi juicio, más interesante que elmero hecho de la sincronización. Dado que al ubicar un control de edición éste se rellenaautomáticamente con el literal Edit1 modificaremos su propiedad Text para dejarlo enblanco o, siguiendo un criterio que más interesante, lo rellenaremos con *.bmp.

3.- Elegimos un control TFileListBox. Se puede comprobar que se permite su asociación concualquier control de edición presente en la ventana, y en concreto con el que hemos puestoen el paso 2.

4.- Elegimos un control TListBox que se rellenará cuando elijamos, haciendo doble clic, desdeel ListBox del paso 3.

5.- Elegimos un control TDirectoryListBox y lo asociamos mediante su propiedad DirLabel conel control de edición de la caja verde (a todo esto, la caja verde se hace con un control detipo TShape, que se encuentra en la paleta Additional), y asociamos su propiedad FileListcon el ListBox del paso 3. ¡Buf!.

6.- Sin trazar una sola línea de código ya tenemos mucho. Se puede poner en marcha y ver elresultado de las distintas componentes de la ventana.

7.- Para completar un poco la aplicación y para que no sea Delphi el único que trabaje, se vana añadir un par de botones, uno de cerrar ventana y otro para visualizar el BitMap delListBox del paso 4 (también se podrá visualizar haciendo doble clic sobre el ListBox).

Para la ubicación y manipulación de archivos gráficos existe un control que se encarga deellos, este control es de la clase TImage. Un control de este tipo permite, entre otrascosas, ajustar el BitMap a su tamaño o sólo visualizar, en el área definida, aquella porciónque quepa.

Alguna de las propiedades más importantes de los controles TImage, o al menos aquellosque vamos a manejar en este ejemplo, son:

- AutoSize. Obvia el tamaño prefijado del control TImage, de tal forma que si el archivográfico a visualizar es mayor que el área definida, ésta se ignora y el BMP ocupa eltotal de su tamaño. Esto puede provocar, incluso, que ni la ventana pueda albergarlo,por lo que, automáticamente, aparecerán en dicha ventana las barras dedesplazamiento oportunas.

- Center. Centra la imagen en el área definida, si no la sitúa en el ángulo superiorderecho.

- Picture. Mediante esta propiedad se asigna al control la imagen que se quierevisualizar. Dado que lo que se pretende hacer es volcar el contenido de una imagenseleccionada por nosotros, en este ejemplo se dejará en blanco dicha propiedadPicture, y se asignará, en tiempo de ejecución, de la siguiente forma:

Imagen.Picture.LoadFromFile( <Archivo> );

- Stretch. Obliga a que el BMP se ajuste al tamaño fijado o, de no caber, se visualicesólo lo que quepa en el área definida. Esta propiedad, que en principio es muy potente,puede hacer que un BMP de gran tamaño quede reducido y distrosionadoexcesivamente al ubicarlo en un recinto excesivamente pequeño. En nuestro caso lovamos a dejar a false, cediendo el control del tamaño a otro control auxiliar.

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Precisamente porque los archivos gráficos pueden ser enormes o pequeños, apaisados uhorizontales, alargados, etc., sería bueno reservar esa famosa zona de pantalla dondeubicar el archivo, independientemente del formato de éste. Para este cometido se va arecurrir a un control del tipo TScrollBox, y dentro de él ubicaremos la imagen (TImage).

La operativa a seguir es ubicar, en primer lugar, un TScrollBox y después un TImage dentrode él. Esto hace que el control TImage quede constreñido al control padre y que, en ningúncaso, se pueda escapar de su recinto. Las propiedades que nos pueden interesar en loscontroles de tipo TScrollBox son:

- AutoScroll. Pone, de manera automática y cuando sea necesario, barras dedesplazamiento vertical y horizontal. Poniéndolo a true conseguimos que, cuando laimagen sea menor que el recuento definido, aparezca en su totalidad, y cuando no,podamos desplazarlos para verla en su totalidad.

- BorderStyle: Tipo de marco que se va a utilizar, según los valores bSingle (tal comoaparece en la figura 2) y bNone (sin marco).

- Una vez ubicado el control TImage dentro de la caja TScrollBox hacemos que aquél seajuste al tamaño total de éste, lo cual se puede consigue con la propiedad Align deTImage. Asignando a Align la constante alClient forzamos que toda la imagen se amolde altamaño de la caja, pero existe un pero; si visualizamos la imagen en tiempo de ejecución novamos a conseguir que aparezcan las mencionadas barras de desplazamiento. Estasbarras sólo aparecen cuando la propiedad Align está a alNone. Por lo tanto, en primerainstancia, podemos elegir alCliente (lo cual provocará que el editor de ventanas seencargue de colocar y redimensionar TImagen a los contornos de la caja) e inmediatamentedespués cambiarlo a alNone, para conseguir el efecto deseado en tiempo de ejecución.

8.- Accedemos a los eventos del botón de visualización, concretamente a OnClick, para que alpulsar sobre el Botón de Ver se muestre el contenido del archivo gráfico elegido. A esteprocedimiento se le ha llamado VerBmp y su contenido es el del fuente 1. En él se inicializala variable nPos, la cual servirá para determinar qué elemento del ListBox "estándar" (el dela clase TListBox) se ha elegido.

Una vez que se tiene la posición en nPos la utilizamos para extraer el ítem concreto, todoello a través del dato Items del ListBox (consistente en una cadena de caracteresorganizada en forma de tabla o array)

ListBox2.Items[nPos]

Esto hará que dispongamos del nombre del archivo y su ruta, que es lo que efectivamentese guarda en la lista deslizante.

// --- Fuente 1 ----------------------------------------------------------

procedure TForm1.VerBmp(Sender: TObject);var

nPos: integer;begin

nPos := ListBox2.ItemIndex;Imagen.Picture.LoadFromFile( ListBox2.Items[nPos] );

end;

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Si partimos de que no sólo queremos visualizar el BMP cuando pulsemos el botón de Ver,sino también al hacer doble clic en el ListBox, habrá que manipular el evento OnDblClick dedicho ListBox. Dado que la operación que hay que realizar en este caso es idéntica a la quese hace cuando pulsamos el botón de Ver, basta con asociar el mismo procedimientoVerBMP ya existente, y que es el que está definido en el fuente 1.

- Ambos llaman al mismo procedimiento, por lo que en el OnClick del botón Ver y elOnDblClick del ListBox aparecerá la llamada a VerBmp.

9.- Accedemos a los eventos del control de edición, para que cada vez que allí se escriba algo,ese algo se asuma como máscara del TFileListBox. El evento que hay que manipular esOnChange del control de edición, donde ensayaremos lo que se ve en le fuente 2.

Por medio del dato Text del control de edición (en nuestro caso EditDir) accedemos alcontenido del control (lo que hay escrito en ese preciso instante). Con ello modificamos lamáscara actual, lo cual se hace alterando el contenido del dato Mask de TFileListBox (ennuestro caso BMPListBox).

// --- Fuente 2 ----------------------------------------------------------

procedure TForm1.CambiarMascara(Sender: TObject);begin

BMPListBox.Mask := EditDir.Text;end;

10.- Por último, queda habilitar un método que permita transferir el dato seleccionado en elListBox de archivos al ListBox estándar. Comúnmente esta operación se realiza tras hacerdoble clic en el elemento deseado, por lo que, nuevamente, habrá que manipular el eventoOnDblClick, esta vez del TFileListBox.

Si al procedimiento lo llamamos Exportar, bastará con escribir esto en la casilla asociada aOnDblClick y pulsar [Intro]; en la porción de código que aparecerá seguidamente añadimoslas líneas que nos interesan.

Como habíamos comentado en las líneas de más arriba, los elementos de un ListBox sealmacenan en el dato Items, pero cuando manipulamos un ListBox de archivos estádisponible otro dato adicional llamado FileName, donde se almacena la unidad, directorio,nombre y extensión del archivo (toda la ruta). Bastará con decir:

BMPListBox.FileName;

Para disponer del dato que nos interesa.

Como vimos en el artículo anterior, para añadir datos a un ListBox se había de recurrir almétodo Add, pasándole como parámetro la cadena a añadir, por lo tanto:

cCadena := BMPListBox.FileName;ListBox2.items.Add( cCadena );

Donde cCadena habrá que inicializarla. En caso de no querer utilizar una variableintermedia, bastará con sustituirlo por su valor efectivo.

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Ya que estamos en el procedimiento invocado cada vez que se hace doble clic en el ListBoxde archivos, podría ser aquí, también, donde se habilitara o no el botón de VerBMP. Comosabemos, este botón se encarga de mostrar el BMP seleccionado en el ListBox, pero siocurriera que ese ListBox no contuviese aún ningún elemento, intentaría ver "algo" que aúnno existe, provocando un error de programa. Para evitar esta situación se puede inhabilitarinicialmente el boton de Ver, y sólo habilitarlo en el método Exportar que estamosperfilando.

PBVer.Enabled := true

Resumiendo, el procedimiento Exportar quedará como se ve en el fuente 3

// --- Fuente 3 ----------------------------------------------------------

procedure TForm1.Exportar(Sender: TObject);begin

ListBox2.items.Add( BMPListBox.FileName );PBVer.Enabled := true

end;

Obsérvese en el fuente 3 cómo no se realiza ningún control sobre el elemento que se va avolcar; esto quiere decir que podremos transferir un mismo archivo tantas veces comodoble clic hagamos en el TFileListBox. Este control se puede realizar recorriéndose elListBox de archivos y comprobando su existencia, del resultado de dicha verificacióndependerá que el elemento se transfiera o no.

Sabemos que los elementos del ListBox se almacenan en el dato Items, y que éstepertenece a la clase TString, por lo que se podrá hacer uso de cualquiera de sus métodosy datos. Uno de ellos es IndexOf, encargado de buscar una cadena dentro de un TString.Cuando IndexOf encuentra la cadena buscada, devuelve la posición que ocupa (siendo 0 laprimera de ellas, 1 la segunda y así sucesivamente). Cuando IndexOf devuelve -1 quieredecir que la cadena buscada no se encuentra. Será en este caso, y no en otro, cuandohabremos de transferir la información.

Si vemos el contenido del fuente 4, que no es más que una ligera modificación sobre elfuente 3 para tener en cuenta esta eventualidad, sabremos qué quiero decir.

// --- Fuente 4 ----------------------------------------------------------

procedure TForm1.Exportar(Sender: TObject);begin

if ListBox2.Items.IndexOf( BMPListBox.FileName ) = -1 thenbegin

ListBox2.items.Add( BMPListBox.FileName );PBVer.Enabled := true

endend;

Conclusión

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Concluir con Delphi es muy sencillo, pero también muy repetitivo. Se trata de un lenguaje y unaarquitectura que con poquísimas líneas de código permite realizar grandes cosas. En esteejemplo, sin ir más lejos, se lanzó un programa en el que interactuaban una serie de controles,todo ello gestionado desde el editor; fue después, tratando de encontrarle las cosquillas, cuandotuvimos que escribir la friolera de 9 líneas de código, de las cuales, si omitimos la variable (quesolo está puesta por dar algo más de claridad al código), los begin y los end, se nos quedan en5.

Esto, en mi tierra, es hacer las cosas bien.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Delphi paso a paso (VI): Controles (IV)Por Vladimir Algara

Para seguir avanzando en el mundo de los controles y retomar el hilo desde donde se abandonóen la entrega anterior, expondré el objetivo del presente artículo.

Hasta lo visto, cualquier ejemplo analizado se ha desarrollado y resuelto en una sola ventana,pero en ningún caso se ha recurrido a mostrar una segunda (o más) en la que seguircompletando los datos de la original.

Partiendo del ejemplo del número anterior, en el que se manipulaban un montón de ListBox y alfinal se mostraba un archivo BMP, dividiremos la ventana para que, en la primera, efectivamente,se permita movernos por los directorios, elegir el/los archivos oportunos, poner una máscara quefiltre el contenido del ListBox de archivos, etc., y la segunda dejarla, exclusivamente, para lavisualización y manipulación del archivo gráfico.

De acuerdo a esta lógica, por un lado se abordará la manipulación de la ventana de arranque dela aplicación, que es la que se ve en la figura 1, y por otro la gestión del BMP, la de la figura 2.

Figura 1. Aspecto de la primera ventana.

La ventana de la figura 1 apenas merece explicación respecto a lo expuesto en la entrega delmes pasado, pues, básicamente, lo único que se ha hecho con ella ha sido quitarle controles, ylos que han sobrevivido quedaron suficientemente explicados allí. La de la figura 2, si bien poseeel mismo control para visualizar la imagen, ha crecido en su casuística, pues ha pasado a teneren cuenta qué archivo gráfico se ve, si hay otros más susceptibles de mostrarse, si se quiereasignar un tamaño determinado o si se quiere ajustar la imagen al recinto definido (bienexpandiendo, bien reduciendo el BMP).

2Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Figura 2. Aspecto de la segunda ventana.

Básicamente, todo esto se controlará por medio de las propiedades Stretch y AutoSize. Ambasalmacenan un valor lógico, para activarlas o desactivarlas, y su significado es:

- Stretch (ajuste): Amoldar la figura al recinto especificado, de tal manera que si la figura esmás pequeña que el recinto, ésta se agrandará, agregando pixeles al original para que ocupetotalmente la superficie, y si es más grande que el recinto se eliminarán pixeles hasta quequepa. La distorsión de la imagen se ve tanto más acentuada cuanto menos se parezcan lasproporciones de la imagen al recinto predefinido.

- AutoSize (tamaño automático): Permite supeditar el recinto fijado al tamaño real de laimagen, de tal manera que si el archivo BMP es menor o mayor que la pantalla devisualización (cosa bastante habitual), el control que alberga la imagen se queda con el nuevotamaño. Esto es así mientras dure la sesión en la que estemos trabajando y, dado que ennuestro recinto vamos a tener que visualizar cualquier cantidad de imágenes, será buenoalmacenar las coordenadas y dimensiones originales para poder restaurarlas cuando locreamos necesario.

Además, y dado que vamos a jugar con las coordenadas del BMP, se habilita un conjunto debotones de radio (RadioButtons) para ajustar (hacer Stretch), visualizar al tamaño real (no hacerStretch) o personalizar el tamaño del BMP. Si lo que se pretende es definir las dimensiones de laimagen, es lógico pensar en dos controles de edición donde indicar el ancho y el alto (estoscontroles deberán estar habilitados cuando queramos personalizar, y deshabilitados en el restode los casos.

Situaciones

Antes de pasar a examinar las características de cada uno de los elementos que completarán elejemplo, paso a enumerar los casos que nos vamos a encontrar, según la operación que sedesee hacer con el BMP y el tamaño original de éste.

- Para BMPs más pequeños que el recinto de visualización. Tanto ajustar como mostrar en

3Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

tamaño original no forzarán a que haya barras de desplazamiento que permitan recorrer elBMP en su totalidad, pues, al ser más pequeño, lo contemplaremos completo. Sin embargo,cuando se fijan las dimensiones, sí habrá que tener en cuenta que el resultado final excedadel habitáculo predefinido.

- Para BMPs más grandes que el recinto de visualización. En este caso ajustar no fuerza elredimensionamiento, pero sí la vista a tamaño original, que forzará a que existan barras dedesplazamiento para recorrer el BMP en su totalidad. Por otra parte, la lógica a seguircuando se trata de fijar las dimensiones es idéntica a la anterior.

De acuerdo a lo expuesto, las situaciones en las que se puede encontrar un BMP es alguna delas de la tabla 1. En dicha tabla también puede verse en qué manera afecta a los estados dedos de las propiedades más importantes de una imagen, la propiedad Stretch y la propiedadAutoSize.

Tipo Ajustado Tanaño Original Personalizado Stretch AutoSizeBMP pequeño Sí No No true falseBMP pequeño No Sí No false falseBMP pequeño No No Sí false trueBMP grande Sí No No true falseBMP grande No Sí No false trueBMP grande No No Sí false trueTabla 1: Estado de Stretch y AutoSize según aspecto de BMP.

Separación de bienes

Una vez vistas las situaciones posibles, tomemos el ejemplo del número anterior y dividámosloen las dos partes, las comentadas más arriba. Para ello:

1.- Se recupera el proyecto del ejemplo del mes anterior.

2.- Se salva con otro nombre para no perder la información que éste poseía (nunca se sabehasta dónde habremos de dar marcha atrás).

3.- Se crea un nuevo form que, a todos los efecto, se trata de la nueva ventana que seráinvocada desde la principal. Para ello se elige el icono de la barra de herramientasdestinada a tal fin, o se procede a su creación mediante el ítem New del menú File. Estaúltima operación nos obliga a elegir entre los distintos tipos de ventanas disponibles, de lascuales se elegirá la más sencilla de todas, la de tipo form, tal y como se indica en la figura3.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Figura 3: Creación de una nueva ventana

Una vez en posesión de dos ventanas, una con todos los controles y otra con ninguno,basta con mandar cosas de un sitio a otro. La forma más fácil consiste en visualizar elcódigo asociado a las ventanas actuales. En él se puede observar que cada una de lasventanas tiene una carpeta en la que se almacena su propio código, cuando pinchemossobre la carpeta Unit1 estaremos accediendo al form1, cuando lo hagamos sobre lacarpeta Unit2 estaremos accediendo al form2, y así sucesivamente para el resto deventanas y sus unidades asociadas a ellas. En las figuras 4 y 5 se ilustra lo que quierodecir,. En la parte de la izquierda de cada figura se ve el editor de código fuente, donde unade las unidades (Units) se llaman LB4 y la otra VerBMP; dependiendo de que una estépinchada o lo esté la otra se visualiza, en la parte de la derecha, su ventana asociada.

Figura 4: Ventana principal (form) y código asociado LB4 (unit)

5Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Figura 5: Ventana para la visualización del BMP (form) y código asociado VerBMP (unit)

Una vez en esta situación bastará con ir a la fuente desde donde queremos copiar (o cortar eneste caso), que es LB4, marcar los controles deseados y llevarlos al portapapeles; cambiamosde ventana, activando aquélla que va a recibir los controles almacenados en el portapapeles ynos los traemos desde allí.

Nota: para cambiar entre código fuente basta con pinchar en la carpeta deseada. Para visualizarla ventana asociada al código elegido se hace mediante el oportuno icono de la barra deherramientas, la opción Toggle Form/Unit del menú View o la pulsación de la tecla [F12].

En este punto ya nos encontramos con dos ventanas, cada una con la separación que hemosvenido predicando. Ahora basta dejarla bonita redimensionando los controles al tamaño quemejor nos parezca y consiguiendo un aspecto similar al que ofrecen las figuras 1 y 2.

Nuevos controles

Como ya se ha adelantado, se han introducido dos controles de lo que aún no hemos visto nada,los RadioButton Groups y los Controles de Edición con Máscara.

Un RadioButton Group es un conjunto de RadioButtons con la característica de ser, dentro delgrupo, mutuamente excluyentes entre sí. Existe en la carpeta Standard la posibilidad de elegirRadioButton individuales, pero cuando queremos agruparlos de manera lógica, lo más eficaz esrecurrir a este otro tipo de agrupación. El RadioButton Group se encuentra en la carpetaStandard y los iconos que los representan (no seleccionado y seleccionado) son los de la figura6.

Figura 6: Iconos para RadioButton Group

6Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Propiedades de los RadioButton Group

Como viene siendo habitual, hacemos un rápido recorrido por eventos y propiedades no vistospor el momento, y que están íntimamente ligados a los RadioButton Groups.

Caption. Es la propiedad de los literales. Con ella se pone un encabezamiento que indicaqué tipo de asociación se ha hecho para el RadioButton Group. En nuestro caso se hapuesto la descripción: Presentación

Columns. Cantidad de columnas dentro de la agrupación. Dado que podemos especificarun número arbitrario de elementos, esta propiedad fija la forma en que se van a presentaren pantalla. Lo habitual es que haya una sola columna y que los elementos se presentencomo en la figura 2, pero si hubiésemos querido que su presentación fuera horizontalhubiera valido con asignar un 3 al dato Column (nuestro ejemplo tiene tres RadioButtondentro del grupo).

ItemIndex. Una agrupación siempre tendrá dos o más elementos, de los cuales uno será elque esté inicialmente marcado (y desmarcado el resto). Sabiendo que estos controles serellenan siguiendo la misma lógica que en los ListBox, el primero de los RadioButtons sereconoce por el índice 0, el segundo por el 1 y así sucesivamente. En nuestro ejemplopartimos de que queremos ver el BMP a tamaño real (segunda posición) por lo que en lapropiedad ItemIndex ponemos un 1.

Items. Elementos con los que el RadioButton Group será rellenado. Este dato alberga unobjeto de la clase TString por lo que disponemos de la posibilidad de ir añadiendo en formade lista la relación de ítems que se quieren mostrar.

Propiedades de los Controles de Edición

El Control MaskEdit se encuentra en la carpeta Adittional y los iconos que los representan (noseleccionado y seleccionado) son los de la figura 7.

Figura 7: Iconos para Controles de Edición con Máscara

Los controles de edición con máscara (MaskEdit) también disponen de sus particularidades:

AutoSelect. Propiedad que permite seleccionar automáticamente el contenido del control deedición cada vez que éste recibe el foco. En nuestro ejemplo se ha cambiado a false, puesel valor por defecto es true.

AutoSize. Propiedad que permite redimensionar el control de edición a medida que sucontenido se va haciendo mayor.

CharCase. Tipo de caracteres a utilizar. El valor ecNormal, que es la opción por defecto,escribe según cómo se tenga el bloqueo de mayúsculas, ecUpperCase en mayúculas yecLowerCase minúsculas.

EditMask. Tipo de máscara que se va a utilizar. Son muchas las máscaras disponibles, y sumisión es forzar la escritura como al programador le convenga. Por ejemplo, se podríaforzar a escribir siempre números, o con un formato genérico para números de teléfono o

7Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

para NIF. El que nosotros utilizaremos será el de los números, impidiendo así que seescriba otra cosa que no sean dígitos (además, se impide que el número exceda de trescifras). Para eso hay que asociar a EditMask la máscara 999 y a MaxLenght el valor 3. Lasposibles máscaras se definen o se eligen en la ventana de la figura 8, la cual aparece traspinchar en el botón de puntos suspensivos asociado a la propiedad EditMask.

Figura 8: Ventana para la definición de máscaras de edición

MaxLenght. Longitud máxima permitida a la hora de teclear.

Text. Contenido inicial del control. Nuestro ejemplo parte de un valor vacío para loscontroles de edición X e Y.

PasswordChar. Carácter que se utiliza para encriptar lo que se escribe. En nuestro caso nose quiere encriptar nada, por lo que se deja a #0, valor por defecto.

Ejemplo de visualización de BitMaps

Los pasos a seguir para obtener las ventanas del ejemplo han sido:

1.- Hacer la separación de bienes antedicha

2.- Instanciar la segunda ventana una vez que se pulse el botón de Ver o se haga doble clicsobre el ListBox (habremos de asociar el código del fuente 1 tanto al evento OnClick delbotón como al OnDblClick del ListBox).

// --- Fuente 1 ----------------------------------------------------------

procedure TForm1.VerBmp(Sender: TObject);var nPos: integer;begin nPos := ListBox2.ItemIndex; DlgVerBMP.Show; DlgVerBMP.cLista := ListBox2.Items; DlgVerBMP.nPosicion := nPos; DlgVerBMP.Imagen.Picture.LoadFromFile( ListBox2.Items[nPos] );end;

8Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Como se puede apreciar, se hace una llamada al método Show, encargado de visualizar laventana no modal, sobre una variable llamada DlgVerBMP.

DlgVerBMP.Show;

La variable DlgVerBMP es el nombre que se le dio a la ventana VerBMP en su propiedadName, y que Delphi se ha encargado de especificar como variable a través del códigogenerado automáticamente (ver fuente 2)

// --- Fuente 2 ----------------------------------------------------------

. . .type TDlgVerBMP = class(TForm) Cerrar: TBitBtn; RadioGroup1: TRadioGroup; ScrollBox1: TScrollBox; Imagen: TImage; GBCoord: TGroupBox; X: TMaskEdit; Y: TMaskEdit; LabelX: TLabel; LabelY: TLabel; procedure Presenta(Sender: TObject); procedure Activar(nPos: Integer); procedure Tamano(Sender: TObject); procedure Recordar(Sender: TObject); procedure CambiaBMP(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

var DlgVerBMP: TDlgVerBMP;. . .

A continuación se asigna a la variable cLista (inventada por nosotros) la totalidad de losítems del ListBox de archivos y la posición que ocupa el elegido (se verá más adelante porqué), para terminar por cargar la imagen (mediante el método LoadFromFile) en el recintodestinado a tal fin.

3.- Importante . Añadir en el apartado uses de LB4 la unidad que contiene la segunda ventana.Si no se hace esto el compilador no podrá unir la información de una unidad con la de otra,dando el consecuente error y no pudiendo realizar la llamada a una ventana desde otra. Endefinitiva, en el apartado uses hay que añadir, a la lista que Delphi incorporaautomáticamente, el literal VerBMP.

uses Windows, Messages,. . . VerBMP;

4.- Sopesando los pasos dados, se puede asegurar que lo menos intuitivo es lo de especificaren el apartado uses la unidad que se quiere utilizar, pero explicado el concepto, parece

9Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

claro que una aplicación con multitud de ventanas, no tiene por qué invocar a todas desdetodos los sitios, de ahí que haya que fijarlo ese sitio y esas ventanas allí donde senecesiten, todo ello de acuerdo al diseño dado a la aplicación y de las necesidades aresolver.

Una vez que vemos que todo funciona correctamente hasta el paso 3 (se puede compilar yejecutar el ejemplo), podemos empezar a utilizar las nuevas funcionalidades conseguidascon los controles estudiados más arriba, de ahí que ubiquemos un RadioButton Group y unMaskEdit.

5.- Determinamos qué hacer cuando se elija alguno de los RadioButton, de acuerdo al eventoOnClick de este tipo de controles. En el fuente 3 se ve el código usado para ajustar laimagen, mostrarla a tamaño original o personalizar sus dimensiones.

// --- Fuente 3 ----------------------------------------------------------

procedure TDlgVerBMP.Presenta(Sender: TObject);var nPos: Integer;begin nPos := RadioGroup1.ItemIndex; Activar( nPos ); case nPos of 0 : Imagen.Stretch := True; 1 : Imagen.Stretch := False; 2 : X.SetFocus(); end;

end;

Lo primero que hace el procedimiento es provocar una llamada a Activar, pasándole comoparámetro el ítem que ha sido pulsado en el RadioButton Group. Después se discierne, enuna estructura case, cómo obrar para el ítem 1, 2 ó 3 (valores 0, 1 y 2, respectivamente,en nPos). Para el ítem 1 se pone el dato Stretch a true, para el ítem 2 se modifica a false ypara el ítem 3 se le da el foco al control de edición X.

Pero pasemos a comprender qué hace el procedimiento Activar. Siempre que no se estépersonalizando el tamaño final del BMP (ítems 1 y 2), los controles de edición X e Y, losliterales y la caja que los abarca deberían estar inhabilitados, pasando a estar disponiblessolamente cuando se elija el ítem 3. Al tratarse de un código que se repite para cadaoperación se ha optado por aislarlo en un procedimiento específico. En el fuente 4, y deacuerdo al valor recibido como parámetro, se activan o desactivan dichos controles.

Cuando nPos sea 0 ó 1, el dato Enabled debería estar a false, por lo que se podríaensayar algo como lo que sigue:

if (nPos = 0) or (nPos = 1) thenbegin

GBCoord.Enabled := false;LabelX.Enabled := false;LabelY.Enabled := false;X.Enabled := false;Y.Enabled := false;

endelse

beginGBCoord.Enabled := false;

10Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

LabelX.Enabled := false;LabelY.Enabled := false;X.Enabled := false;Y.Enabled := false;

end;

Pero resulta algo excesivo cuando se puede recoger el valor false o true en una variable yser esa variable la que se encargue de modificar el contenido del dato Enabled.

if (nPos = 0) or (nPos = 1) thenlValor := falseelse

lValor := true;GBCoord.Enabled := lValor;LabelX.Enabled := lValor;LabelY.Enabled := lValor;X.Enabled := lValor;Y.Enabled := lValor;

o, como se hace en el fuente 4, obviar la estructura if y asignar directamente a la variable.

// --- Fuente 4 ----------------------------------------------------------

procedure TDlgVerBMP.Activar( nPos: Integer );var lValor : Boolean;begin lValor := not ((nPos = 0) or (nPos = 1)); GBCoord.Enabled := lValor; LabelX.Enabled := lValor; LabelY.Enabled := lValor; X.Enabled := lValor; Y.Enabled := lValor; if not lValor then begin if nPos = 1 then Imagen.AutoSize := True else Imagen.AutoSize := False; Imagen.Width := XOrig; Imagen.Height := YOrig; end;end;

Hasta aquí tenemos asegurado la activación y la desactivación de controles de acuerdo alRadioButton elegido, así como el comportamiento de Stretch y AutoSize en los distintoscasos. Aún nos queda tomar como buenos los datos que se introduzcan en los controles deedición y asignárselos al ancho y alto de la imagen.

6.- Asignación de los valores tecleados. Ante tal tesitura podemos elegir entre dos caminos. Elprimero consiste en introducir los valores y, una vez que se consideren aceptables pulsar unbotón que actualice lo que se acaba de teclear. El segundo, más funcional, consiste enactualizar el alto y ancho a medida que las nuevas dimensiones están siendo introducidas enlos controles de edición.

Por ejemplo, si queremos asignar unos valores de 300x459, a medida que escribamos se

11Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

irá asignando un valor de 3, luego un 30 y luego un 300 para el ancho, y un valor de 4, de45 y de 459 al alto. Eso, efectivamente, es más funcional, pero puede darse el caso que ungráfico tenga unas dimensiones de 0x0, lo cual no es representable. Por lo tanto, y parallevar a cabo este redimensionamiento dinámico, protegeremos los valores muy pequeños ysólo empezaremos a mostrar la imagen a partir de un rango mínimo (que en el ejemplo delfuente 5 se ha fijado en 20).

El fuente 5, para que sea invocado a cada pulsación en los controles de edición, habrá deespecificarse en el evento OnChange de ambos EditMask.

// --- Fuente 5 ----------------------------------------------------------

procedure TDlgVerBMP.Tamano(Sender: TObject);var nValorX, nValorY : Integer; nCod : Integer;begin Val( Trim(X.Text), nValorX, nCod ); Val( Trim(Y.Text), nValorY, nCod ); if (nValorX > 20) and (nValorY > 20) then begin Imagen.Stretch := True; Imagen.Autosize := True; Imagen.Width := nValorX; Imagen.Height := nValorY; end;

end;

El contenido de un control de edición es siempre de tipo carácter, y se recoge por mediodel dato Text de dicho control. Por lo tanto, para saber qué dato tiene almacenado elcontrol y así poder fijar la coordenada X (ancho) diremos:

Trim(X.Text)

Donde Trim() es la función que se encarga de quitar los blancos sobrantes

Una vez obtenida la cadena de caracteres que contiene el valor tecleado la convertimos aun número que podemos manipular, por ejemplo, para comparación, sumas, restas, etc.Esta operación se hace mediante el uso del procedimiento Val():

Val( cCadena, nValor, nCod )

Donde cCadena es la cadena alfanumérica a convertir, nValor una variable numéricapasada por referencia y donde se recoge el valor convertido y nCod un código de error quese rellena cuando la operación de conversión ha fallado.

De acuerdo con los valores de los controles X e Y, modificamos el alto y el ancho (datosWidth y Height, respectivamente) y alteramos AutoSize para que pase a valer true, perosólo cuando ambos controles superen las 20 unidades; por lo que la imagen más pequeñaserá de 21x21 pixeles y la más grande de 999x999 pixeles.

Pero esta asignación a los datos Width y Height hace que la imagen pase a tener,

12Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

efectivamente, esos nuevos valores, lo que hará que si queremos ver la imagen tal y comoes en realidad no podamos dar marcha atrás. La solución pasa por recordar en algunavariable los valores originales, y restaurarlos cuando hagan falta.

Eso se ha hecho en las variable XOrig e YOrig. Alterando la definición de la clase podemosacceder o añadir datos públicos a esa clase (podía haberse hecho en los privados, puescualquier referencia a ellos se hace desde métodos de la propia clase). Como se ve en elfuente 6, se ha hecho necesario guardar, además, una copia de los ítems del ListBox dearchivos y del ítem elegido (estos dos sí han de ser públicos, pues se accede a ellos desdefuera de procedimientos propios de la clase).

// --- Fuente 6 ----------------------------------------------------------

type TDlgVerBMP = class(TForm) Cerrar: TBitBtn; RadioGroup1: TRadioGroup; ScrollBox1: TScrollBox; Imagen: TImage; GBCoord: TGroupBox; X: TMaskEdit; Y: TMaskEdit; LabelX: TLabel; LabelY: TLabel; procedure Presenta(Sender: TObject); procedure Activar(nPos: Integer); procedure Tamano(Sender: TObject); procedure Recordar(Sender: TObject); procedure CambiaBMP(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); private { Private declarations } public { Public declarations } XOrig: Integer; YOrig: Integer; cLista: TStrings; nPosicion: Integer; end;

Ya se dispone de un lugar donde almacenar los valores que, en un determinado momento,puede resultar ventajoso tenerlos; ahora sólo falta saber cuándo y/o dónde asignarle elvalor. Pueden ser varios los sitios donde hacerlo, aunque sí parece lógico que la asignaciónse haga una sola vez. Para resolver esta cuestión se va a recurrir a los eventoscontemplados para la ventana. Los eventos que se pueden interceptar son los demovimiento, redimensionado, activación, desactivación de ventana, etc. El que en definitivanos va a valer para nuestra operativa será el de creación de la ventana, identificado conOnCreate.

El sencillo código del fuente 7 será el que se asocie a la creación de la segunda ventana.

// --- Fuente 7 ----------------------------------------------------------

procedure TDlgVerBMP.Recordar(Sender: TObject);begin XOrig := Imagen.Width; YOrig := Imagen.Height

13Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

end;

Es decir, al ancho original (XOrig) le asignamos el ancho del control (Witdh) tal y como seve al aparecer la segunda ventana, y en el alto original (YOrig) almacenamos el alto delcontrol (Height).

7.- Para terminar, y siempre con el afán de dar más funcionalidad al ejemplo que nos ocupa, seha implementado una rutina que nos permite recorrer todos los archivos que esténalmacenados en el ListBox de archivos.

De nuevo nos encontramos ante dos posibles soluciones al problema. Por un lado está laubicación de dos botones que permitan avanzar y retroceder a través de la lista, por otro sepueden utilizar el botón derecho e izquierdo del ratón para ir uno hacia adelante o uno haciaatrás, respectivamente. Este último, aparte de no saturar la ventana con controles, nospermite introducirnos en una nueva operativa, en la que va a entrar en juego las pulsacionesen los botones del ratón.

Como siempre, habrá que determinar cuál es el evento que queremos interceptar, y luegoprogramarlo. Las pulsaciones del ratón que queremos procesar, a priori, parece lógico quesean las que se efectúan dentro de la imagen, ignorando aquéllas que se hagan encualquier otro control. Si ahora consultásemos los posibles eventos relacionados con elratón, aparecería una lista como la de abajo.

OnClickOnDblClickOnMouseDownOnMouseMoveOnMouseUp

Los dos primeros (OnClick y OnDblClick) los descartamos, pues sólo controlan que sepulse y se suelte el botón principal (una o dos veces). OnMouseMove está pensado para lagestión de cualquier movimiento del ratón. Por último OnMouseUp y OnMouseDown nosinforman que un determinado botón se soltó o se pulsó (respectivamente).

Para comprender cómo se puede advertir que se utilizó el botón principal o el auxiliar, bastacon echar un vistazo al código del fuente 8, generado automáticamente por Delphi

// --- Fuente 8 ----------------------------------------------------------

procedure TDlgVerBMP.CambiaBMP( Sender: TObject;Button: TMouseButton;Shift: TShiftState;X, Y: Integer

);

Comentemos los parámetros recibidos de manera automática por el procedimiento. Comosiempre, Sender es el control que ha llamado al procedimiento y se utiliza cuando dichoprocedimiento es común a más de un control. La variable Button es la encargada dealmacenar el botón pulsado, pudiendo consultar su contenido según las constantespredefinidas (mbLeft, mbRight y mbMiddle). La variable Shift guarda información acerca dela tecla auxiliar presionada en el teclado, esto es, [Shift], [Alt] y/o [Ctrl], también de acuerdo

14Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

a constantes predefinidas (ssShift, ssAlt, ssCtrl, ssLeft, ssRight, ssMiddle y ssDouble). X eY son las coordenadas de pantalla donde se pulsó o se soltó el botón del ratón. De todasellas nos bastará con manipular el dato Button, aunque igualmente podríamos haberinvolucrado alguna pulsación de teclado, y por tanto de parámetro Shift.

En el fuente 9, una vez comprendidos los parámetros manipulables, vemos cómo hacer laoperación de avance y retroceso entre imágenes.

// --- Fuente 9 ----------------------------------------------------------

procedure TDlgVerBMP.CambiaBMP( Sender: TObject;Button: TMouseButton;Shift: TShiftState;X, Y: Integer

);begin if Button = mbLeft then if nPosicion > 0 then nPosicion := nPosicion - 1 else nPosicion := 0; if Button = mbRight then begin nPosicion := nPosicion + 1; if nPosicion >= cLista.Count then nPosicion := cLista.Count - 1; end; Imagen.Picture.LoadFromFile( cLista[ nPosicion ] );end;

Como primera medida se comprueba, por medio de la variable Button, si se pulsó el botónizquierdo del ratón. Si no, se comprueba si ha sido el botón derecho:

if Button = mbLeft then. . .if Button = mbRight then. . .

Sea como fuere, se manipula el dato nPosicion (una de las variables públicas de la clase -ver fuente 5-) decrementando o incrementando su valor, teniendo cuidado de no sobrepasarel límite inferior (0) ni el superior (número total de elementos del ListBox de archivos menos1). Sabiendo ya el nuevo valor de nPosicion, capturamos el elemento que se correspondeen el ListBox y que, previamente, hemos almacenado en cLista.

cLista[ nPosicion ]

Conocido ya el elemento que nos interesa lo cargamos, como ya hemos hecho antes, pormedio del método LoadFromFile() del control Imagen.

Ese será el código que asociaremos al evento OnMouseUp, pero hay que decir queigualmente se lo podemos asociar a OnMouseDown.

Un saludo.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Delphi paso a paso (VII): Aplicaciones MDIPor Vladimir Algara

En este artículo se van a enumerar los pasos que deben seguirse para proyectar una sencillaaplicación MDI. Bien es cierto que han sido muchas las veces en las que se ha explicaco elconcepto de aplicación MDI en contraposición al de SDI, pero me parece obligado hacer unapequeña reseña de la característica que distingue a una y a otra.

Las siglas MDI (Multiple Documents Interface, o lo que es lo mismo Interfaz de DocumentosMúltiples) identifican a las aplicaciones en las que se dispone de una ventana principal y todo unconjunto de hijas que dependen de ella; este conjunto puede ser abierto sin necesidad de ircerrando progresivamente las ventanas ya existentes. En contraposición a las citadas siglasaparecen las SDI (Single Document Interface, o lo que es lo mismo Interfaz de DocumentoSencillo) en el que aparece la presentación de una única ventana hija por vez, lo cual quiere decirque para obtener una segunda ventana hija, forzosamente, hay que cerrar la actual.

Para ilustrar el ejemplo se va a realizar una aplicación en la que se permita mostrar un númeroindefinido de ventanas, donde se podrá teclear una serie de números, con los cuales operararitméticamente; es decir, sin pretensión de otra cosa que ilustrar, se creará una rudimentariacalculadora (y la palabra rudimentaria alcanzará con este ejemplo un significado indiscutible) queserá llamada cuantas veces se quiera desde una ventana padre, encargada de distribuir la lógicadel funcionamiento a lo largo de la vida de la aplicación.

Ventana marco (o ventana padre)

Como ya se ha dicho, una aplicación MDI dispone de una ventana padre que se encarga de lagestión general del programa. Desde ella, y habitualmente por medio de los ítems de un menú,se llama cuantas veces sea necesario a su o sus ventanas hijas, las cuales tendrán uncomportamiento propio como si de programas aislados e independientes se tratara.

Como primer paso se creará la ventana marco de aplicación, a la cual dotaremos con lascaracterísticas de las ventanas MDI. Esta operación, como ya estamos acostumbrados a hacer,se realizar modificando la pertinente propiedad del inspector de objetos asociado a la ventana.

Por tanto, una vez dentro de Delphi, partamos de un formulario (Form) vacío y cambiemos laspropiedades especificadas en la tabla 1.

Propiedad Valor Original Nuevo ValorCaption Form1 Ventana MDIFormStyle fsNormal fsMDIFormName Form1 VentanaMDIWindowState wsNormal wsMaximizedTabla 1. Valores originales y nuevos para la ventana marco

La propiedad Caption se encarga de cambiar el título de la ventana, FormStyle hace que laventana no sea una de las creadas por defecto, sino que se comportará como despachadora deventanas, será aplicación MDI, Name hace que cualquier referencia a la ventana padre sea através del nombre asignado, ya que siempre será más representativo que el genérico Form1,Form2, etc. (siempre que se haga referencia a la ventana padre, ya sea por el código tecleadopor nosotros como por el generado automáticamente por Delphi, se realizará por medio de esteidentificativo, en nuestro caso VentanaMDI); por último, se ha modificado la propiedad

2Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

WindowState para que nada más arrancar la aplicación la ventana principal aparezcamaximizada a la totalidad de la pantalla).

Más adelante, cuando dispongamos del menú general de la aplicación, la propiedad Menu habráde ser actualizada con el diseñado para la ocasión.

Menú principal

Recordemos, someramente, cómo diseñar nuestros menús; aunque para más detalles se remiteal lector a los manuales de usuario o al artículo sobre menús en Delphi publicado en el número17 de Algoritmo .

Lo elementos que nos encontraremos, tanto para la ventana marco como para las hijas, sedescriben y enumeran más abajo.

1.- Línea donde se escribirá cada opción principal del menú.

En esta línea se especifican las distintas opciones principales que componen el cuerpocentral del menú.

2.- Líneas donde se escribirán cada una de las opciones secundarias.

En esta línea se especifican las distintas subopciones de las opciones principales.

3.- Líneas donde se escribirán cada una de las subopcines.

En esta línea se especifican las distintas subopciones anidadas.

En cualquiera de los tres casos anteriores (o cualquier otro nivel de anidación desubopciones), si quisiéramos activar un Hot-Key para cada opción, bastaría con anteponer ala letra en cuestión el símbolo &:

&Salir crearía como Hot-Key la letra S

Para crear subopciones a una opción, lo único que tendremos que hacer es pulsar el botónizquierdo del ratón sobre la opción secundaria seleccionada, eligiendo Create Submenu (opulsando [Ctrl-flecha derecha]) en el menú local asociado.

4.- Separadores entre subopciones. Para añadir este elemento visual basta con escribir un guióny pasar a la siguiente línea. Aparecerá una línea separadora.

5.- Abreviatura de teclado para acceso rápido a opciones.

Los menús, y más concretamente los ítems que componen un menú, sólo tienen un evento;aquél que sucede cuando se elige alguna de las opciones. Por eso la carpeta Events sólodispone del evento OnClick().

Si queremos especificar un evento concreto, basta con escribir en el combo asociado aOnClick() el nombre del procedimiento que queremos ejecutar cuando hagamos Clic, y pulsar[Intro]. Esto hará que el nombre de dicho evento se registre en el bloque type, y que se escriba,automáticamente, las líneas esenciales de ese método. Por ejemplo si escribimos SalirClick yasociamos una acción obtendremos el código del fuente 1.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

// --- Fuente 1 --------------------------------------------------------procedure TVentanaMDI.SalirClick(Sender: TObject);begin

Closeend;

Para confeccionar el menú de la ventana principal se han especificado los ítems, las teclasaceleradoras y los eventos asociados de la tabla 2.

Items Caption Acelerador EventoEjecutar Ejecutar

Calculadora C&alculadora Ctrl-A CalcularSeparador -Salir Salir Ctrl-Z SalidaClick

Ventanas VentanasCascada Cascada CascadaMosaico Mosaico MosaicoIconizar Iconizar Iconizar

Tabla 2: Menú de la ventana principal

El aspecto de la ventana principal, tal y como se ha diseñado, que no maximizada, debe sersimilar al que se muestra en la figura 1.

Figura 1: Aspecto de la ventana principal.

Eventos del menú

El evento de Salir de la aplicación ya se ha comentado, pasemos a comentar los que restan, queson dos. Uno, el de visualización de las ventanas instanciadas y que es similar para Cascada,Mosaico e Iconizar. Lo único que en ellos se hace es llamar al procedimiento que realiza laacción adecuada, y que respectivamente son Cascade, Tile y ArrangeIcons, tal y como se ve enel fuente 2.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

// --- Fuente 2 --------------------------------------------------------procedure TVentanaMDI.Cascada(Sender: TObject);begin Cascadeend;

procedure TVentanaMDI.Mosaico(Sender: TObject);begin Tileend;

procedure TVentanaMDI.Iconizar(Sender: TObject);begin ArrangeIconsend;

El procedimiento en que nos detendremos algo más será sobre el encargado de mostrar lacalculadora una vez invocada desde el menú principal.

Se podría ensayar una sencilla llamada al procedimiento Show genérico, pero esta operacióndeja sin efecto las especificaciones de la ventana dadas en el inspector de objetos (por ejemploposición y tamaño), siendo fijadas éstas por la ventana padre. Si lo que queremos es que laventana se muestre según los parámetros dados al diseñarla, la forma de proceder será la quese ofrece en el fuente 3

// --- Fuente 3 --------------------------------------------------------procedure TVentanaMDI.Calcular(Sender: TObject);var CalcForm: TVentanaCalculadora;begin CalcForm := TVentanaCalculadora.Create(Self)end;

Se habilita una variable para dar claridad al código y por si queremos hacerla más genérica. Acontinuación, en vez de ejecutar un Show, creamos un objeto mediante el procedimiento genéricoCreate, al cual le pasamos como parámetro la ventana marco (Self), para que se sepa quién esel propietario de la ventana recién creada (el de la calculadora).

Una vez hecho esto, la ventana aparecerá con las características dadas en el inspector deobjetos, y no como la ventana marco fija.

Para finalizar, y con las miras puestas en la conservación de lo que vamos creando, salvaremosnuestro trabajo, pero teniendo la precaución de darle un nombre distinto al que Delphi le da pordefecto. Accediendo a Save as.. del menú File le podemos llamar Padre.

Ventana hija

Son las ventanas llamadas desde la ventana principal.

Como característica esencial que las define nos encontramos con que la propiedad FormStyleestá especificada como fsMDIChild. Esto provoca que su comportamiento esté, en todomomento, supeditado a las vicisitudes de la ventana padre.

5Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Como primer paso, y como ocurrió anteriormente, se creará la ventana hija (Child), a la cualdotaremos con las características de las ventanas MDIChild. Esta operación se realiza, comoantes, modificando la pertinente propiedad del inspector de objetos asociado a la nueva ventana.

Las propiedades fijadas se especifican en la tabla 3.

Propiedad Valor Original Nuevo ValorCaption Form1 "Calculadora"FormStyle fsNormal fsMDIChildName Form1 VentanaCalculadoraTabla 3: Valores originales y nuevos para la ventana hija

Nuevamente, la propiedad Caption se encarga de cambiar el título de la ventana, FormStylehace que la ventana no sea una de las creadas por defecto, sino que se comportará como hijade la ventana marco

Más adelante, cuando dispongamos del menú de la "calculadora", la propiedad Menu habrá deser actualizada.

Menú de la calculadora

Para confeccionar el menú de la ventana principal se han especificado los ítems, las teclasaceleradoras y los eventos asociados de la tabla 4.

Items Caption Acelerador EventoNúmeros Números

0..9 &0..&9 El0, El1,.., El9Operaciones Operaciones

Suma Suma PbOperarResta Resta PbOperar- -Multiplicación Multiplicación PbOperarDivisión Division PbOperar- -Igual a Igual a PbIgualarBorrar Borrar Borrar

Tabla 4: Menú de la ventana que alberga la calculadora

El aspecto de la ventana hija debe ser similar al que se muestra en la figura 2.

6Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Figura 2: Aspecto de la ventana hija

Veamos cómo se comportan los diversos procedimientos y comentemos sus características másdestacables.

// --- Fuente 4 --------------------------------------------------------procedure TVentanaCalculadora.El0(Sender: TObject);begin Agregar( 0 )end;procedure TVentanaCalculadora.El1(Sender: TObject);begin Agregar( 1 )end;. . .. . .procedure TVentanaCalculadora.El9(Sender: TObject);begin Agregar( 9 )end;

En el fuente 4 se ve cómo en cada uno de los procedimientos asociados a los botones (y a losítems del menú) que simbolizan los números de la calculadora (El0, El1,...,El9), se llama a unprocedimiento auxiliar que recibe el número a añadir en el control de edición. Una de las muchasmejoras que puede sufrir la "calculadora" es la de impedir teclear un cero si no se ha introducidopreviamente otro número, o permitir teclear números decimales, etc., etc.

En cualquier caso el procedimiento Agregar se ve en el fuente 5.

// --- Fuente 5 --------------------------------------------------------var VentanaCalculadora: TVentanaCalculadora; Ch: string[1]; nValor: integer;

. . .

7Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

procedure TVentanaCalculadora.Agregar( nValor: byte );begin Resul.Text := Resul.Text + IntToStr(nValor)end;

. . .

initialization Ch := '+'; nValor := 0;

El procedimiento podría no haber pertenecido a la clase TVentanaCalculadora, pero en tal casono se podría haber accedido a ninguno de los controles de la ventana, y en este procedimientose altera el contenido del control de edición. Por tanto, como sí es necesario que pertenezca ala clase deberemos informar de ello en la sección type de su definición (ver fuente 6). Lavariables nValor y Ch se han declarado como públicas del módulo, y van a servir para almacenarel primer operando y el operador a usar (la inicialización de ambas variables se hace en lasección Initialization).

// --- Fuente 6 --------------------------------------------------------type TVentanaCalculadora = class(TForm) MainMenu1: TMainMenu; Nmeros1: TMenuItem; . . . . . . procedure El0(Sender: TObject); procedure El1(Sender: TObject); . . . . . . procedure El9(Sender: TObject); procedure Agregar( nValor: byte ); . . . . . .

Como se ve en la tabla 4, Suma, Resta, Multiplicación y División tienen un procedimientocomún, el que se ha llamado PBOperar, y dado que es común habrá que distinguir en su interiorsi se ha llamado por uno o por otro botón (Sender). Si a esto le sumamos que podemos realizarla operación desde los ítems del menú de la ventana hija, el Sender podrá ser otros cuatrocontroles más. En el fuente 7 se ve cómo se discierne entre uno y otro y cómo se almacena, enla variable de tipo cadena Ch, el símbolo a utilizar.

// --- Fuente 7 --------------------------------------------------------procedure TVentanaCalculadora.PBOperar(Sender: TObject);begin if (Sender=PBSuma) or (Sender=SumaMnu) then Ch := '+'; if (Sender=PBResta) or (Sender=RestaMnu) then Ch := '-'; if (Sender=PBMult) or (Sender=MultMnu) then Ch := '*'; if (Sender=PBDiv) or (Sender=DivMnu) then Ch := '/'; nValor := StrToInt( Resul.Text ); Resul.Clearend;

8Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Cuando se pincha el botón de igual (=) para obtener el resultado, o se elige el ítem del menúIgual a, se invoca al procedimiento PBIgualar, (ver fuente 8) en el cual se recurre a las dosvariables públicas nValor y Ch con los datos correctamente almacenados, además se conviertea valor numérico el dato del control de edición, se calcula convenientemente y se almacena elresultado en el control de edición.

// --- Fuente 8 --------------------------------------------------------procedure TVentanaCalculadora.PBIgualar(Sender: TObject);var nActual: Integer; nCod: Integer; nResultado: Integer;begin Val( Resul.Text, nActual, nCod ); case Ch[1] of '+': nResultado := nValor + nActual; '-': nResultado := nValor - nActual; '*': nResultado := nValor * nActual; '/': nResultado := nValor div nActual; end; Resul.Text := IntToStr( nResultado )end;

Otra mejora sería la de realizar la división con el operador /, la cual permite obtener decimales(lo cual obligaría a tipificar la variable como real, no como integer)., en vez del operador div queresulta, para el ejemplo, tan cómodo como ineficaz.

Relación entre ventanas

Una aplicación MDI con ventanas hijas instancia, de manera automática, al menos una de sushijas. Hecho esto, y acto seguido, se pueden instanciar más de la manera que se ha visto en elfuente 3. Aún así, podemos querer no instanciar ni una sola de ellas de manera automática, sinodejar esta operación al “buen criterio” del usuario. Para hacer esto hay que entrar en la opciónOptions... del menú Project, y en la carpeta Form desplazar del ListBox de la izquierda al de laderecha todas aquellas ventanas que no queramos lanzar automáticamente. En nuestro casosólo hay que trasladar la ventana VentanaCalculadora (ver figura 3), pues sólo tenemos un tipode ventana hija.

9Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Figura 3: Opciones del Proyecto

En lo que a menús se refiere, es habitual mezclar los de las ventanas hijas con el de la padre,haciendo que todos ellos convivan en la ventana marco. Para esto hay que manipular el datoGroupIndex del menú, diciendo el nivel que cada menú tiene.

Por defecto, el valor de GroupIndex es 0, lo cual haría que los menús de las ventanas hijas, quetambién tienen un GroupIndex a 0 por defecto, reemplazara al de la ventana padre. Sinembargo, si al ítem Ejecutar (ventana padre) le dejamos el GroupIndex a 0 y al ítem Ventanas(también de la ventana padre) le reservamos un 9, nos estaremos asegurando de que el ítemVentanas siempre aparecerá al final del menú. Cuando ahora se instancie la ventana hija sólo sereemplazará el ítem Ejecutar, y se compartirá el de Ventanas. También esto se puede evitarasignando al GroupIndex de todos los ítems de la ventana hija el valor 1, quedándonos elaspecto del menú como se ve en la figura 4.

10Algoritmo . La revista para el programador de sistemas de bases de datos. http:// www.eidos.es - © Grupo EIDOS

Figura 4: Menús compartidos entre ventanas

Para acabar de depurar el código, diremos que si una ventana hija de una MDI es cerrada (porcualquiera de sus formas de hacerlo), ésta no desaparece, sino que se iconiza hasta que laaplicación general finalice. Si queremos cerrar la ventana y, además, que ésta desaparezca deltodo hay que ensayar el código del fuente 9, encargado de destruir el objeto y de liberar latotalidad de la memoria.

// --- Fuente 9 --------------------------------------------------------procedure TVentanaCalculadora.Liberar(Sender: TObject; varAction:TCloseAction);begin Action := caFreeend;

En cualquier caso, y cuando se acabe la aplicación, todas las ventanas hijas desaparecerán, sinque el programador se tenga que encargar de recorrérselas cerrándolas o destruyéndolas.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (VIII)Cajas de diálogo estándar(I)

Por Vladimir Algara

En esta entrega me propongo contar algunos de los automatismos que Windows ofrece alprogramador que explota sus recursos (los de Windows), y todo ello lo desea explotar conDelphi 32.

A la hora de desarrollar una aplicación existen gran cantidad de elementos que son comunes acualesquiera otras, es por ello que distintos fabricantes hayan dirigido sus esfuerzos a laconstrucción de unos interfaces capaces de hacer más clara y accesible la forma de dominar yprogramar dichos automatismos.

Los elementos a los que me refiero, básicamente, son a las ventanas de configuración ypetición de datos de Windows (en concreto de Windows 95). Sus ventanas, comunes acualquier aplicación que corra bajo este sistema operativo, se suelen emplear para solicitardesde el archivo que va a ser leído (con su ruta correcta, extensión, atributos...), a configurarla forma en que imprimirá la impresora por defecto.

Las ventanas que vamos a encontrar son:

• Recuperación de archivos (OpenDialogf)• Salvaguarda de archivos (SaveDialog)• Elección de tipos de letra (FontDialog)• Ventana de impresión (PrintDialog)• Configuración de la impresión (SetUpPrinterDialog)• Búsqueda de cadenas de caracteres en un texto (SearchDialog)• Búsqueda y reemplazamiento de cadenas de caracteres en un texto

(SearchReplaceDialog)

Ventanas para la apertura de archivos

Todos los ejemplos que se vayan mostrando a lo largo del artículo se basan en losconocimientos que se hayan podido ir adquiriendo en entregas anteriores de esta mi sagainterminable. Los únicos elementos novedosos serán los que se han mencionado en el párrafoprecedente. Por tanto se asume como conocida gran parte de la operativa seguida en laconfección de las ventanas de ejemplo, así como las pautas a seguir para la incorporación ymodificación de código fuente.

Cualesquiera de las ventanas estándar que se usarán se encuentran ubicadas en la barra deelementos, bajo la pestaña (tabs) que lleva por nombre Standard Dialogs, en la cual apareceuna colección de iconos semejante a la que se muestra en la figura 1.

Figura 1. Iconos de la pestaña Standard Dialogs de la barra de elementos.

Como ocurriera con los menús, siempre que se desee elegir unos de estos elementoshabremos de ubicarlos en la ventana que va a hacer uso de ellos y luego ser llamadosexpresamente. A diferencia de los menús (la ventana poseía una propiedad -en su inspectorde objetos- en la que identificar el menú asociado) estas ventanas han de ser instanciadasexpresamente desde la ventana que será su propietaria.

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

En primer lugar comentaremos la funcionalidad de las ventanas para la apertura de archivos,así como sus características, dejando para después el código a ensayar para que puedaoperarse con ellas.

Figura 2. Aspecto de una ventana OpenDialog.

Como se puede apreciar en la figura 2, en una ventana OpenDialog se distinguen diversaszonas bien diferenciadas. En la parte superior el directorio en curso (en forma de árbol dedirectorios), a la derecha los iconos para la realización de las operaciones más habituales(acceder al directorio padre del actual, crear una nueva carpeta que cuelgue de la que nosencontramos, y modos de visualización de los archivos mostrados); más abajo se aprecia unarelación de archivos disponibles, estos archivos se filtran de acuerdo al criterio especificado enel combo de la parte inferior (Archivos de tipo) y que en la figura 2 se corresponde con todosaquéllos que tienen extensión BMP. Se permite realizar un segundo filtro, en el que se puedeindicar el patrón de nombre que habrá de seguir el archivo o grupo de archivos (control deedición de Nombre de archivo) por medio de los caracteres comodines por todos conocidos.

Sea como fuere, una vez obtenido el conjunto de selección se podrá optar por uno de ellos,bien a través del botón de abrir, bien a través de realizar un doble clic. Esto no provoca en sí laapertura del archivo, sino sólo su selección de entre los de una colección. Tendrá que ser laaplicación que ha llamado a la ventana de diálogo la que se tendrá que encargar,convenientemente, de abrir el archivo elegido. La ventana OpenDialog, por su parte, de loúnico que se encarga es de permitir elegir alguno de los archivos existentes en el equipo,devolviendo dicho nombre o la cadena vacía.

Especificidades desde Delphi

Para crear una ventana OpenDialog desde Delphi, partiremos de una ventana principal desdela que se permitirán unas operaciones determinadas, entre ellas la de llamar a una ventana deltipo que nos ocupa. Una vez llamada podríamos, por ejemplo, escribir en el caption de laventana el nombre del archivo elegido.

Creamos una ventana de prueba en la que ubicaremos tres elementos, dos botones (uno parasalir y otro para llamar a la ventana OpenDialog) y la propia ventana para la apertura dearchivos. La ventana principal quedaría como se ve en la figura 3

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 3. Ventana de ejemplo para la aplicación.

En el fuente 1 se ven los pasos a seguir para instanciar la ventana OpenDialog desde laventana principal de la aplicación.

// --- Fuente 1 ----------------------------------------------------procedure TForm1.LlamarOpenDialog(Sender: TObject);begin

if OpenDialog1.Execute then begin Caption := OpenDialog1.FileName; OpenDialog1.InitialDir := ''; end;end;

Por el hecho de ubicar un control de tipo OpenDialog, se crea una variable con nombreOpenDialog1 (que como todas es susceptible de ser cambiada a través de la propiedad Namedesde el Inspector de objetos), que usamos para ejecutar (y por tanto mostrar) la ventana:

if OpenDialog1.Execute then

Si conseguimos ejecutar con éxito la ventana, entonces pintamos en el Caption de la ventanapadre el nombre (con la ruta) del archivo elegido (si es que se elige alguno) y especificamoscomo Directorio inicial aquél en el que nos hayamos quedado en la última operación deelección de archivo.

Para complicar algo más el ejemplo y no quedarnos en la mera visualización del nombre delarchivo, lo que haremos será visualizar el contenido de dicho archivo en un control de edición(de otra ventana) en el cual podamos modificar su contenido en fondo y forma.

Antes de eso repasaremos algunas de las características propias de las ventanas OpenDialog,para ver cómo configurarlas. En la figura 4 se ve el aspecto típico que ofrece el Inspector deObjetos para una ventana de tipo OpenDialog.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 4. Inspector de Objetos para una ventana de tipo OpenDialog.

La explicación de sus propiedades son:

DefaultExt Extensión por defecto que aparecerá en el control de edición. Cualquiera delos comodines habituales tiene cabida en esta máscara. En nuestro ejemploespecificaremos *.TXT.

FileEditStyle Determina si el usuario se encontrará con un control de edición o un Combo ala hora de especificar el archivo que quiere recuperar. Los dos valorespermitidos son fsComboBox o fsEdit.

FileName Nombre del archivo que se va a recuperar. En nuestro ejemplo podemosespecificar PRUEBA.

Filter Perfiles de tipos de archivos que se pueden usar para agrupar, de maneralógica, un conjunto de archivos. Como se aprecia en la figura 5, se puedenindicar una serie de literales y asociar éstos a una máscara de archivos quecumplan un determinado filtro. A un literal se le puede asociar una o másmáscaras (separando con punto y coma), aunque lo habitual es sólo una.

Figura 5. Editor de filtros.

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

FilterIndex En caso de que exista una relación de filtros (especificada según lo reciéncomentado), en este punto se indica por cual de ellos empezar. En nuestroejemplo si especificamos 1, se visualizarán los Archivos de texto (y por tantolos que lleven Extensión TXT).

HistoryList Cuando la opción FileEditStyle está seleccionada como fsComboBox, en estepunto se podrá indicar qué histórico de aperturas de archivos se ha llevado acabo.

InitialDir Directorio a partir del cual se muestran los archivos del equipo.

Options Tipo de archivos que van a ser tenidos en cuenta. Según los atributos y lascaracterísticas de los archivos nos podemos encontrar los casos que semuestran en la tabla 1.

Valor SignificadoOfAllowMultiSelect Cuando es True permite selecciona más de un

archivo en el ListBox.OfCreatePrompt Cuando es True aparece una caja de diálogo

indicando la elección de un archivo inexistente. Si secontesta que Sí, el archivo se crea nuevo.

OfExtensionDifferent Se activa cuando el archivo devuelto no se ciñe a lasespecificaciones dadas en la propiedad DefaultExt.

OfFileMustExist Si es True obliga a que el archivo exista.OfHideReadOnly Si es True oculta los archivos con atributos Read

Only.OfNoChangeDir Si es True impide cambiar de directorio.OfNoReadOnlyReturn Si es True aparece un mensaje si el usuario eligió un

archivo marcado como Read Only.OfNoTestFileCreate Se invoca cuando se quiere crear un archivo en una

red y éste está protegido.OfNoValidate Si es True no se verifica que lo caracteres

introducidos para el nombre del archivo sean válidos.OfOverwritePrompt Si es True se indica, mediante una caja, que el

archivo será sobreescrito.OfReadOnly Si es True el check de archivos de Sólo lectura de la

caja se marca.OfPathMustExist Si es True sólo se podrán especificar rutas válidas.OfShareAware Si es True se ignora cualquier error ocurrido en red.OfShowHelp Si es True se mostrará un botón de ayuda en la caja

de diálogo.Tabla 1. Opciones para los archivos.

Title Literal de la ventana (caption)

Una vez dadas estas características a la ventana y ejecutado el programa de ejemplo,aparecerá la ventana de la figura 6. Pero antes de lanzarlo, añadamos la funcionalidadcomentada más arriba. Una vez que se elija el archivo, visualizaremos su contenido en uncontrol de edición multilínea, que pertenecerá a una ventana independiente. Para ir ganandoterreno añadiremos a esta ventana tres botones, uno para salir de ella, otro para almacenar loshipotéticos cambios realizados en el control de edición, y por tanto en el archivo, y otro quenos va a permitir acceder a las peculiaridades de otra caja de diálogo estándar, la de elecciónde tipos de letra.

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 6. Aspecto de la ventana de diálogo, para las características dadas

El código necesario concerniente a la ventana OpenDialog y por lo tanto para poner enfuncionamiento este ejemplo se ve en el fuente 2. Aparece en negrilla el código que ha habidoque introducir manualmente.

// --- Fuente 2 ---------------------------------------------------------unit OD;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Buttons, StdCtrls, Ventana;

type TForm1 = class(TForm) OpenDialog1: TOpenDialog; Button1: TButton; BitBtn1: TBitBtn; Label1: TLabel; procedure LlamarOpenDialog(Sender: TObject); private { Private declarations } public { Public declarations } end;

var Form1: TForm1; oVentana: TForm2;

implementation

{$R *.DFM}

procedure TForm1.LlamarOpenDialog(Sender: TObject);begin

if OpenDialog1.Execute then begin Caption := OpenDialog1.FileName; OpenDialog1.InitialDir := ''; oVentana := TForm2.Create( Form1 );

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

oVentana.Show; oVentana.Memo1.Lines.LoadFromFile( OpenDialog1.FileName ); end;end;

end.

Como se puede apreciar, se ha añadido a la porción de código que se vio en el fuente 1 treslíneas más, las encargadas de instanciar la ventana que albergará el contenido del archivoelegido. Expliquémoslo un poco.

Se sigue utilizando Execute para hacer la llamada estándar a la ventana OpenDialog, tambiénse sigue sustituyendo el Caption de la ventana padre con el nombre del archivo elegido asícomo también permanece la especificación del directorio inicial de lectura; pero, además, secrea una nueva instanciación, la de la ventana encargada de mostrar el contenido del archivoseleccionado:

oVentana := TForm2.Create( Form1 );

TForm2 es el nombre de la ventana creada, Create es el método encargado de crearla yForm1 es el parámetro pasado a Create para que TForm2 tenga información de quién ha sidola ventana que la ha creado. Una vez creada, la mostramos por pantalla, como siempre através del método Show

oVentana.Show;

Como lo primero que queremos hacer es visualizar el contenido del archivo seleccionado,recurrimos al método LoadFromFile, el cual recibe como parámetro el nombre del archivodesde el que queremos recuperar la información (que ya hemos visto que lo tenemosalmacenado en OpenDialog.FileName). El método LoadFromFile se aplica al control de edición(Memo1) de la ventana recién creada, y más concretamente a su dato Lines, según se ve acontinuación:

oVentana.Memo1.Lines.LoadFromFile( OpenDialog1.FileName );

Con esto se consigue que el archivo elegido se muestre en la ventana, pero no se ha tenido laprecaución de comprobar que el tamaño del texto no supere el máximo permitido en el controlde edición; tampoco se ha corroborado que se ha elegido un archivo entre los disponibles,aunque esto no produce error alguno no estaría de más el mostrar un mensaje indicativo oimpedir salir de la ventana sin haber realizado la elección oportuna. Dado que son operacionesexentas de complejidad y no aportan nada nuevo, pasaremos a los elementos que realmenteinteresan.

Ventanas para la salvaguarda de archivos

Como ha ocurrido con la ventana OpenDialog, siempre que se desee elegir una ventanaSaveDialog la habremos de ubicar en la ventana que va a hacer uso de ella y, luego, se llamaexpresamente.

Como hicimos antes, comentaremos la funcionalidad de las ventanas para la salvaguarda dearchivos, así como sus características, dejando para después el código a ensayar para quepueda operarse con ella.

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 7. Aspecto de una ventana SaveDialog.

Como se puede apreciar en la figura 7, en una ventana SaveDialog se distinguen las mismaszonas diferenciadas que se distinguían en la ventana OpenDialog. En la parte superior eldirectorio en curso (en forma de árbol de directorios), a la derecha los iconos para larealización de las operaciones más habituales, etc.

Sea como fuere, una vez decidido el nombre que se le va a dar al archivo se podrá optar porrealizar la salvaguarda, bien a través del botón de guardar, bien a través de realizar un dobleclic. Esto no provoca en sí la salvaguarda del archivo, sino sólo su selección de entre los deuna colección. Tendrá que ser la aplicación que ha llamado a la ventana de diálogo la que setendrá que encargar, convenientemente, de guardar el archivo con el nombre elegido. Laventana SaveDialog, por su parte, de lo único que se encarga es de permitir elegir un nombreen alguno de los directorios y/o unidades existentes en el equipo, devolviendo dicho nombre ola cadena vacía.

Especificidades desde Delphi

Para crear una ventana SaveDialog desde Delphi, partiremos de la ventana antes generada,desde la que se permite la operación de guardar los cambios hechos en el control de edición,el cambio de fuentes a usar o su cancelación.

Una vez creada la ventana de prueba quedará como la de la figura 8.

9Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 8. Ventana de ejemplo para la aplicación.

Para nuestro ejemplo habíamos decidido guardar el contenido del control de edición multilíneaen forma de archivo, lo cual se hará al pulsar el botón guardar.

Antes de eso repasaremos algunas de las características propias de las ventanas SaveDialog,para ver cómo configurarlas. La explicación de sus propiedades son similares a las que sedieron para OpenDialog, de ahí que sólo se comenten las cambiantes respecto al anterior.

DefaultExt Extensión por defecto que aparecerá en el control de edición. En nuestroejemplo especificaremos *.TXT.

FileName Nombre del archivo que se va a salvar. En nuestro ejemplo podemosespecificar DOC.

Title Literal de la ventana (caption). En nuestro ejemplo pondremos "Guardarcambios"

El código necesario concerniente a la ventana SaveDialog y por lo tanto para poner enfuncionamiento este ejemplo se ve en el fuente 3. Aparece en negrilla el código que ha habidoque introducir manualmente.

// --- Fuente 3 ---------------------------------------------------------unit Ventana;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons;

type TForm2 = class(TForm) BitBtn1: TBitBtn; BitBtn2: TBitBtn; Memo1: TMemo; SaveDialog1: TSaveDialog; FontDialog1: TFontDialog; BitBtn3: TBitBtn; procedure GuardarArchivo(Sender: TObject); private { Private declarations }

10Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

public { Public declarations } end;

var Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.GuardarArchivo(Sender: TObject);begin

if SaveDialog1.Execute then begin SaveDialog1.InitialDir := ''; Memo1.Lines.SaveToFile( SaveDialog1.FileName ); end;

end;

end.

Como se puede apreciar se ha añadido un código similar al que se usó en el fuente 2 para laventana OpenDialog, el código encargado de almacenar el texto en forma de archivo.

Se sigue utilizando Execute para hacer la llamada estándar a la ventana SaveDialog, tambiénse sigue especificando el directorio inicial de escritura; pero, además, se guarda el contenidodel control de edición (Memo1.Lines) en forma de archivo, mediante el método SaveToFile,que recibe como parámetro el nombre del archivo deseado. El nombre de este archivo lotenemos guardado en el dato FileName de la ventana SaveDialog.

Memo1.Lines.SaveToFile( SaveDialog1.FileName );

Tipos de letra ( FontDialog )

Basándonos en que ya disponemos de un sitio donde se está visualizando un texto, vamos allamar a la ventana estándar FontDialog para cambiar el tipo de letra con el que se muestra

Antes de nada repasaremos las características propias de las ventanas FontDialog, para vercómo configurarlas. La explicación de sus propiedades son:

Device Dispositivo por el que se va a obtener el tipo de letra elegido. En nuestroejemplo especificaremos fdScreen, para verlo por pantalla, aunque podríamoshaber especificado fdPrinter (impresora) o ambos (fdBoth).

Font Determina el tipo de fuente a usar, definiendo cada una de sus características,como son tamaño de la letra, color, ancho, si será negrita o no, etc.

MaxFontSize Es el máximo valor permitido en lo que a tamaño de letra se refiere, medianteeste valor podemos limitar superiormente los tamaños disponibles.

MinFontSize Es el mínimo valor permitido en lo que a tamaño de letra se refiere.

Options En la tabla 2 se muestran los valores que modifican las características de lasfuentes elegidas.

11Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Valor SignificadofdAnsiOnly Si es True sólo se pueden seleccionar fuentes que estén

marcadas como ANSI Windows.fdEffects Si es True aparecerá en la ventana de diálogo la

posibilidad de dotar de efectos a las fuentes.fdFixedPitchOnly Si es True sólo se permiten fuentes monoespaciadas.fdForceFontExist Si es True aparece una ventana informativa si se elige

una fuente que no está presente en el combo mostrado.fdLimitSize Si es True los valores MaxFontSize y MinFontSize pasan

a tener sentido.fdNoFaceSel Si es True no aparece ninguna fuente por defecto en la

ventana.fdNoOEMFonts Si es True sólo se pueden seleccionar fuentes vectoriales

de Windows.fdScalableOnly Si es True sólo se pueden seleccionar fuentes escalares

de Windows.fdNoSimulations Si es True sólo se pueden seleccionar fuentes GDI de

Windows.fdNoSizeSel Si es True no aparece ningún tamaño por defecto en la

ventana.fdNoStyleSel Si es True no aparece ningún estilo por defecto en la

ventana.fdNoVectorFonts Igual que fdNoOEMFonts.fdShowHelp Si es True aparecerá un botón de ayuda (Help) en la

ventana.fdTrueTypeOnly Si es True sólo se permiten fuentes True Type.fdWysiwyg Si es True sólo se permiten fuentes que aparezcan con el

mismo aspecto en la impresora y en la pantalla.Tabla 2. Valores para las fuentes.

El código necesario concerniente a la ventana FontDialog y por lo tanto para poner enfuncionamiento este ejemplo se ve en el fuente 4.

// --- Fuente 4 ---------------------------------------------------------procedure TForm2.CambiarFuente(Sender: TObject);begin FontDialog1.Execute; Memo1.Font := FontDialog1.Font;end;

Como se puede apreciar, se ha tenido que añadir un nuevo procedimiento a la porción decódigo que se vio en el fuente 3. Es el encargado de modificar el tipo de letra con que sevisualiza el contenido del control de edición.

De nuevo, se sigue utilizando Execute para hacer la llamada estándar a la ventana FontDialog,pero, esta vez, se altera el dato Font del control de edición multilínea, asignándole aquél quese haya elegido en FontDialog1, la de la ventana encargada de elegir entre los distintos tiposde letras disponibles.

El resultado de la recuperación de un archivo y su alteración en el modo de visualizarlo sepuede ver en la figura 9. En ella se ha elegido un tipo de letra Arial, de paso 10 y negrita.

12Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 9. Cambio de Font en el control de edición.

Bueno, hasta aquí tres de las ventanas de diálogo estándar que brinda Windows. Para lapróxima entrega abordaremos el resto, siguiendo como base del ejemplo el que aquí se havenido desarrollando.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (IX):Cajas de diálogo estándar(y II)

Por Vladimir Algara

Esta entrega se concibe como la continuación de la ofrecida en el número anterior deAlgoritmo , en la que, como en aquélla, me propongo contar todos o casi todos losautomatismos que Windows ofrece al programador respecto a las cajas de diálogo estándar, ytodo ello, nuevamente, con Delphi 32.

Vimos entonces una serie de cajas de diálogo encaminadas a facilitar la vida a todo aquél quese quisiera valer de sus ventajas, dejando para más adelante las que vamos a ver hoy.

Las ventanas que nos quedan por repasar son:

• Ventana de impresión (PrintDialog)• Configuración de la impresión (SetUpPrinterDialog)• Búsqueda de cadenas de caracteres en un texto (SearchDialog)• Búsqueda y reemplazamiento de cadenas de caracteres en un texto

(SearchReplaceDialog)

Para información del resto (recuperación de archivos -OpenDialog-, salvaguarda de archivos -SaveDialog- y elección de tipos de letra -FontDialog-) remito al lector a la ya citada referenciade esta misma revista.

Ventanas para la impresión

Todos los ejemplos que se vayan mostrando a lo largo del artículo parten del ejercicio que sefue desarrollando en la primera entrega, y además se asume una soltura en el manejo ycomprensión de la forma en que Delphi opera.

Cualesquiera de las ventanas estándar que se usarán se encuentran ubicadas en la barra deelementos, bajo la pestaña (tabs) que lleva por nombre Standard Dialogs, en la cual apareceuna colección de iconos semejante a la que se muestra en la figura 1, y de los que sóloutilizaremos los marcados con una flecha.

Figura 1: Iconos a utilizar de la pestaña Standard Dialogs de la barra de elementos

Como ya se comentó, siempre que se elija unos de estos elementos se ubicarán en la ventanay sólo serán visibles en tiempo de creación/modificación, aunque serán trasparentes a la horade mostrarla. Una vez disponibles se puede hacer uso de ellos llamándolos expresamente, laforma genérica, ya se vio, es:

<nombre>.Execute

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

En primer lugar abordaremos las ventanas para la impresión de archivos, así como suscaracterísticas, dejando para después el código a ensayar para que pueda operarse con ellas.

Figura 2: Aspecto de una ventana PrintDialog

Como se puede apreciar en la figura 2, en una ventana PrintDialog se distinguen tressecciones principales. En la parte superior todo lo referente a las características de laimpresora definida por defecto (nombre, tipo, ubicación...), en la parte izquierda el tipo deimpresión que se quiere realizar, donde se permite elegir (según configuración) si se aplicará atodo el documento, a lo seleccionado o a un rango de páginas, y en la parte derecha las copiasa imprimir y la organización de éstas.

De acuerdo con lo que aquí se elija se modificará el contenido de las distintas propiedades(datos) de la ventana, datos que fijarán la forma en que se tendrá que comportar la aplicaciónde nuestro ejemplo.

La ventana PrintDialog, por su parte, de lo único que se encarga es de determinar si seimprimirá o no, el trabajo de volcar lo realmente imprimible lo ha de fijar el procedimientoejecutado. La forma genérica de su uso es mediante la consulta de lo que Execute devuelve,pues un valor booleano a True, significará que se pulsó Aceptar en la ventana PrintDialog, y unvalor False que se presionó Cancelar.

Especificidades desde Delphi

Para crear una ventana PrintDialog desde Delphi se parte de la ventana que contenía el controlde edición multilínea a la que, respetando la funcionalidad hasta ahora conseguida, añadimosotras, es decir, tantos botones gráficos como ventanas queramos invocar. En la figura 3 sepuede apreciar el aspecto que ofrecerá para permitir llamar a las ventanas estándar que nospreocupan.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 3: Ventana de ejemplo para la aplicación

Cuando pulsemos el botón de Imprimir aparecerá una caja estándar similar a la de la figura 2.Que salga con más o menos elementos dependerá de las especificidades dadas en elInspector de Objetos para una ventana de tipo PrintDialog

La explicación de sus propiedades las abordaremos comenzando por el dato Options, puesmuchas de sus características aportan o quitan luz al resto:

Options Son los distintos aspectos y componentes de la ventana, algunos mutuamenteexcluyentes entre sí, otros que se complementan. Todos ellos se recogen en latabla 1.

Valor SignificadopoHelp Si su valor es true aparece un botón de Help.

poPageNums Si su valor es true se permite elegir un rango depáginas para mandar a impresora.

poPrintToFile Si su valor es true aparece el CheckButton en el quese indica si la impresión del texto elegido va aobtenerse por impresora o en forma de archivo. Si semarca, tendremos que preocuparnos de preguntar siestá checked o no y, si lo está, mostrar una ventanapara pedir el nombre del archivo de volcado.

poSelection Si su valor es true se habilita el RadioButtoncorrespondiente a la Selección de texto.

poWarning Si su valor es true y no hay impresora conectada (o nose encuentra disponible) se muestra una ventanainformativa de dicha situación, de la cual sólo sepuede salir pulsando OK.

poDisablePrintToFile Si su valor es True y poPrintToFile lo es también, elCheckButtom en el que se redirige la impresión a unarchivo no aparece.

Tabla 1: Opciones para los archivos

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Collate Determina la organización de las hojas del listado. Cuando es true elCheckButton Organizar aparecerá marcado por defecto.

Copies Número de copias que se desean obtener. De acuerdo con el entero que sealmacena en este dato se pueden lanzar tantas copias como las especificadas,de forma similar a lo siguiente:

for nCopias := 1 to PrintDialog1.Copies do. . .

Siendo nCopias una variable auxiliar del programa para el bucle for,PrintDialog1 el nombre de la variable donde se guarda la ventana instanciaday Copies el dato de dicha ventana destinado a almacenar las reproduccionesdeseadas.

FromPage Fija el comienzo de página para la impresión del documento. Su valor pordefecto es 0, lo cual quiere decir que la posición queda sin efecto.

MaxPage Fija el tope máximo permitido a la hora de especificar el rango de impresión.Si dicho tope se superase aparecería un mensaje informativo dando cuenta deello. Este parámetro no tiene efecto si en Options no está incluido el valorpoPageNums.

MinPage Fija el tope mínimo permitido a la hora de especificar el rango de impresión. Sidicho tope se superase aparecería un mensaje informativo dando cuenta deello. Este parámetro no tiene efecto si en Options no está incluido el valorpoPageNums.

PrintRange Tipo de rango a utilizar en la impresión. Admite tres posibles valores,prAllPages que fija el RadioButton de Todas, prSelection para el de Seleccióny prPageNums para el rango de páginas.

PrintToFile En el caso de que en Options esté incluido el valor poPrintToFile, este valorpermite que el CheckButtom aparezca marcado, o no, por defecto.

ToPage Fija el final de página para la impresión del documento. Su valor por defectoes 0, lo cual quiere decir que la posición queda sin efecto.

No existen eventos para este tipo de ventanas.

El código necesario concerniente a la ventana PrintDialog y, por lo tanto, para poner enfuncionamiento nuestro ejemplo se ve en el fuente 1. Mucho de él se comentó en la entregaanterior, y es el que se encarga de almacenar los presuntos cambios realizados en el controlde edición de la ventana y cambiar el aspecto de la letra utilizada. El que realmente nosinteresa a nosotros aparece en el procedimiento ImrpimirTexto; lo que está en negrilla es elcódigo que ha habido que introducir manualmente.

// --- Fuente 1 ---------------------------------------------------------unit Ventana;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, Printers;

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

type TForm2 = class(TForm) BitBtn1: TBitBtn; BitBtn2: TBitBtn; Memo1: TMemo; SaveDialog1: TSaveDialog; FontDialog1: TFontDialog; BitBtn3: TBitBtn; Imprimir: TBitBtn; SetUp: TBitBtn; Buscar: TBitBtn; BusReem: TBitBtn; PrintDialog1: TPrintDialog; PrinterSetupDialog1: TPrinterSetupDialog; FindDialog1: TFindDialog; ReplaceDialog1: TReplaceDialog; procedure GuardarArchivo(Sender: TObject); procedure CambiarFuente(Sender: TObject); procedure ImrpimirTexto(Sender: TObject); procedure SetUpImpresora(Sender: TObject); procedure BuscaCadena(Sender: TObject); procedure lEncontrado(Sender: TObject); procedure ReemplazaCadena(Sender: TObject); private { Private declarations } public { Public declarations } end;

var Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.GuardarArchivo(Sender: TObject);begin

if SaveDialog1.Execute then begin SaveDialog1.InitialDir := ''; Memo1.Lines.SaveToFile( SaveDialog1.FileName ); end;

end;

procedure TForm2.CambiarFuente(Sender: TObject);begin FontDialog1.Execute; Memo1.Font := FontDialog1.Font;end;

procedure TForm2.ImrpimirTexto(Sender: TObject);var nLinea: Integer; PrintText: TextFile; {variable de tipo file}

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

begin if PrintDialog1.Execute then begin AssignPrn(PrintText); Rewrite(PrintText); Printer.Canvas.Font := Memo1.Font; for nLinea := 0 to Memo1.Lines.Count - 1 do Writeln(PrintText, Memo1.Lines[ nLinea ]);

System.CloseFile(PrintText); end;

end;

end.

Expliquémoslo un poco.

Se sigue utilizando Execute para hacer la llamada estándar a la ventana PrintDialog, comoocurría con sus ventanas hermanas, y se consulta el valor devuelto; determinando así si lo quese ha pulsado ha sido Aceptar o Cancelar.

if PrintDialog1.Execute then

Si se pulsó Aceptar en la ventana estándar de impresión el método Execute devolverá un valortrue, pasando a ejecutarse la porción de código existente entre el begin y el end.

La función AssignPrn() se encarga de asignar un archivo auxiliar al dispositivo de impresión,después de esto se fuerza una primera escritura que provoca la apertura física del archivodeclarado (con ReWrite). Por fin el contenido es actualizado en la impresora por medio deldato Canvas de la variable Printer (esta variable se crea de manera automática para disponeren todo momento de, al menos, una instanciación de la clase de impresión TPrinter). Una vezdefinidos los pasos previos necesarios pasamos a recorrer el texto a imprimir almacenado enel control de edición, tal como se ve más abajo:

for nLinea := 0 to Memo1.Lines.Count - 1 do Writeln(PrintText, Memo1.Lines[ nLinea ]);

Se utiliza la variable auxiliar nLinea que oscila desde el valor 0 (correspondiente a la línea 1del control de edición) hasta el máximo número de líneas almacenadas. Memo1 es la variableque identifica el control de edición multilínea, Lines es el dato que almacena las cadenasalfanuméricas que se han adaptado al ancho del control de edición y que recibe comoparámetro la línea que se desea consultar.

Con todos estos ingredientes lo único que resta es imprimir físicamente las distintas líneas, locual se realiza con la función WriteLn(), encargada de escribir una cadena de caracteres yprovocar un salto de línea.

Por último se cierra el archivo auxiliar con CloseFile().

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Ventanas para la configuración de la impresora

Como ha ocurrido con la ventana PrintDialog y anteriores, siempre que se desee elegir unaventana PrinterSetupDialog la habremos de ubicar en la ventana que va a hacer uso de ella y,luego, llamarla expresamente.

Como hicimos antes, comentaremos la funcionalidad de las ventanas para la configuración deimpresoras e impresión, lo cual no nos llevará mucho.

Figura 4: Aspecto de una ventana PrinterSetupDialog

Especificidades desde Delphi

Para crear una ventana PrinterSetupDialog desde Delphi, partiremos de la ventana antesgenerada, desde la que se permitirá la operación de configuración.

Este tipo de ventanas no posee datos que no hayamos explicado con anterioridad, y comoocurriera con las PrintDialog, no dispone de eventos propios, por lo que su explicación quedaasí zanjada.

Nos limitaremos a llamarla, desde el botón destinado a tal efecto, de la siguiente manera:

procedure TForm2.SetUpImpresora(Sender: TObject);begin PrinterSetupDialog1.Execute;end;

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Ventanas para la búsqueda de cadenas de caracteres

Las ventanas FindDialog ofrecen un aspecto como el de la figura 5, y sus características sedescriben más abajo.

Figura 5: Aspecto de una ventana FindDialog

La explicación de sus propiedades son similares a las que se dieron para otras Standard DialogWindow, de ahí que sólo se comenten las cambiantes respecto a las anteriores.

FindText Cadena de caracteres por defecto que aparecerá en el control de edición. Ennuestro ejemplo lo dejamos en blanco.

Options Son los distintos aspectos y componentes de la ventana. Todos ellos serecogen en la tabla 2.

Valor Significado

frDisableMatchCase Habilita o no el CheckBox que discierne entremayúsculas y minúsculas.

frDisableUpDown Habilita o no los RadioButtons para la búsqueda haciaarriba o hacia abajo.

frDisableWholeWord Habilita o no el CheckBox que discierne entre palabrasexactamente coincidentes o no.

frDown Cuando es True se marca la búsqueda hacia abajo.

frHideMatchCase Oculta o no el CheckBox que discierne entremayúsculas y minúsculas

frHideWholeWord Oculta o no el CheckBox que discierne entre palabrasexactamente coincidentes o no.

frHideUpDown Oculta o no los RadioButtons para la búsqueda haciaarriba o hacia abajo.

frMatchCase Cuando está a True marca el CheckBox que discierneentre mayúsculas y minúsculas. Puede ser cambiadomanualmente.

frReplace Se explicará en las ventanas ReplaceDialog.

frReplaceAll Se explicará en las ventanas ReplaceDialog.

frShowHelp Cuando está a True muestra un botón de ayuda.

frWholeWord Cuando está a True marca el CheckBox que discierneentre palabras exactamente coincidentes o no. Puedeser cambiado manualmente.

9Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Este tipo de ventanas poseen, al contrario que las precedentes, un evento propio que, como enotras ocasiones, manipularemos a nuestro gusto.

OnFind Evento al que se llama cada vez que existe una ocurrencia de la cadenabuscada. En nuestro caso hemos especificado que el procedimiento invocadova a ser uno que lleva por nombre lEncontrado, el cual se puede ver en elfuente 2 de más abajo y que forma parte del fuente 1 antes expuesto.

// --- Fuente 2 ---------------------------------------------------------

. . .

private nLineaActual: Integer; { Private declarations } public { Public declarations } end;

. . .procedure TForm2.BuscaCadena(Sender: TObject);begin nLineaActual := 0; FindDialog1.Position := Point( 20, 20 ); FindDialog1.Execute;end;

procedure TForm2.lEncontrado(Sender: TObject);var I, J, nPos, nCarSaltar: Integer;begin I := nLineaActual; while I <= Memo1.Lines.Count - 1 do begin nPos := Pos( FindDialog1.FindText, Memo1.Lines[I]); if nPos <> 0 then begin nCarSaltar := 0; for J := 0 to I - 1 do nCarSaltar := nCarSaltar + Length(Memo1.Lines[J]);

nCarSaltar := nCarSaltar + (I*2); nCarSaltar := nCarSaltar + nPos - 1; Memo1.SetFocus; Memo1.SelStart := nCarSaltar; Memo1.SelLength := Length( FindDialog1.FindText ); nLineaActual := I + 1; I := Memo1.Lines.Count; end else MessageBeep( MB_ICONHAND ); I := I + 1; end;end;. . .

10Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Como se puede apreciar se ha añadido un código similar al que se usó en el fuente 1.

Disponemos de dos procedimientos, BuscaCadena que se encarga de instanciar la ventanaFindDialog, y lEncotrado que es el que está asociado al evento de éxito o fracaso en labúsqueda realizada.

En el primero, además, se inicializa la variable privada de la clase nLineaActual (encargada desaber por que línea se ha quedado la última búsqueda) y se reposiciona la ventana en lapantalla (a efectos de que ambas no se solapen). En el segundo se desarrolla el algoritmo quese encarga de encontrar la cadena en el control de edición (almacenada en el dato FindText dela ventana estándar de búsqueda), averiguar su posición dentro del control de edición,sobreiluminar la cadena en el texto y recordar cuál fue la última línea donde se halló la cadenadeseada; en caso de no encontrar la ocurrencia, provoca un tono de aviso (en este ejemplo seha recurrido a la función MessageBeep( MB_ICONHAND ) del API de Windows para realizardicho sonido).

Hay que tener en cuenta (y esto corre de su cuenta señor/a lector/a) que la aparición repetidade una cadena en la misma línea no se hallaría, pues sólo se tiene en cuenta la línea por laque se va, y no la columna. Habríamos de acudir a una segunda variable de la clase para irregistrando los valores de la columna actual e ir manipulándolos para calcular correctamentelos desplazamientos.

Ventanas para el reemplazamiento de cadenas de caracteres

No me detendré apenas en las ventanas ReplaceDialog pues son casi idénticas a lasFindDialog. Su única diferencia consiste en que con estás (ya lo sabe) se puede sustituir lacadena hallada. Ofrecen un aspecto como el de la figura 6, y sus características se describenmás abajo.

Figura 6: Aspecto de una ventana ReplaceDialog

FindText: Cadena de caracteres por defecto que aparecerá en el control de edición parala búsqueda. En nuestro ejemplo lo dejamos en blanco.

ReplaceText: Cadena de caracteres por defecto que aparecerá en el control de edición dereemplazamiento. En nuestro ejemplo lo dejamos en blanco.

Options: Los posibles valores se recogen en la tabla 2 y 3.

Valor Significado

frReplace Especifica que la cadena ReplaceText reemplazará laocurrencia de la cadena FindText.

frReplaceAll Especifica que la cadena ReplaceText reemplazará TODAS lasocurrencias de la cadena FindText.

11Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Este tipo de ventanas poseen además del evento OnFind de las FindDialog, elevento OnReplace.

OnReplace: Evento al que se llama cada vez que existe un reemplazamiento de la cadenade búsqueda.

Una operativa similar a la que se ha usado con las ventanas FindDialog se puede aplicar a lasReplaceDialog para realizar búsquedas y reemplazamientos en el control de edición de nuestraventana de trabajo.

Bueno, hasta aquí las ventanas de diálogo estándar que restaban para completar la saga quese comenzó en el número anterior y que Windows brinda para la unificación de tareas entreaplicaciones.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (X)Delphi y Windows 95 (I)

Por Vladimir Algara

Continuando con el afán de avanzar hacia la asimilación de conceptos y el control de lasherramientas brindadas por Delphi, atajaremos los nuevos elementos disponibles en la versiónde 32 bits del omnipresente producto estrella de Microsoft, lo cual quiere decir que sólo podránponerlo en funcionamiento aquellos que posean la versión 2.0 o posterior de Delphi.

Ya vimos, hace no muchas entregas, el uso y abuso de los controles, en ésta continuaremoscon esa saga, pero aplicando los esfuerzos a conocer los nuevos controles aportados enWindows 95.

Los distintos controles que vamos a comentar se encuentran, como todo hasta ahora, en lapaleta de herramientas (paleta de elementos); bajo la pestaña que lleva por nombre Win95. Enella se pueden encontrar los iconos de la relación de más abajo, correspondientes con susrespectivas funcionalidades:

Tabulador de control (carpetas dependientes)

Página de control (carpetas independientes)

Estructura arbórea Estructura en forma de lista

Barra de marcaje Barra de progreso (barra Gauge)

Cabecera de control Barra de estado

Spinner (UpDown) HotKey (teclas activas)

Editor de texto enriquecido (RTF)

Dada la gran variedad de controles, y dado que cada uno tiene su propia colección deespecificidades, voy a ir comentando la forma de usarlos (poniendo ejemplos que pretendenser esclarecedores) hasta completar un tamaño aceptable para el presente artículo.

Las carpetas

Antes de comenzar quiero hacer una aclaración y solicitar el perdón del lector, pues nunca sémuy bien cómo referirme a los controles denominados ControlTabs, de ahí que, a veces, losllame (o los haya llamado en artículos precedentes a éste) carpetas, pestañas, o simplementeTabs; así que, teniendo esto en cuenta por ambas partes, intentaré referirme a ellos siemprecon el mismo nombre (carpetas) y no liarme más de lo que suelo.

Pues bien, los ControlTabs (es broma). Pues bien, las carpetas, son, en mi opinión, unos delos controles más ingeniosos que tienen las ventanas de Windows, pues en una sola de ellasse puede solicitar gran cantidad de información y, además, tenerla organizada por grupos quecumplen un determinado patrón. Antes de que existieran no había más remedio que mostrargigantescas ventanas para la solicitud de datos en las que cada agrupación no quedaba másremedio que identificarla por medio de un GroupBox (cuadro agrupador) y, dentro de él, sedisponían los controles pertinentes, o bien se diseñaba una ventana por cada grupo de datos.

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Con las carpetas podemos trabajar con una ventana de pequeñas dimensiones en la que sepermite preguntar multitud de cuestiones, y, además, gratamente organizadas. Por ejemplo,una de las ventanas de configuración más complicadas que yo he visto es la que muestra elprocesador de textos Microsoft Word, en su ítem Opciones del menú Herramientas. En lafigura 1 podemos echar un vistazo a la ventana referida, en la que, entre las muchas carpetasque dispone, se ha seleccionado una de las que menos datos solicita (en la figura se puede verque se trata de la carpeta de Edición).

Figura 1: Carpetas de Word para la configuración de Opciones

Todos los ejemplos que se vayan mostrando a lo largo del artículo parten de un ejercicio quese seguirá desarrollando en la segundo entrega; además, se asume una soltura en el manejo ycomprensión de la forma en que Delphi opera con el lenguaje y con el diseño de las ventanas,así como la conexión de éstas con el inspector de objetos.

Cuando estudiamos las carpetas de Windows 95 podemos hacer dos distinciones esenciales;por un lado se encuentran las carpetas que hacen referencia a un mismo elemento y cuyoobjetivo suele consistir en usarlo para distinguir sus distintas partes, organizándolas de lamanera más conveniente; por otra parte se dispone de las carpetas que, por decirlo de algunaforma, son independientes entre sí (como lo son las de la figura 1 de más arriba).

Respectivamente encontramos estos dos tipos de carpetas bajo el nombre de tabuladores decontrol (ControlTab) y páginas de control (ControlPage).

El ejemplo propuesto consistente en una única ventana (en entregas posteriores se convertiránen dos), en la que se escoge un tipo de cada uno de los de arriba; unas carpetas dependientes,en la que cada una se corresponde con el capítulo de un hipotético libro, y unas carpetasindependientes, donde se permite perfilar las características de una persona según una seriede parámetros. El resultado final se muestra en la figura 2 y los pasos que se han seguido parallegar a eso se detallan más abajo.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 2: Ventana conteniendo los dos tipos de carpetas

Especificidades de las carpetas

Vamos a ir explicando por orden y aisladamente, aunque más adelante comprobaremos quese solapan, cada uno de los tipos de carpetas.

Carpeta del t ipo ControlTabs

Cuando se elige un ControlTabs (la de los capítulos del libro en nuestro ejemplo), sólotenemos la posibilidad de enumerar las distintas carpetas de que va a disponer, sin opción aubicar elementos diferentes en diferentes carpetas (como sucederá en las del otro tipo). Si,como en nuestro ejemplo, decimos que en esa carpeta va a aparecer un Control de EdiciónMultilínea, en cada una de las carpetas aparecerá dicho control y no otro (a no ser que locambiemos en tiempo de ejecución, pero esto sería excesivo, pues esa operativa está yacontemplada en las carpetas de tipo PageTabs, y lo único que conseguiríamos sería controlarmanualmente algo que ya se hace de manera automática).

La explicación de las propiedades y de los eventos las abordaremos comenzando por el datoTabs, pues es el encargado de definir el aspecto, en lo que a literales se refiere, de la carpetaen curso:

- Tabs: Se trata de un dato de la clase TStrings, y contiene la relación de literales queaparecerán en las pestañas de cada una de las carpetas que componennuestro control. Estos literales pueden ser cambiados en tiempo de ejecución,pero lo habitual es perfilarlos en tiempo de diseño por medio de la ventana dela figura 3. En ella Delphi brinda la posibilidad de ir añadiendo, en forma delista, la relación de ítems que se quieren mostrar. En nuestro ejemplo seespecifican los cuatro capítulos del libro.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 3: Definición de los literales de las carpetas

Una posibilidad, no obligada, consiste en transformar esta lista en un archivo(archivo plano de texto ASCII, editable desde cualquier parte), para despuéspoderlo recuperar y asignarlo en tiempo de ejecución.

- MultiLine Esta propiedad brinda la posibilidad de mostrar las carpetas en una sola línea(si no cupieran en la ventana aparecería una barra de desplazamiento que nospermitiría llegar a cualquiera de ellas) o en varias (como se apreciaba en lafigura 1). La opción por defecto es el valor lógico false, pero, para nuestroejemplo, no va a afectar que esté en un estado o en otro, ya que una de lasmodificaciones que vamos a introducir en el control va a ser el de sualineación respecto de la ventana, y esto, en cualquier caso, tiene másprioridad que lo guardado en MultiLine.

- TabHeight Altura del recinto donde va el literal de las carpetas. Cuando se especifica 0(valor por defecto) Delphi se encarga de calcular el tamaño, en píxels, deacuerdo al tipo de letra utilizado (forma y tamaño de Font), pero cuando somosnosotros los que imponemos el valor no habrá cálculo automático posible,siendo el valor asignado el que se utilice (aunque esto suponga el sacrificio departe del texto y lo convirtamos en ilegible).

- TabIndex Es un número que corresponde a la carpeta que se seleccionará por defecto alvisualizar la ventana que contiene el control. Por defecto su valor es de -1, locual indica que se tiene que escoger la primera carpeta del conjunto,independientemente de la posición que ésta ocupe.

En la fase de diseño los distintos literales aparecen dispuestosconsecutivamente, pero esta cadencia se podría alterar al realizar unaordenación de algún tipo, por ejemplo alfabética, que hiciera que su posiciónvariase respecto al original. En este caso los valores internos asociados a cadaliteral no se reasignarían, por lo que el elemento que ahora ocupa la posicióninicial bien podría tener un TabIndex igual a 3. Esto se soluciona recurriendo alvalor -1 que, como adelanté, siempre identifica al primer elemento. Lasdistintas carpetas están numeradas empezando desde 0 y terminando enCount-1 (número de carpetas menos una).

Otras propiedades que han sido cambiadas para el ejemplo, pero que no voy a explicar porqueya lo hice en entregas anteriores, son Align (a la que se le ha asignado un valor de alTop para

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

que siempre aparezca en la parte superior de la ventana), Hint y ShowHints (para mostrar unpequeño tip cada vez que paremos el cursor del ratón sobre los capítulos del libro) y Font (paraque cualquier elemento ubicado en la carpeta aparezca con el tipo de letra y tamañoestablecido por defecto para la carpeta).

Por otro lado, las especificaciones dadas al control de edición multilínea han sido la de fijarAlign (que se le ha asignado un valor de alClient para que abarque toda la superficie de lacarpeta a la que pertenece) y la de vaciar las líneas para que aparezca totalmente vacío enfase de diseño (propiedad Lines en blanco).

En lo que a eventos se refiere, se han manipulado dos de ellos; uno que está estrechamenterelacionado con la carpeta (es OnChange y va a ser el que nos permitirá realizar una ciertaoperación cada vez que se seleccione alguna de las carpetas disponibles; en nuestro ejemplose controla que se cargue el contenido de un determinado archivo, según la carpeta elegida) yotro con la ventana (es OnActivate y va a ser el encargado de recuperar el texto inicial que semostrará en la carpeta; es decir, el de texto correspondiente al Capítulo 1).

El código que se ensaya en OnActivate se ve en el fuente 1.

// --- Fuente 1 -----------------------------------------------------procedure TVentanaPpal.FormActivate(Sender: TObject);begin Memo1.Lines.LoadFromFile('CAP1.TXT');end;

Como se aprecia, nos valemos del contenido de un archivo que lleva por nombre CAP1.TXT.Este archivo debe existir y debe estar ubicado en el mismo directorio donde se encuentre elejecutable de la aplicación de ejemplo, de lo contrario la puesta en marcha de nuestro ejemploproducirá un error.

Recurriendo al método LoadFromFile, de la clase TStrings, recuperamos la "valiosa"información y la almacenamos en la propiedad Lines de nuestro control de edición. Esto haceque, nada más mostrar la ventana, el texto 1 aparezca en la carpeta del Capítulo 1, como sesupone que debe ser. No hay otra forma de hacerlo, pues los controles de tipo TMemo, aunquesí permiten especificar un valor por defecto a partir del contenido de un archivo, sólorecuperan el contenido de dicho archivo en la lectura inicial, pero no lo asocian unívocamente;por lo que no se actualizará si cambia la del archivo.

Algo similar hacemos con el método OnChange. En el fuente 2 se hace extensiva, a todas lascarpetas, la idea aplicada en el fuente 1.

// --- Fuente 2 -----------------------------------------------------procedure TVentanaPpal.MiraCapitulo(Sender: TObject);var nCapitulo: integer;begin nCapitulo := TabCapitulos.TabIndex; case nCapitulo of 0 : Memo1.Lines.LoadFromFile('CAP1.TXT'); 1 : Memo1.Lines.LoadFromFile('CAP2.TXT'); 2 : Memo1.Lines.LoadFromFile('CAP3.TXT'); 3 : Memo1.Lines.LoadFromFile('CAP4.TXT'); end;

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

end;

Como se ve, almacenamos en la variable auxiliar nCapitulo el valor contenido en la propiedadTabIndex del conjunto de carpetas, el cual nos indica qué carpeta en concreto se haseleccionado. De acuerdo a que haya sido una u otra recurrimos al pertinente archivo. Estadistinción se realiza por medio de una estructura case...end, donde se evalúan los valoresposibles de la variable nCapitulo.

Como alternativa al fuente propuesto, podría haberse recurrido a construir el nombre delarchivo como concatenación de cadenas de caracteres (así evitaríamos utilizar el case...end),pues su nombre nos lo permite. Esto lo haríamos de la siguiente forma:

// --- Fuente 3 -----------------------------------------------------procedure TVentanaPpal.MiraCapitulo(Sender: TObject);var cCapitulo: string;begin // Se almacena en cCapitulo el número de la carpeta elegida más 1 Str( TabCapitulos.TabIndex + 1, cCapitulo ); // Quitar blancos a la cadena de caracteres cCapitulo := Trim( cCapitulo ); // Componer nombre del archivo sabiendo que todos empiezan por // CAP y tienen extensión TXT Memo1.Lines.LoadFromFile('CAP' + cCapitulo + '.TXT');end;

Carpeta del t ipo PageControl

Cuando se elige una carpeta de la clase TPageControl (la que se ha utilizado para definir lascaracterísticas personales en nuestro ejemplo), tenemos la posibilidad de ubicar en cada unade ellas la cantidad de controles que nos vengan en gana (no como sucedía en las del tipoanterior).

La explicación de las propiedades y de los eventos también las abordaremos comenzando porel dato análogo a Tabs, que, como ya sabemos, era el encargado de definir los literales de lacarpeta en curso.

En este caso, la forma de diseñar cada carpeta no se hace a través de una propiedad delInspector de Objetos, sino a través del menú local asociado a la carpeta. Una vez ubicado elcontrol TPageControl en la ventana estamos en disposición de hacer clic con el botónsecundario del ratón (botón derecho del ratón para los diestros e izquierdo para los zurdos),con lo que surgirá un menú local como el de la figura 4. En ese menú disponemos de tresposibilidades más de las que habitualmente aparecen, que son la de añadir una nueva página(nueva carpeta), ir a la siguiente o ir a la anterior (estas dos últimas opciones son accesorias,pues podemos seleccionar una u otra carpeta sin más que pinchar sobre la deseada, y no hacefalta para nada recurrir al menú).

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 4: Menú local de las carpetas

En lo que al Inspector de Objetos se refiere, tendremos dos conjuntos de propiedades según elpunto de la carpeta en el que pinchemos; así, si lo hacemos en la parte de la carpeta destinadaa mostrar el literal que la identifica (pestaña) aparecerá un Inspector de Objetos en el que sepermite alterar las propiedades de la clase TPageControl propiamente dicha, mientras que sipinchamos sobre el "cuerpo" de la carpeta lo que se podrá definir serán las propiedades yeventos de la clase TTabSheet.

Nos encontramos en un claro ejemplo de clases contenedoras y contenidas. En laprogramación orientada al objeto se introduce el concepto de posesión en contraposición a laherencia múltiple. Un objeto puede almacenar en una o más de sus propiedades otro objeto, e,incluso, un array de objetos. En este caso la clase TPageControl almacena un array de objetosde la clase TTabSheet.

- ActivePage: Se trata del dato que identifica cuál de los objetos TTabSheet va a ser el quese encuentre pulsado nada más mostrar la ventana (ver figura 2). En nuestroejemplo ponemos el sexo por delante de cualquier otra cosa.

- MultiLine Muestra las carpetas en una sola línea o en varias. La opción por defecto esfalse.

- TabHeight Altura del recinto donde va el literal de las carpetas. El valor 0 equivale a uncálculo automático.

Otras propiedades que han sido cambiadas para el ejemplo son Align (que se le ha asignadoun valor de alBottom para que siempre aparezca en la parte inferior de la ventana), Hint yShowHints (para mostrar un pequeño tip). Hay que hacer una llamada sobre los tips, puesexiste uno genérico para el control TPageControl y otros específicos para cada uno de loscontroles TTabSheet.

En lo que a eventos se refiere no se ha manipulado ninguno de ellos, pues no han hecho faltapara la operativa de ejemplo y los que hay nuevos son idénticos a los de la clase TTabControl.

Por último, partiendo de que en cada carpeta se puede meter lo que nos venga en gana, heaprovechado para utilizar controles que aún no habíamos visto, los de tipo CheckButtons. Lasdemás carpetas se ha rellenado usando controles conocidos como RadioButtons y GroupBox,todos ellos los podemos encontrar dentro de la carpeta Standard de la barra de elementos.

Recordemos que un RadioButton Group es un conjunto de RadioButtons con la característicade ser, dentro del grupo, mutuamente excluyentes entre sí. Aunque existe la posibilidad de

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

elegir RadioButton individuales, lo más apropiado es agruparlos de esta manera lógica,.

Propiedades de los CheckButtons

Como viene siendo habitual, hacemos un rápido recorrido por eventos y propiedades no vistospor el momento, y que están ligados a los CheckButtons.

-.AllowGrayed: Permite definir un control de tres estados, donde se puede dejar marcado,desmarcado o indeterminado (el cual se corresponde con una tonalidad gris,que da nombre a la propiedad)

- Caption: Es la propiedad en la que se especifica el literal a mostrar.

-.Checked: Permite partir de un valor marcado (valor true) o desmarcado (false, pordefecto) para el Check.

-.State: Son los distintos estados posibles y los cuales podemos consultar por medio delos valores predefinidos. Estos valores se recogen en la tabla 1.

Valor DescripcióncbUnchecked El check box no tiene marca.cbChecked El check box tiene marca indicando que el usuario ha

seleccionado la opción correspondiente.CbGrayed El check box se encuentra en posición indeterminada. La

aplicación se tendrá que encargar de interpretar quésignificado tiene esto.

Tabla 1: Valores almacenados según el estado de un CheckButton

El único evento digno de mención es OnClick que, como su nombre hace pensar, será el quese llame cada vez que marquemos o desmarquemos el control

Bueno, hasta aquí llego, en la próxima entrega, dado que lo más complicado ya se ha idorepasando, explotaremos la funcionalidad del resto de los nuevos controles de Windows 95.Hasta entonces, un saludo.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (XI)Delphi y Windows 95 (II)

Por Vladimir Algara

En esta segunda parte voy a continuar con los controles disponibles en Windows 95 y, como yase vio en la entrega anterior, están disponibles en la versión de 32 bits de Delphi, es decir, en la2.0 y posteriores.

Los distintos controles que vamos a comentar en esta entrega serán los que se encuentran acontinuación de las carpetas, pues en la anterior, que llevaba el número I, se trataron,ampliamente, todo lo referente a ellas. Como recordará el lector, los dos tipos de carpetasvenían simbolizados por sendos iconos y se correspondían con:

Tabulador de control (carpetas dependientes)

Página de control (carpetas independientes)

El resto, como los anteriores, se encuentran disponibles en la paleta de herramientas (paleta deelementos); bajo la pestaña que lleva por nombre Win95.

Dada la gran variedad de controles, y dado que cada uno tiene su propia colección deespecificidades, seguiré comentando la forma de usarlos, aprovechando el ejemplo del anteriorartículo y ampliándolo para el que nos ocupa.

Las estructuras arbóreas

Estas estructuras son las usadas, por poner un ejemplo por todos conocido, en el Explorador deWindows 95, en el que la sección de los directorios se encuentra en la parte izquierda (éstos sepueden desplegar, comprimir o navegar por ellos). En la figura 1 se muestra la estructuracomentada y que pertenece, como ya se ha comentado, al Explorador de Windows 95.

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 1: Estructura arbórea del Explorador

Estas estructuras son, en mi opinión, los controles que ofrecen la mejor organización visual delos elementos principales respecto a los subordinados. La principal ventaja consiste en que sepermite seleccionar un determinado ítem dentro de una jerarquía de padre e hijos, en los quecada familia se puede representar en un compartimento-estanco.

Como ocurre en los datos de la figura 1, podemos organizar nuestra información en celdillas,que cada una de estas celdillas disponga de un número variable de datos (que a su vez puedeconsistir en otra celdilla) los cuales disponen de distintas características. Si aplicamos estalógica a los elementos de la figura 1, cada celdilla se correspondería a cada uno de losdirectorios, los datos almacenados en estos directorios pueden ser, a su vez, directorios oarchivos (lo cuales no aparecen en la sección de la izquierda, sino en los de la derecha).

El siguiente ejemplo se ha desarrollado con la lógica aquí descrita, y su resultado final se puedever en la figura 2. En él se establecen dos grupos correspondientes a sendos elementosprincipales (Alfonso y Vladimir), en ellos se albergan tres ramas (Motes, Ojos y Aficiones), cadauna de las cuales posee una cantidad concreta de datos, todos ellos estrictamente clasificadosy accesibles de manera coherente. Como ocurriera con la sección de los directorios en elExplorador, estas ramas se pueden contraer y expandir (no así las hojas) y también seleccionardesde programa; cualquiera de estas acciones provoca la activación de un evento que, en casode programarlo, realizará las acciones pertinentes.

Figura 2: Propiedades y características para los elementos Alfonso y Vladimir

Como hacemos siempre, recorramos las distintas propiedades y eventos disponibles para estecontrol y expliquemos las de más interés.

Propiedades

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

- Count Número de elementos en el árbol.- Editable Determines si el valor actualmente seleccionado es editable o no, de acuerdo

con su contenido true o false.- Indent Número de pixeles usados para la separación horizontal.- Item Retorna el ítem del árbol.- Items Almacena todos los ítems del árbol. La forma de definirlos, en la fase de diseño

de la aplicación, se puede ver en la ventana de la figura 3. En fase deejecución, como veremos más abajo, se puede alterar la estructura inicialmentefijada.

Figura 3: Definición de los nodos/ítems en tiempo de diseño.

New Item: añade un nuevo nodo.New SubItem: añade un nuevo ítem al nodo seleccionado.Delete: Elimina el ítem seleccionadoLoad: Almacena, en forma de archivo, la estructura creada hasta el momento.

- ShowButtons Fuerza la aparición del símbolo '+' en un nodo comprimido (expansible) y elmenos '-' en uno ya expandido (comprimible).

- ShowLines Fuerza la aparición de las líneas que conectan los nodos y los ítems.- ShowRoot Fuerza la aparición de las líneas que conectan los nodos padres con los nodos

hijos.- Sorted Ordena alfabéticamente los literales.- TopItem Primer ítem del árbol.

Métodos

- AddChildFirst Añade un nodo al árbol en la primera posición.- AddChildObjectFirst Añade un nodo hijo al árbol asociándole un objeto.- AddFirst Añade un ítem en el primer nodo.- AddObject Añade un ítem al árbol en la primera posición.- AddObjectFirst Añade un ítem al árbol asociándole un objeto.- Append Agrega al final del árbol un ítem.- AppendChild Agrega al final del árbol un nodo.- AppendChildObject Agrega al final del árbol un nodo asociándole un objeto.- AppendObject Agrega al final del árbol un ítem.- FullCollapse Comprime todo el árbol.- FullExpand Expande todo el árbol.- GetFirstNode Devuelve el primer elemento del árbol.- Insert Inserta un ítem en una posición concreta del árbol.-SortChildren Ordena alfabéticamente los literales de un nodo concreto.

Eventos

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

-OnChange Se lanza cuando el aspecto del árbol cambia (después de comprimir, de añadir,de borrar, etc.).

-OnChanging Se lanza cuando el aspecto del árbol cambia (antes de refrescar en pantalla).-OnCollapse Se lanza al contraer un nodo.-OnCollapsing Se lanza al contraer un nodo (antes de refrescar en pantalla).-OnDeletion Se lanza al borrar un ítem.-OnEdited Se lanza al editar un ítem.-OnEditting Se lanza mientras se está editando un ítem.-OnExpanded Se lanza al expandir un nodo.-OnExpanding Se lanza al expandir un nodo (antes de refrescar en pantalla).

Las estructuras detalladas

Estas estructuras también son usadas, por poner el mismo ejemplo del Explorador de Windows95, en la sección de los archivos, que se encuentra en la parte derecha de la ventana queveíamos en la figura 1. los archivos existentes en el directorio/unidad seleccionado. En la figura1 se muestra la estructura comentada y que pertenece al Explorador de Windows 95.

Esta estructuras está destinada a guardar diversa información de un elemento, de ahí que en elExplorador constituya la mejor de las opciones disponibles, pues cada movimiento en laestructura arbórea se corresponde con una actualización de la estructura de detalle (partederecha de la ventana). Cada archivo consta de distintas propiedades, como son el nombre(propiedad principal), el tamaño, el tipo y la fecha y hora de creación.

El siguiente ejemplo, resultado de ejecutar la aplicación de ejemplo de la entregacorrespondiente a este mes, se ha desarrollado con la lógica de ir añadiendo información a undeterminado concepto, y su resultado final se puede ver en la figura 4. Los encabezamientos(Cab1, Cab2, etc.) son descriptores de lo que aparecerá más abajo, y lo que aparece más abajoes el resultado de inventar por mi parte información absolutamente incoherente.

Figura 4: Ventana con varios controles

Todos los ejemplos que se vayan mostrando a lo largo del artículo parten del ejercicio que sedesarrolló en la primera entrega; además, se asume una soltura en el manejo y comprensión de

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

la forma en que Delphi opera con el lenguaje y con el diseño de las ventanas, así como laconexión de éstas con el inspector de objetos.

Recorramos, aquí también, las distintas propiedades y eventos disponibles para este control.

Propiedades

- Columns Relación de los objetos TListColumn.- EditLabels Permite o no la edición de los ítems- IconArrangement Posición por defecto de los iconos.- Items Relación de los objetos TListItem. La forma de definirlos es idéntica a la

que se explicó en el ejemplo de las estructuras arbóreas, y, por lo tanto, sedefinen en tiempo de diseño en una ventana similar a la que se mostró enla figura 3 de más arriba.

- ViewStyle Tipo de visualización a utilizar. Ver la tabla 1 para especificaciones másconcretas.

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Valor DescripciónvsReport En forma de lista detallada propiamente dichavsIcon En forma de iconosvsList En forma de relación en la que sólo aparece la propiedad

principalvsSmallIcon En forma de lista de iconos pequeños

Tabla 1: Tipo de listas disponibles.

- MaxItems Máxima cantidad de ítems disponibles por control.- ShowColumnHead Permitir o no la visualización de la cabecera descriptiva.

Métodos

- Arrange Reorganización de los ítems.- Scroll Desplazamiento de los elementos en la ventana.- Sort Permite la ordenación manual o automática de los elementos.

Eventos

- OnChange Se lanza el aspecto de la lista (después de añadir, borrar, etc.).- OnChanging Se lanza al cambiar el aspecto de la lista (antes de refrescar en pantalla).- OnColumnClick Se lanza al pinchar sobre la cabecera de los elementos. Por ejemplo, al

pinchar sobre la cabecera destinada a mostrar el nombre del archivo en elExplorador de Windows 95, se produce una ordenación por dicho nombre,el código encargado de esto debería implementarse en el eventoOnColumnClick.

- OnDeletion Se lanza al borrar un elemento de la lista.- OnEdited Se lanza al editar.- OnEditting Se lanza mientras se está editando un elemento de la lista.

Las barras de marcaje (TrackBar)

Se trata, ni más ni menos, que de una ampliación (para mejor) de las barras dedesplazamiento, con la peculiaridad añadida que se pueden dar, además de los valores máximoy mínimo alcanzables, unos valores de referencia. Además, la escala es mucho más reducida yconcreta que la que se obtenía con las ScrollBar, por lo que su manejo se hace más cómodo.

Podemos ver una barra de éstas (en uno de sus aspectos habituales) en la ventana de la figura4 de más arriba. En el ejemplo se muestra un barra en la que elegir una temperatura ambiente(en grados centígrados) y en la que se fija un tope máximo de 70º y un mínimo de -100º;además, se ponen unas marcas de referencia en las que se indican cuáles han sido el máximoy mínimo valores alcanzados en la Tierra (61º y -78º, respectivamente). Todos estos valoresson internos a la barra, por lo que para poder visualizarlos ha habido que recurrir a la ubicaciónen la ventana de textos fijos.

Propiedades

- LineSize Cantidad de marcas a usar en la barra- Max Valor máximo alcanzable en la barra (en nuestro ejemplo 70º).- Min Valor mínimo alcanzable en la barra (en nuestro ejemplo -100º).- Orientation Posición de la barra: horizontal (tbHorizontal) o vertical (tbVertical). En nuestro

ejemplo horizontal.

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

- PageSize Cantidad de marcas a desplazar cuando se usa avance o retroceso de página(en nuestro ejemplo 10).

- Position Valor por defecto de la barra (en nuestro ejemplo 0º).- SelEnd Punto máximo de referencia (en nuestro ejemplo 61º).- SelStart Punto mínimo de referencia (en nuestro ejemplo -78º).- TickMarks Posición de las marcas respecto de la barra. Ver tabla 2.

Valor DescripcióntmTopLef Arriba de la barra si es horizontal o a la izquierda si es

verticaltmBoth A ambos lados de la barratmBottomRight Abajo de la barra si es horizontal o a la derecha si es vertical

Tabla 2: Tipo de posiciones disponibles

Eventos

El único evento digno de mención es OnChange que, como su nombre hace pensar, será el quese llame cada vez que movamos el control

- OnChange Siempre que la barra se mueve, ya sea con el ratón, con las flechas demovimiento o con el avance o retroceso de página, se invoca este método.Nosotros lo usaremos para actualizar un texto fijo y que el resultado de moverla barra se vea allí fielmente reflejado.

En el fuente 1 se ve lo explicado en el evento OnChange, y además de aprovecha el valor de labarra para determinar que, al cerrar la ventana, habrá un retardo más o menos largo o no habráningún tipo de espera. Se actualiza un literal y luego se obra en consecuencia.

// --- Fuente 1 ---------------------------------------------------------

procedure TDlgFiesta.ActualizarGrados(Sender: TObject);

var

cGrados: string[4];

begin

Str( TrackGrados.Position, cGrados );

Grados.Caption := 'Grados: ' + cGrados;

if TrackGrados.Position > 0 then

LitBarra.Caption := 'Barra de progreso desde 0 a ' + cGrados

else

LitBarra.Caption := 'No habrá barra de progreso al salir';

end;

Se declara la variable de tipo cadena cGrados (de hasta cuatro caracteres) en la que sealmacena el valor actual de la barra (el cual se almacena en el dato interno Position), comodicho valor es de tipo numérico, previamente hay que convertirlo a cadena de caracteresmediante la función Str(), resultado que concatenamos con el literal 'Grados: '. El conjunto finalse asigna al texto fijo que lleva por nombre Grados.

De manera similar se actualiza el texto fijo que lleva por nombre LitBarra, y se hace de acuerdoa que el dato Position de la barra. Cuando sea estrictamente mayor que cero se especificarán lacantidad de unidades que contará nuestra (aún inexistente) barra de progreso, en cambio,cuando sea menor o igual que cero, aparecerá el texto constante 'No habrá barra de progresoal salir'.

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Las barras de progreso

Las barras de progreso o barras Gauge son los controles que se usan cuando se quiere indicarel punto en el que se encuentra un determinado trabajo (por lo habitual largo en el tiempo o enel tamaño). Así, cuando se necesita realizar una copia de seguridad, por ejemplo, es norma,casi obligada, mostrar el punto en el que nos encontramos y ofrecer una idea orientativa de loque el proceso va a tardar. Estas dos características las cubren las barras de progreso, pues alrellenar completamente el recinto disponible a tal efecto nos encontraremos en la situación defin del proceso.

En nuestro ejemplo se ha utilizado la barra de progreso para provocar un retardo en el cierre dela ventana, que será tanto más grande cuanto mayor sea la temperatura elegida en la barra demarcaje comentada en el apartado anterior.

Cuando la barra de estado llegue al final (este final habrá que fijarlo en tiempo de ejecuciónsegún el contenido Position de la TrackBar) querrá decir que ha contado todo lo que tenía quecontar y, por ende, deberá cerrar la ventana.

Propiedades

- Max Valor máximo alcanzable en la barra (en nuestro ejemploTrackGrados.Position).

- Min Valor mínimo alcanzable en la barra (en nuestro ejemplo 0).- Position Valor por defecto de la barra (en nuestro ejemplo 0).- Step Cantidad de marcas cuando avance la barra (en nuestro ejemplo 1).

En el fuente 2 se ve el código a ensayar para el control de la barra de progreso.

// --- Fuente 2 ---------------------------------------------------------

procedure TDlgFiesta.TiempoEspera(Sender: TObject);

var

nCont: integer;

nAux: integer;

cTope: string[4];

begin

ProgressBar1.Min := 0;

ProgressBar1.Max := TrackGrados.Position;

for nCont := 0 to TrackGrados.Position do

begin

// Retardo

for nAux := 0 to 1000000 do;

// Asignar los valores de nCont a la barra de progreso

ProgressBar1.Position := nCont;

Str( nCont, cTope );

// y asignarlo al literal asociado

LitActual.Caption := cTope;

// Procesar los mensajes mientras que se encuentre en el bucle

Application.ProcessMessages;

end;

Close;

end;

9Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Como se ve, se declaran todas las variables auxiliares que van a hacer falta en elprocedimiento. Como primera medida se asigna a los valores mínimo y máximo los valores de 0y, como ya hemos dicho, grados de la TrackBar, pasando a continuación a realizar un bucle queabarca desde 0 hasta el máximo fijado.

Dentro del bucle se produce un retardo, que va de 0 a 1000000, después del cual se procede aactualizar los valores de la barra de progreso y del literal fijo. Si sólo hiciéramos esto,observaríamos cómo, efectivamente, la barra de progreso se iba actualizando, aunque no así elliteral fijo. Esto sucede porque nos encontramos actualizando un control que no tiene capacidadpara "autorrefrescarse" (el texto fijo) cuando el control de la aplicación lo tiene un bucle (pueseste bucle está acaparando los recursos del sistema mientras nos encontremos dentro de él).Ese es el motivo de que se ponga la siguiente línea:

Application.ProcessMessages;

que obliga a que los eventos se sigan procesando de la forma esperada, y por lo tantopermitiendo que el texto fijo se actualice.

Bueno, hasta aquí llego, en la próxima entrega seguiremos indagando en la funcionalidad delresto de los nuevos controles de Windows 95. Hasta entonces, ponga en marcha el ejemploanexo y comprueba lo que he querido decir con toda esta perorata. ¡Ah!, un saludo.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (XII)Delphi y Windows 95 (y III)

Por Vladimir Algara

En esta tercera y última parte voy a finalizar con los controles disponibles en Windows 95 y,como ya se recordó en la entrega anterior, sólo están disponibles en las versiones de Delphi apartir de la 2.0 en adelante.

Los distintos controles a abordar en esta entrega serán los que se ven a continuación.

Cabecera de control

Barra de estado

Spinner (UpDown)

HotKey (teclas activas)

Editor de texto enriquecido (RTF)

Todos ellos se encuentran en la paleta de herramientas (paleta de elementos); bajo la pestañaque lleva por nombre Win95.

Cabeceras de control

Son identificadores que suelen encontrarse en la parte superior de la ventana (la alineación pordefecto, respecto de la ventana , es de AlTop) y que responden a la pulsación efectuada sobreellos. En realidad, yo las concibo como un invento cómodo de agrupar los PushButton y darlesun aspecto diferente al que estamos acostumbrados, pues se trata de controles que no llegan ala organización que nos puede dar las estructuras en forma de lista, ni la funcionalidad de lascarpetas (pestañas).

En cualquier caso, se gestionan, como ya he dicho, con la misma lógica que los PushButton, deahí que no vaya a hacer mucho hincapié en su manipulación, limitándome a contar lasdiferencias que existen entre uno y otro.

En la figura 1 se muestra el control comentado, que pertenece a la clase THeaderControl, ymás abajo se enumeran sus propiedades y sus eventos más resultones.

Figura 1: Ejemplo de Cabecera de Control

Para la definición de las distintas secciones se utiliza la ventana destinada a tal fin, y que sepuede ver en la figura 2.

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 2: Definición de las secciones de un THeaderControl

Como se puede advertir se dispone de dos posibles operaciones, la de añadir nuevassecciones (New) y la de eliminar alguna de las existentes (Delete). De acuerdo a aquella que setenga seleccionada, en la parte de la derecha de la ventana podremos modificar distintoselementos (los cuales se corresponden con sendas propiedades de la clase THeaderControl), asaber:

- Text Literal que aparecerá en la sección creada. Puede modificarse en tiempo deejecución.

- Width Anchura en píxels de la sección. Este ancho, si no se especifica explícitamente,se puede cambiar en tiempo de ejecución, bien por acción del usuario, bien através de programación.

- MinWidth Valor mínimo que la columna puede alcanzar. Esto limita la decisión de usuariode hacer tan pequeña como quiera la sección que se decida a redimensionar,de ahí que el valor por defecto sea 0, lo cual permite reducir a la mínimaexpresión el tamaño del rectángulo. Si no se quiere que el usuario sobrepaseunos límites mínimos será aquí donde indicar esta frontera.

- MaxWidth Valor máximo que la columna puede alcanzar. Si no se quiere que el usuariosobrepase unos límites máximo será aquí donde indicar esta frontera.

- Alignment Tipo de alineación del texto en la celda, según los valores de la tabla 1.

Valor DescripcióntaLeftJustify Alineación a la izquierdataCenter centradotaRightJustify Alineación a la derecha

Tabla 1: Tipo de justificaciones disponibles.

- Style Tipo de contenido que va a tener la cabecera de control. Puede ser un texto sinmás (entonces se fija el valor de Style a hsText, que es el valor por defecto), opuede contener cabeceras de control anidadas (en cuyo caso habría que fijar elvalor a hsOwnerDraw).

- AllowClick Permite o no que las distintas secciones de la cabecera de control respondan, ono, cuando se hace clic sobre ellas.

Como ocurriera con los PushButton, se puede especificar qué procedimiento invocar cuando seproduzca el evento OnClick. En el fuente 1 de más abajo se implementa una sencilla línea decódigo cuyo único cometido es hacer aparecer una ventana informando del hecho de que unade las secciones ha sido pulsada.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

// --- Fuente 1 ---------------------------------------------------------

procedure TDlgFiesta.HacerClicEnSeccion( HeaderControl: THeaderControl;

Section: THeaderSection );

begin

MessageDlg( 'Estoy aquí, en ' + Section.Text, mtConfirmation,

mbYesNoCancel , 0);

end;

Lo único a resaltar, por resaltar algo, es que se accede a la propiedad Text de la variableSection, que es pasada automáticamente al procedimiento cuando se realiza un clic en algunade las secciones. Al ser Section una variable de la clase THeaderSection, podemos consultar omodificar cualquiera de sus datos accesibles, y Text es el que interesa, para conocer el literalsobre el cual se pinchó.

Barra de estado

Las habituales barras de control de Windows fueron sustituidas por las más completas deWindows 95, en las que, además de disponer de un control más específico delredimensionamiento de la ventana, lo cual es accesorio, se dispone de un lugar en el que poderinformar de la cuestión que se desee. La asociación de esta zona a la ventana hace quecualquier suceso se pueda ver allí reflejado, independientemente del tipo de ventana que seesté manipulando y sin necesidad de mostrar una ventana adicional.

En la figura 3 se ofrece un aspecto tan completo como poco estético de lo que se puedemostrar en una barra de estado con formato Windows 95.

Figura 3: Tipos de Barras de estado

Si en las cabeceras de control el tipo de alineación por defecto era en la parte superior de laventana, en las barras de estado es en el lado opuesto, es decir, la variable encargada de laalineación respecto a la ventana toma un valor de AlBottom.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Tipo de barra sencilla

Son las más habituales, pues dan una información genérica no agrupable. Para especificarlahay que jugar con dos datos: SimplePanel, que deberá tener un valor verdadero, y SimpleText,donde se indicará el literal a mostrar (accesible, lógicamente, en tiempo de ejecución). Estohace que obtengamos la barra de estado de la figura 3 que lleva por leyenda "Texto sencillo".

Tipo de barra compuesta

Son menos habituales pero más completas y organizadas, pues dan información específicaque, además, podemos agrupar por el criterio que nos parezca más intuitivo. Para especificarlahay que jugar con SimplePanel, que deberá tener un valor falso, y Panels, donde se indicaránel/los distintos bloques de información (aparte de la información contenida en ellos).

En la figura 3 se distingue la existencia de tres secciones, cada una de ellas alineada respectoal habitáculo que la contiene, con un tamaño predeterminado y mostrando un aspecto realzado,hundido o plano según criterios seguidos en la fase de diseño. Este diseño se realiza en unaventana casi idéntica a la que se ha mostrado en la figura 2, en la que el único elementonovedoso sería es aspecto realzado, etc. al que hacía referencia. Este dato se encuentratipificado en la Variable Bevel, y los valores posibles se dan en la tabla 2.

Valor DescripciónpbNone Sin realzado, aspecto planopbLowered HundidopbRaised Realzado, sobresaliendo

Tabla 2: Tipo de realzados disponibles.

Otros datos de interés son las propiedades SizeGrip (para mostrar o no el triángulo de laesquina inferior derecha donde se redimensiona la ventana) y Hints (para poner etiquetasexplicativas de lo que la barra de estado hace). Esta última propiedad ya había sido comentadaen entregas anteriores, y ya se explicó que siempre iba asociada a la propiedad ShowHints quepermite o no mostrar dichas etiquetas, pero aquí, como estamos tratando un controlíntimamente ligado a la ventana, la propiedad ShowHints que hay que manipular es la de laventana, no la del control (pues éste no la posee)

Spinner

Los spinner son contadores que, normalmente, van asociados a controles de edición de tiponumérico, en los que al pinchar la flecha arriba el valor se incrementa, decrementándose alpinchar la flecha abajo (o las teclas de navegación). Digo que, normalmente, los spinner vanasociados a controles de edición de tipo numérico, pero en realidad se pueden asociar acualquier control existente en la ventana.

Las propiedades de la clase TUpDown, a la cual pertenece este control, son:

- Associate Control con el que se va a asociar el spinner. Se trata de un ComboBox en elque aparece la relación de todos los controles. Nosotros podemos asignarlo entiempo de ejecución, pero cuidado con equivocarse al hacerlo, pues provoca unerror de excepción insalvable.

- AlignButton Posición relativa respecto al control al que el spinner está asociado. Los valorespermitidos son a la derecha (udRight) o a la izquierda (udLeft).

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

- ArrowKey Permitir el uso de la flecha arriba y flecha abajo para simular la pulsación delratón sobre el spinner (será efectivo cuando el control de edición tenga el foco)o para usarlas como teclas de navegación por el control de edición. El valor pordefecto es true.

- Max/Min Valores máximos y mínimos alcazables.- Wrap Hace que la lista sea circular, es decir, que si se está pulsando la flecha arriba y

se intenta sobrepasar el tope máximo fijado, cuando Wrap esté a falsepermanecerá en dicho máximo, pero cuando esté a true seguirá aumentandodesde el mínimo fijado hacia arriba. Una lógica similar hay que aplicar cuandolo que se está haciendo es pulsar la flecha abajo en vez de la flecha arriba.

- Position Es el lugar donde se almacena el valor actual del control de edición. Esaccesible y modificable.

Teclas activas

Este control es autoexplicativo, pues se trata de la posibilidad que tenemos de activar, o mejordicho, asociar a una pulsación de tecla una cierta operativa.

Cuando se define, delimita y asigna un determinado comportamiento a este tipo de controles,se dispone, de una forma abreviada, de realizar una operación más o menos repetitivamediante un teclazo.

Propiedades

- AutoSize Permite que el tamaño del control se adapte al contenido, el cual será la teclaasociada a dicho control, por ejemplo Alt-F12. No es lo mismo ubicar F12 en elcontrol que Ctrl-Alt-W.

- BorderStyle Tipo de enmarcado que llevará el control. Puede llevar un marco sencillo (valorbsSimple) o no llevar ninguno (bsNone), son las dos únicas alternativas.

- HotKey Tecla que se asociará al control. Si pinchamos sobre el ComboBox asociado aesta propiedad en el inspector de objetos, aparecerá la relación de las teclasque se permite utilizar.

- InvalidKeys Relación de las teclas no permitidas. La relación de teclas válidas se ve filtradapor los modificadores que elijamos en esta propiedad, los cuales se encuentranrepresentados en la tabla 3.

Valor DescripciónhcNone Todas las combinaciones de teclas son válidas.hcShift La tecla Shift no está permitida en la definición de la tecla.hcCtrl La tecla Ctrl no está permitida en la definición de la tecla.hcAlt La tecla Alt no está permitida en la definición de la tecla.hcShiftCtrl La combinación de teclas Shift+Ctrl no está permitida en la

definición de la tecla.hcShiftAlt La combinación de teclas Shift+Alt no está permitida en la

definición de la tecla.hcCtrlAlt La combinación de teclas Ctrl+Alt no está permitida en la

definición de la tecla.hcShiftCtrlAlt La combinación de teclas Shift+Ctrl+Alt no está permitida en la

definición de la tecla.

En el fuente 2 se ha programado un procedimiento que provoque el cierre de la ventana y queregrese a la principal.

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

// --- Fuente 2 ---------------------------------------------------------

procedure TDlgFiesta.QueHago(Sender: TObject);

begin

Close;

end;

Bueno, aquí termino con este complicadísimo código, dejando el comentario de los controles deedición para formatos RTF (Rich Text Format -Texto enriquecido como lo traducen en Windows95) para que el lector lo compare con el de controles de edición multilínea, pues son idénticosen su estructura (heredan de una clase común), aunque el contenido del primero es, si se mepermite, mucho más rico que el del segundo. Un saludo.

1Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Delphi paso a paso (y XIII)El depurador

Por Vladimir Algara

En esta entrega, que lleva como número de orden el (y XIV), se pretende finalizar con esta seriede artículos que han llevado por nombre Delphi paso a paso. No quiero decir con esto quenunca más vuelva a tratar el tema de Delphi, ni mucho menos, pero sí que lo trataré de maneramenos sistemática y, sobre todo, no concibiéndolo como un curso en el que se van quemandoetapas y en el que se va aprendiendo, pautadamente, las características de un lenguaje.

Cada vez que, puntualmente, me surja un tema interesante que pueda serlo asimismo para ellector de mis ideas, lo expondré en este medio, pues son muchos los temas que aún quedanpor tratar.

Uno de los temas no abordados en esta serie, y que en su concepción resultaba inimaginableno hacerlo así, es la gestión de bases de datos; entendiéndolos en su manera de explotarlos enfondo y forma. Gracias a que existen otros articulistas que han compartido conmigo lasbondades de Delphi, creo que el acercamiento que yo hubiera hecho a tales temas ha quedadomás que resuelto con sus respectivos razonamientos, lo cual me ha llevado a hacer esterazonamiento, ajeno a lo que será el contenido de las líneas que nos ocupan, y llegar a laconclusión de que es mucho mejor abandonar la forma reglada de explicar Delphi y tomar comoregla la explicación esporádica de los casos interesantes.

En resumen, que el y XIV del título significa que el curso lo doy por concluido en su concepto,pero no que vaya a abandonar el lenguaje Delphi por otro mejor postor, sólo voy a deshacer mimonogamia informática arrastrada en los últimos meses.

Bueno, después de toda esta retahíla de nosequé, voy a entrar en el meollo del artículo, que noes otro que el uso y abuso del depurador de Delphi para corrección y seguimiento de errores enlas aplicaciones diseñadas en el lenguaje de Borland.

Tipos de errores

Todo el mundo sabe a qué tipos de errores nos enfrentamos a la hora de diseñar una aplicacióncon cualquier herramienta, los peores de todos suelen encontrarse en la elección del analistaque ha de perfilar el coherente funcionamiento del programa.

Bromas aparte, los errores se clasifican históricamente en:

- Errores de compilación: Delphi posee un potente compilador/enlazador que, una vezentendida su idiosincrasia, permite fijar con total exactitud el/los puntos conflictivos delcódigo erróneo. Estos errores consisten, esencialmente, en fallos sintácticos, del tipo faltapunto y coma, inconsistencia entre tipos de variables, sentencias mal construidas,estructuras de control inacabadas, etc., etc., etc.

- Errores de enlazado. Superada la fase de compilación llega la de enlazado, en la que seespecifica que elementos, ajenos a nuestro código compilable, se van a usar para elcorrecto funcionamiento de la aplicación. Es la parte en la que se han de reseñar losmódulos OBJ, librerías y DLLs, y la ausencia de alguna de ellas provocará la queja deDelphi.

- Errores de ejecución. Los hay, a su vez, de dos tipos, y la forma de clasificarlos se limita ala dificultad de corregirlos. Los más evidentes son los denominados errores Run-Time, loscuales originan una ruptura del buen funcionamiento de la aplicación y una salida de lamisma. Dado lo acotado del error su localización es fácil, aunque su corrección puede noserlo tanto (consisten en especificaciones de archivos que no existen y que se quierenutilizar, divisiones por cero, la obtención de la raíz cuadrada de un número negativo, etc.);

2Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

los menos evidentes son los llamados errores lógicos y consisten en un funcionamientoanómalo que, en el peor de los casos, pueden dar con nuestros huesos en el Psiquiátricoasignado a nuestra Área Sanitaria.

En lo que a mi experiencia personal se refiere, el error lógico que más tiempo me ha llevadodescubrir fue uno en el que se juntaron varios factores:

1.- No utilizaba la notación Word Mixing en la declaración de mis variables.

2.- El programa estaba hecho en un Pascal arcaico sobre una máquina arcaica (no disponíade depurador)

3.- No tenía la experiencia suficiente para enfrentarme a lo que debía resolver (vamos, que notenía ni idea)

El resultado de estas tres variables derivaron en que el programa se tiró sin funcionar más deuna semana, todo por culpa de una línea en la que se utilizaba un contador de manerainadecuada:

I := 1 + 1

En la que realmente debería haber puesto, como todo el mundo alcanza a comprender:

I := I + 1

Este error siempre lo oculté y aquí y ahora me sincero con todos.

En fin, experiencias personales aparte, cualquiera de los dos tipos de errores que se puedendar en tiempo de ejecución los vamos a resolver (o lo intentaremos) por medio del depurador deDelphi.

Ventajas y desventajas

Antes de pasar a explicar cómo manejar el depurador, debo hacer la advertencia que siemprese hace cuando se habla de este tipo de utilitarios, y que gira en torno a los recursos que estosutilitarios consumen. No es lo mismo entregar un ejecutable final con la información necesariapara ser depurado que sin ella. En tiempo de desarrollo podemos permitirnos el lujo deintroducir dicha información dentro de nuestros ejecutables, pero una vez acabados ydepurados habremos de eliminarla. Por una parte impediremos que el ejecutable seainspeccionado por ojos espías, y, por otra, reduciremos su tamaño final (hay que tener presenteque Delphi añade la información necesaria en el archivo .DCU asociado a la aplicación, y que lavuelve a eliminar cuando no se desea usar el debugger) y aumentaremos la velocidad de losprocesos.

Estableciendo que vamos a depurar el programa, sepamos de qué ventajas inmediatas vamosa disponer:

1. La primera, y principal, la de poder recorrer e inspeccionar el código que se estáejecutando en el momento que el programa responde a nuestras especificaciones o aestímulos exteriores.

2.- Cambiar el contenido de las variables que influyen en la forma de trabajar de undeterminado proceso.

3Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

3.- Provocar la parada del programa a tenor del contenido de una o más variables dereferencia. O de la conjunción de todas ellas.

4.- Alterar el valor asociado a los campos de las bases de datos

Configuración del depurador

El depurador se configura genéricamente en el menú Tools, en el ítem Options... Desde élaccedemos a la ventana de configuración general Environment Options en la que, entre otras,se encuentra la pestaña Preferences.

Dado que existen distintos aspectos que podemos tocar en lo que a preferencias del entorno serefiere, dentro de la pestaña Preferences existen otras subdivisiones correspondientes a sendasfacetas configurables. La que a nosotros nos interesa se encuentra en el GroupBox reservadoal Debugging que se ve en la figura 1.

Figura 1: Ventana para la configuración del depurador

Los valores que se pueden fijar en este cuadro de grupo son, como se ve en la figura de másarriba, los siguientes:

1.- Integrated Debugging: Es la piedra angular de todo este berenjenal. Cuando está marcado(valor por defecto) se dispone de depurador para las aplicaciones, si no, no.

2.- Step program block: Ejecuta el programa usando el depurador desde el principio de laaplicación, o a partir del módulo que contenga la información necesaria como para serreconocido como bloque de comienzo (un punto de ruptura, por ejemplo). Estos dos puntosestán íntimamente ligados al depurador, los otros tres ya no tanto, pero como se englobandentro del mismo GroupBox los explicaré.

4Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

3.- Hide designer on run: Hace que el inspector de objetos y el lugar donde se diseñan lasdistintas ventanas desaparezcan, a la hora de ejecutar una aplicación.

4.- Break on exception: Habilita o no la salida de la aplicación cuando se produce un error detipo Run-Time. El valor por defecto de este Check es marcado, lo cual significa que cuandoocurra un error aparecerá la ventana de la figura 2 y la aplicación acabará. Si no estámarcado aparecerá una caja diferente a la anterior (la de la figura 3), que también nosinforma de que ha sucedido un error, pero permite que el programa siga funcionando.

Por ejemplo, en la porción de código de más abajo, se fuerza la aparición de un error. Sepretende leer el contenido de un archivo que no existe, lo cual provoca un error deexcepción que, en un caso nos echa y en otro no.

nCapitulo := TabCapitulos.TabIndex;

case nCapitulo of

0 : Memo1.Lines.LoadFromFile('KK.TXT'); // Error, no existe el

archivo

1 : Memo1.Lines.LoadFromFile('CAP2.TXT');

2 : Memo1.Lines.LoadFromFile('CAP3.TXT');

3 : Memo1.Lines.LoadFromFile('CAP4.TXT');

end;

Figura 2: Ventana informativa y el programa deja de ejecutarse

Figura 3: Ventana informativa y el programa continúa su normal ejecución

Cuando no marcamos la opción de disponer de depurador (punto 1.-), nunca aparecerá laventana de la figura 2, siempre saldrá la de la 3.

5.- Minimize on run: Hace que Delphi se minimice al ejecutar la aplicación.

Puesta en marcha

Para hacer funcionar el depurador ha de hacerse desde dentro de Delphi. Una vez funcionandopodemos acceder a la parte del código que se quiere inspeccionar y allí poner una marca que

5Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

indique a la utilidad que debe parar la ejecución del programa. Estos puntos son los llamadosBreakPoints (Puntos de ruptura) y son la forma más habitual de trabajar con el depurador.

De cualquier forma, esta no es la única manera que tenemos de comunicarnos con eldepurador, existen otras y todas ellas se engloban en el menú Run, del cual se ha hecho unextracto en la figura 4.

Figura 4: Operaciones con el depurador

1.- Run: Ejecuta la aplicación sin pausa, hasta que encuentra un Punto de Ruptura o se pulsala combinación de teclas (varias veces) [Ctrl] + [Alt] +[Pet Sis].

2.- Parameters: Permite pasar parámetros a la aplicación.

3.- Step Over: Ejecuta una línea de la aplicación. Si la línea consiste en una llamada a unafunción o a un procedimiento, ésta se realiza de una sola vez, sin entrar en el código quehaya en su interior.

4.- Trace Into: Ejecuta, como el anterior, una línea de la aplicación, pero, en este caso, si lalínea es una llamada a función o a procedimiento, se recorre el código contenido en ellos.

5.- Run to Cursor: Dado que una vez que se ejecuta un programa se puede uno moverlibremente por el código que lo guía, podemos acceder a una línea sospechosa decontener error y elegir esta opción, lo cual hará que la aplicación se desarrolle libremente,sin trabas, hasta el momento que se alcance la línea escogida.

6.- Show Execution Point: Muestra la línea de programa por la que se va. Esto puede ser útil sinos hemos movido por otras porciones de código fuente o hemos minimizado la ventanaque contiene el código ejecutable.

7.- Program Pause. Detiene, momentáneamente, la ejecución de la aplicación.

8.- Program Reset. Vuelve a ejecutar, desde el principio, la aplicación en curso.

Puntos de Ruptura

Los Puntos de Ruptura son las herramientas más usadas en la interacción con el depurador. EnDelphi hay varias formas de especificar uno de estos Puntos de Ruptura.

6Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

La forma de hacerlo es ir a una línea "sospechosa" de contener error y convertirla en un Puntode Ruptura. Esta operativa se puede realizar cuantas veces se desee.

El resultado final puede ser algo similar a lo que se ve en la figura 5, en la que existen trespuntos de ruptura, uno de ellos inhabilitado (color verde) y dos no (color rojo, azul para eldaltónico de Alfonso ).

Figura 5: Puntos de Ruptura en el código de ejemplo

Para poner un Punto de Ruptura (o quitarlo) basta con realizar alguna de las accionessiguientes sobre la línea elegida.

- Pinchar con el botón principal del ratón en la parte izquierda del editor de código.

- Pulsar la tecla [F5].

- Pinchar con el botón auxiliar del ratón para que aparezca el menú local y elegir la opciónToggle BreakPoint.

- Acceder, por medio del menú de Run, al ítem Add BreakPoint; lo cual hará que aparezca laventana de la figura 6. Allí, como se puede ver, se le indica la unidad donde está la línea aañadir (por defecto en el que nos encontramos), el número de línea de programa (pordefecto en la que nos encontramos), una condición y un número de ejecuciones.

7Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 6: Puntos de Ruptura desde la ventana de configuración

Al especificar algo en Condition, el Punto de Ruptura sólo será válido cuando ese "algo"devuelva un valor verdadero; por ejemplo, se podría poner:

nCapitulo >= 2

Para que sólo entrara cuando se pinche sobre las pestañas 3 y 4.

Al especificar un número en Pass Count, el Punto de Ruptura sólo será válido cuando lalínea que lo tiene haya sido leída ese número de veces.; por ejemplo, se podría poner un 3,lo cual permitiría pasar dos veces por la línea definida sin que el Punto de Ruptura saltase.

A la ventana de la figura 6 se puede llegar desde la ventana de la figura 7, en la que semuestran todos los Puntos de Ruptura definidos hasta el momento. Esta ventana, apartede ofrecernos una relación detallada de ellos, nos permite modificar el contenido de todoso parte de ellos (haciendo doble clic sobre alguno o pinchando con el botón auxiliar delratón) y variarlo de acuerdo a nuestros propósitos. A la ventana de la figura 7 se llega pormedio del menú de View, a través del ítem BreakPoints.

Figura 7: Relación de los Puntos de Ruptura

Puntos de Observación

Los Puntos de Observación son otras de las herramientas más usadas en la consulta de losdatos con el depurador. Al contrario que los Punto de Ruptura, éstos no detienen la normalejecución del programa, sino que sólo muestran el contenido de una variable, un conjunto deellas o una expresión compleja.

Para poner un Punto de Observación (o quitarlo) basta con acceder, por medio del menú deRun, al ítem Add Watch...; lo cual hará que aparezca la ventana de la figura 8. Allí, como sepuede ver, se le indica la expresión que se desea ver, un número de ejecuciones a partir de lacual queremos que se haga efectiva la visualización, el número de dígitos que se van a usar endicha visualización y si se puede ver o no (el valor por defecto de Enabled es estar marcado, osea, que se pueda ver, pero en algún punto del programa nos puede interesar eliminar algunode los Puntos de Observación definidos). Los distintos RadioButtons son autoexplicativos.

8Algoritmo . La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS

Figura 8: Puntos de Ruptura desde la ventana de configuración

Identif. Tipo afectado Descripción

,C Caracteres Muestra los caracteres correspondientes a los ASCCIcomprendidos entre el 0 y el 31. De no especificarlo, dichoscaracteres se interpretan como secuencia de escape del tipo \n, \t,etc.

,S Caracteres Muestra los caracteres ASCCI comprendidos entre el 0 y el 31como secuencia de escape.

,D Enteros Muestra los valores enteros en formato decimal, incluidos loscampos de las bases de datos tratadas.

,H o ,X Enteros Muestra los valores enteros en formato hexadecimal, añadiendo elprefijo 0x, incluidos los campos de las bases de datos tratadas.

,P Punteros Muestra información de la dirección ocupada por los punteros(segmento:offset). Esto permite identificar, de manera indirecta, porejemplo, qué variable está causando un determinado problema.

,R Registros,clases, objetos

Ofrece información específica de los distintos valores almacenadosen las variables de una clase, para una instaciación concreta de unobjeto concreto.

,nM Cualquiera Visualiza n bytes de memoria, comenzando en la dirección de laexpresión reseñada. Una especificación del tipo 5M, mostraráinformación de los cinco primeros bytes de la variable en cuestión.

Tabla 1: Modificadores de formato.

Bueno, aquí me despido de la serie y de todo aquel que me quiera oír. En esta entrega no seacompaña con ningún tipo de ejemplo, pues provocar errores en un programa es la cosa mássencilla del mundo, lo que es más complicado es quitárselos de encima.

Lo dicho, se cierra la serie, pero no la investigación en Delphi.