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 seguir completando los datos de la original. Partiendo del ejemplo del número anterior, en el que se manipulaban un montón de ListBox y al final 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 que filtre el contenido del ListBox de archivos, etc., y la segunda dejarla, exclusivamente, para la visualizació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 de la 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 del mes pasado, pues, básicamente, lo único que se ha hecho con ella ha sido quitarle controles, y los que han sobrevivido quedaron suficientemente explicados allí. La de la figura 2, si bien posee el mismo control para visualizar la imagen, ha crecido en su casuística, pues ha pasado a tener en cuenta qué archivo gráfico se ve, si hay otros más susceptibles de mostrarse, si se quiere asignar un tamaño determinado o si se quiere ajustar la imagen al recinto definido (bien expandiendo, bien reduciendo el BMP). 1 Algoritmo. 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. Ambas almacenan 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 es más pequeña que el recinto, ésta se agrandará, agregando pixeles al original para que ocupe totalmente la superficie, y si es más grande que el recinto se eliminarán pixeles hasta que quepa. La distorsión de la imagen se ve tanto más acentuada cuanto menos se parezcan las proporciones de la imagen al recinto predefinido. - AutoSize (tamaño automático): Permite supeditar el recinto fijado al tamaño real de la imagen, de tal manera que si el archivo BMP es menor o mayor que la pantalla de visualización (cosa bastante habitual), el control que alberga la imagen se queda con el nuevo tamaño. Esto es así mientras dure la sesión en la que estemos trabajando y, dado que en nuestro recinto vamos a tener que visualizar cualquier cantidad de imágenes, será bueno almacenar las coordenadas y dimensiones originales para poder restaurarlas cuando lo creamos necesario. Además, y dado que vamos a jugar con las coordenadas del BMP, se habilita un conjunto de botones de radio (RadioButtons) para ajustar (hacer Stretch), visualizar al tamaño real (no hacer Stretch) o personalizar el tamaño del BMP. Si lo que se pretende es definir las dimensiones de la imagen, es lógico pensar en dos controles de edición donde indicar el ancho y el alto (estos controles deberán estar habilitados cuando queramos personalizar, y deshabilitados en el resto de los casos. Situaciones Antes de pasar a examinar las características de cada uno de los elementos que completarán el ejemplo, paso a enumerar los casos que nos vamos a encontrar, según la operación que se desee 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 2 Algoritmo. 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 el BMP 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 exceda del habitáculo predefinido. - Para BMPs más grandes que el recinto de visualización. En este caso ajustar no fuerza el redimensionamiento, pero sí la vista a tamaño original, que forzará a que existan barras de desplazamiento para recorrer el BMP en su totalidad. Por otra parte, la lógica a seguir cuando 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 de las de la tabla 1. En dicha tabla también puede verse en qué manera afecta a los estados de dos de las propiedades más importantes de una imagen, la propiedad Stretch y la propiedad AutoSize. Tipo Ajustado BMP pequeño Sí BMP pequeño No BMP pequeño No BMP grande Sí BMP grande No BMP grande No Tabla 1: Estado de Stretch y Tanaño Original Personalizado No No Sí No No Sí No No Sí No No Sí AutoSize según aspecto de BMP. Stretch true false false true false false AutoSize false false true false true true Separación de bienes Una vez vistas las situaciones posibles, tomemos el ejemplo del número anterior y dividámoslo en 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 sabe hasta 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 herramientas destinada 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 las cuales se elegirá la más sencilla de todas, la de tipo form, tal y como se indica en la figura 3. 3 Algoritmo. 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 el código asociado a las ventanas actuales. En él se puede observar que cada una de las ventanas tiene una carpeta en la que se almacena su propio código, cuando pinchemos sobre la carpeta Unit1 estaremos accediendo al form1, cuando lo hagamos sobre la carpeta Unit2 estaremos accediendo al form2, y así sucesivamente para el resto de ventanas y sus unidades asociadas a ellas. En las figuras 4 y 5 se ilustra lo que quiero decir,. En la parte de la izquierda de cada figura se ve el editor de código fuente, donde una de 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) 4 Algoritmo. 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 en este caso), que es LB4, marcar los controles deseados y llevarlos al portapapeles; cambiamos de ventana, activando aquélla que va a recibir los controles almacenados en el portapapeles y nos los traemos desde allí. Nota: para cambiar entre código fuente basta con pinchar en la carpeta deseada. Para visualizar la ventana asociada al código elegido se hace mediante el oportuno icono de la barra de herramientas, 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 hemos venido predicando. Ahora basta dejarla bonita redimensionando los controles al tamaño que mejor 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 del grupo, mutuamente excluyentes entre sí. Existe en la carpeta Standard la posibilidad de elegir RadioButton individuales, pero cuando queremos agruparlos de manera lógica, lo más eficaz es recurrir a este otro tipo de agrupación. El RadioButton Group se encuentra en la carpeta Standard y los iconos que los representan (no seleccionado y seleccionado) son los de la figura 6. Figura 6: Iconos para RadioButton Group 5 Algoritmo. 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 vistos por 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 indica qué tipo de asociación se ha hecho para el RadioButton Group. En nuestro caso se ha puesto la descripción: Presentación Columns. Cantidad de columnas dentro de la agrupación. Dado que podemos especificar un número arbitrario de elementos, esta propiedad fija la forma en que se van a presentar en pantalla. Lo habitual es que haya una sola columna y que los elementos se presenten como en la figura 2, pero si hubiésemos querido que su presentación fuera horizontal hubiera valido con asignar un 3 al dato Column (nuestro ejemplo tiene tres RadioButton dentro del grupo). ItemIndex. Una agrupación siempre tendrá dos o más elementos, de los cuales uno será el que esté inicialmente marcado (y desmarcado el resto). Sabiendo que estos controles se rellenan siguiendo la misma lógica que en los ListBox, el primero de los RadioButtons se reconoce por el índice 0, el segundo por el 1 y así sucesivamente. En nuestro ejemplo partimos de que queremos ver el BMP a tamaño real (segunda posición) por lo que en la propiedad ItemIndex ponemos un 1. Items. Elementos con los que el RadioButton Group será rellenado. Este dato alberga un objeto de la clase TString por lo que disponemos de la posibilidad de ir añadiendo en forma de 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 (no seleccionado 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 de edición cada vez que éste recibe el foco. En nuestro ejemplo se ha cambiado a false, pues el valor por defecto es true. AutoSize. Propiedad que permite redimensionar el control de edición a medida que su contenido 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 y ecLowerCase minúsculas. EditMask. Tipo de máscara que se va a utilizar. Son muchas las máscaras disponibles, y su misión es forzar la escritura como al programador le convenga. Por ejemplo, se podría forzar a escribir siempre números, o con un formato genérico para números de teléfono o 6 Algoritmo. 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 se escriba otra cosa que no sean dígitos (además, se impide que el número exceda de tres cifras). Para eso hay que asociar a EditMask la máscara 999 y a MaxLenght el valor 3. Las posibles máscaras se definen o se eligen en la ventana de la figura 8, la cual aparece tras pinchar 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 los controles de edición X e Y. PasswordChar. Carácter que se utiliza para encriptar lo que se escribe. En nuestro caso no se 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 clic sobre el ListBox (habremos de asociar el código del fuente 1 tanto al evento OnClick del botó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; 7 Algoritmo. 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 la ventana 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 propiedad Name, y que Delphi se ha encargado de especificar como variable a través del código generado 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 por qué), para terminar por cargar la imagen (mediante el método LoadFromFile) en el recinto destinado 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. En definitiva, en el apartado uses hay que añadir, a la lista que Delphi incorpora automáticamente, el literal VerBMP. uses Windows, Messages, . . . VerBMP; 4.- Sopesando los pasos dados, se puede asegurar que lo menos intuitivo es lo de especificar en el apartado uses la unidad que se quiere utilizar, pero explicado el concepto, parece 8 Algoritmo. 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 desde todos los sitios, de ahí que haya que fijarlo ese sitio y esas ventanas allí donde se necesiten, todo ello de acuerdo al diseño dado a la aplicación y de las necesidades a resolver. Una vez que vemos que todo funciona correctamente hasta el paso 3 (se puede compilar y ejecutar el ejemplo), podemos empezar a utilizar las nuevas funcionalidades conseguidas con los controles estudiados más arriba, de ahí que ubiquemos un RadioButton Group y un MaskEdit. 5.- Determinamos qué hacer cuando se elija alguno de los RadioButton, de acuerdo al evento OnClick de este tipo de controles. En el fuente 3 se ve el código usado para ajustar la imagen, 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 como parámetro el ítem que ha sido pulsado en el RadioButton Group. Después se discierne, en una 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 y para 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, los literales y la caja que los abarca deberían estar inhabilitados, pasando a estar disponibles solamente cuando se elija el ítem 3. Al tratarse de un código que se repite para cada operación se ha optado por aislarlo en un procedimiento específico. En el fuente 4, y de acuerdo 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ía ensayar algo como lo que sigue: if (nPos = 0) or (nPos = 1) then begin GBCoord.Enabled := false; LabelX.Enabled := false; LabelY.Enabled := false; X.Enabled := false; Y.Enabled := false; end else begin GBCoord.Enabled := false; 9 Algoritmo. 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 y ser esa variable la que se encargue de modificar el contenido del dato Enabled. if (nPos = 0) or (nPos = 1) then lValor := false else 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 al RadioButton elegido, así como el comportamiento de Stretch y AutoSize en los distintos casos. Aún nos queda tomar como buenos los datos que se introduzcan en los controles de edició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. El primero consiste en introducir los valores y, una vez que se consideren aceptables pulsar un botón que actualice lo que se acaba de teclear. El segundo, más funcional, consiste en actualizar el alto y ancho a medida que las nuevas dimensiones están siendo introducidas en los controles de edición. Por ejemplo, si queremos asignar unos valores de 300x459, a medida que escribamos se 10 Algoritmo. 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, de 45 y de 459 al alto. Eso, efectivamente, es más funcional, pero puede darse el caso que un gráfico tenga unas dimensiones de 0x0, lo cual no es representable. Por lo tanto, y para llevar a cabo este redimensionamiento dinámico, protegeremos los valores muy pequeños y sólo empezaremos a mostrar la imagen a partir de un rango mínimo (que en el ejemplo del fuente 5 se ha fijado en 20). El fuente 5, para que sea invocado a cada pulsación en los controles de edición, habrá de especificarse 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 medio del dato Text de dicho control. Por lo tanto, para saber qué dato tiene almacenado el control 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 a un 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érica pasada por referencia y donde se recoge el valor convertido y nCod un código de error que se 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 (datos Width y Height, respectivamente) y alteramos AutoSize para que pase a valer true, pero sólo cuando ambos controles superen las 20 unidades; por lo que la imagen más pequeña será 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, 11 Algoritmo. 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 como es en realidad no podamos dar marcha atrás. La solución pasa por recordar en alguna variable 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 podemos acceder o añadir datos públicos a esa clase (podía haberse hecho en los privados, pues cualquier referencia a ellos se hace desde métodos de la propia clase). Como se ve en el fuente 6, se ha hecho necesario guardar, además, una copia de los ítems del ListBox de archivos y del ítem elegido (estos dos sí han de ser públicos, pues se accede a ellos desde fuera 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 el valor. Pueden ser varios los sitios donde hacerlo, aunque sí parece lógico que la asignación se haga una sola vez. Para resolver esta cuestión se va a recurrir a los eventos contemplados para la ventana. Los eventos que se pueden interceptar son los de movimiento, redimensionado, activación, desactivación de ventana, etc. El que en definitiva nos va a valer para nuestra operativa será el de creación de la ventana, identificado con OnCreate. 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 12 Algoritmo. 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 se ve al aparecer la segunda ventana, y en el alto original (YOrig) almacenamos el alto del control (Height). 7.- Para terminar, y siempre con el afán de dar más funcionalidad al ejemplo que nos ocupa, se ha implementado una rutina que nos permite recorrer todos los archivos que estén almacenados en el ListBox de archivos. De nuevo nos encontramos ante dos posibles soluciones al problema. Por un lado está la ubicación de dos botones que permitan avanzar y retroceder a través de la lista, por otro se pueden utilizar el botón derecho e izquierdo del ratón para ir uno hacia adelante o uno hacia atrás, respectivamente. Este último, aparte de no saturar la ventana con controles, nos permite introducirnos en una nueva operativa, en la que va a entrar en juego las pulsaciones en los botones del ratón. Como siempre, habrá que determinar cuál es el evento que queremos interceptar, y luego programarlo. Las pulsaciones del ratón que queremos procesar, a priori, parece lógico que sean las que se efectúan dentro de la imagen, ignorando aquéllas que se hagan en cualquier otro control. Si ahora consultásemos los posibles eventos relacionados con el ratón, aparecería una lista como la de abajo. OnClick OnDblClick OnMouseDown OnMouseMove OnMouseUp Los dos primeros (OnClick y OnDblClick) los descartamos, pues sólo controlan que se pulse y se suelte el botón principal (una o dos veces). OnMouseMove está pensado para la gestión de cualquier movimiento del ratón. Por último OnMouseUp y OnMouseDown nos informan 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, basta con 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. Como siempre, Sender es el control que ha llamado al procedimiento y se utiliza cuando dicho procedimiento es común a más de un control. La variable Button es la encargada de almacenar el botón pulsado, pudiendo consultar su contenido según las constantes predefinidas (mbLeft, mbRight y mbMiddle). La variable Shift guarda información acerca de la tecla auxiliar presionada en el teclado, esto es, [Shift], [Alt] y/o [Ctrl], también de acuerdo 13 Algoritmo. 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 e Y son las coordenadas de pantalla donde se pulsó o se soltó el botón del ratón. De todas ellas nos bastará con manipular el dato Button, aunque igualmente podríamos haber involucrado 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 la operació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ón izquierdo 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 sobrepasar el límite inferior (0) ni el superior (número total de elementos del ListBox de archivos menos 1). Sabiendo ya el nuevo valor de nPosicion, capturamos el elemento que se corresponde en 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, por medio del método LoadFromFile() del control Imagen. Ese será el código que asociaremos al evento OnMouseUp, pero hay que decir que igualmente se lo podemos asociar a OnMouseDown. Un saludo. 14 Algoritmo. La revista para el programador de sistemas de bases de datos. http://www.eidos.es - © Grupo EIDOS