Browses elásticos

En Xailer, cuando usamos la propiedad nAlign o nAnchors a un browse, éste se ajusta dinámicamente a las dimensiones del formulario, pero no pasa lo mismo con su contenido, las columnas del browse.

Nunca me ha gustado mucho este comportamiento, así que tenía dos opciones: o quejarme amargamente porque no funcionaba como yo quería, o remangarme y cambiar su funcionamiento.

Y.. bueno… digamos que me quejo poco.

Este artículo está dividido en dos partes.

La primera está destinada para los que yo llamo pintores: aquellos que buscan nuevas mejoras para aplicar a sus programas y quieren aprender a utilizarlas, pero les dá igual cómo funcionan por dentro.

La segunda parte está destinada a los fabricantes de pinceles, que son los que están más interesados en cómo se construye la herramienta que en su uso definitivo.

Funcionamiento de los browses elásticos.

Lo primero será añadir un ArrayBrowse a nuestro formulario y cambiar la propiedad nAnchors a akTOPLEFTRIGHT.

Luego, completaremos es ArrayBrowse añadiendo las columnas necesarias.

Ya sólo nos queda añadir un puñado de líneas al dentro del programa

Nos creamos dos nuevos métodos Cargadatos() y ArrayBrowse1onsize() dentro de la definición de la clase

CLASS TForm1 FROM TForm

   COMPONENT oArrayBrowse1
   COMPONENT oArrayBrowse2
   COMPONENT oArrayBrowse1Column5
   COMPONENT oLabel1
   COMPONENT oLabel2

   METHOD CreateForm()
   METHOD FormShow( oSender )
   METHOD ArrayBrowse1onsize( oSender )
   METHOD ArrayBrowse1Create( oSender )
   METHOD Cargadatos()

ENDCLASS

En el evento OnCreate del ArrayBrowse indicaremos lo siguiente

oSender:aCols[2]:nPorcien := 25
oSender:aCols[3]:nPorcien := 50
oSender:aCols[4]:nPorcien := 25
//                          -------
//                          100%

Donde le estamos diciendo al browse el % de la anchura de cada colunma elástica.

Las columnas 1 y 5, son de anchura fija, por lo cual no hace falta modificar la propiedad nPorcien

Ahora le definimos a la columna 2 una anchura mínima de 200 pixel. Si al cambiar de tamaño el browse, la columna 2 tiene una anchura menor de 200, la ocultará automáticamente.

oSender:aCols[2]:nMinlen := 200

Por último, alimentamos el valor del evento OnSize del browse para que se ejecute en método ArrayBrowse1onsize

oSender:OnSize := "ArrayBrowse1onsize"

En ArrayBrowse1onsize nos limitamos a llamar al método Pintar() del ArrayBrowse, que es el encargado de recalcular el tamaño de las columnas y mostrarlas dentro del browse

METHOD ArrayBrowse1onsize( oSender ) CLASS TForm1
   oSender:Pintar( oSender )
retu nil

Por último, en el evento FormShow del formulario llamamos a CargaDatos(), para que rellene los valores del ArrayBrowse al mostrar el formulario

METHOD FormShow( oSender ) CLASS TForm1
   ::CargaDatos()
RETURN Nil

Listo!, dale al play y a funcionar.

Si eres del tipo de los pintores, esto es todo amigo, ya hemos terminado.

Si eres de los fabricantes de pinceles, continúa leyendo, que te interesa lo que viene.

Para lograr nuestro efecto «elástico» en el ArrayBrowse, no he hecho otra cosa que jugar con dos de las clases de Xailer: TBrwColumn y TArrayBrowse.

Modificaciones a TBrwColumn

CLASS TBrwColumn FROM XBrwColumn
PUBLISHED:
   PROPERTY nPorcien     INIT 0
   PROPERTY nMinLen      INIT 0
PROTECTED:
   DATA nInitWidth
PUBLIC:
   METHOD Create( oParent ) CONSTRUCTOR
ENDCLASS

Le he añadido 3 propiedades nuevas

nPorcien: % que tiene que ocupar cada una de las columnas elásticas dentro del browse. Las columnas con nPorcien=0 se comportan como las columnas de toda la vida

nMinLen: Tamaño mínimo de la columna a partir del cual la ocultaremos.

nInitWidth: Tamaño inicial de la columna

La propiedad nInitWidth la usa internamente el programa y guarda el tamaño inicial de cada columna. Es muy importante para el correcto funcionamiento de la clase que este dato no se pueda modificar desde fuera de la definición de la clase, por eso la hemos declarado como DATA Y PROTECTED.

Así, si intentamos hacer ::oArraybrowse1:aCols[4]:ninitWidth := 25 desde nuestros programas, Xailer nos dará un error.

Para poder guardar el valor de nInitWidth tenemos que sobrecargar el evento OnCreate de la clase, para ello, nos creamos nuestro propio método Create modificado

METHOD Create( oParent ) CLASS TBrwColumn
   super:Create( oParent )
   ::nInitWidth := ::nWidth
RETURN Self

Lo primero que hacemos es super:Create( oParent ), o sea, ejecutar el create de la clase original (XBrwColumn) para que genere el objeto correspondiente a cada columna.

Una vez creada la columna, guardamos el valor que tiene nWidth en ese momento, cuando aún no hemos empezado a jugar con la anchura de la columna

La segunda clase que he modificado es TArrayBrowse

Al igual que a TBrwColumn, le he añadido la propiedad  nInitWidth, que guardará el tamaño original del browse.

CLASS TArrayBrowse FROM XArrayBrowse
PROTECTED:
   DATA ninitwidth
PUBLIC:
   METHOD Create( oParent ) CONSTRUCTOR
   METHOD WMSize( nWParam, nLParam )
   METHOD Pintar( oSender )
   METHOD Porcion( nPixels, nPorcien )
   EVENT OnSize( oSender, nSizeType, nClientWidth, nClientHeight )
ENDCLASS

Además, he sobrecargado el método Create, he añadido dos métodos nuevos (pintar y porción) y he creado el evento OnSize, que inicialmente no existe en la clase TArrayBrowse.

Vamos a empezar por esto último, que personalmente es lo que más me ha llamado la atención.

Como ya hemos visto, en la definición de la clase preparamos el método WMSize y el evento OnSize

Luego, en el desarrollo de la clase en sí, basta con poner:

METHOD WMSize( nWParam, nLParam ) CLASS TArrayBrowse
   ::OnSize( nWParam, LoWord( nLParam ), HiWord( nLParam )  )
RETURN Super:WMSize( nWParam, nLParam ) )

Y listo. No explico más de este método porque tamposo sé mucho más, ha sido Ignacio el que me ha dicho cómo hacerlo. Yo me he limitado a copiar y pegar.

El segundo paso es sobrecargar el método create.

METHOD Create( oParent ) CLASS TArrayBrowse
   super:Create( oParent )
   ::nInitWidth := ::nWidth
   ::Pintar( Self )
RETURN Self

Al igual que hemos hecho antes en TBrwColumn, llamamos al create original, cargamos nInitWidth y, en este caso, llamamos al método pintar() para que nos dibuje por primera el browse elástico.

De los dos métodos que nos quedan, porcion() se encarga de calcular y traducir el ancho de la columna de porcentaje a pixels

METHOD Porcion( nPixels, nPorcien ) class TArrayBrowse
   RETURN nPixels * ( nPorcien / 100 )

Pintar() contine el corazon de la modificacion.

METHOD Pintar( osender ) CLASS TArrayBrowse
Local nColumnas := Len( oSender:aCols )
Local nResto := oSender:nWidth - oSender:nInitWidth
Local nPorcienOcultas := nAnchoOcultas := nColVisibles := 0
Local n,a

nColumnas: cuántas columnas tiene el browse
nResto: El tamaño actura del browse menos el tamaño inicial, esto es, los pixels que nos sobran del browse y que hay que rellenar.
nPorcienOcultas: En caso de que una columna esté oculta, el % de la anchura de la misma, que luego tendremos que repartir entre las columnas visibles.
nAnchoOcultas: suma de la anchura inicial de las columnas ocultas.
nColVisibles : Número de columnas elásticas dentro del browse

Lo primero es saber cuántas columnas tenemos que mostrar y de qué anchura.

Para ello:

     Nos metemos en un bucle por cada una de las columnas
         Si es una columna elástica (nPorcien > 0)
            A la anchura inicial de la columna le sumamos los pixels
            correspondientes al % que ocupa la columna de la parte
            que nos sobra del browse (nResto)
           Si el resultado es menor que el nMinLen de la columna,
           la ocultamos.
                   Acumulamos la anchura y el porcentaje de la columna
                   oculta.como no se muestra,luego tendremos que
                   repartir estos valores entre las columnas visibles
            Si la columna es visible
                   Incrementamos el contador de columnas visibles
  FOR n := 1 TO nColumnas
      IF oSender:aCols[n]:nPorcien > 0
         a := oSender:aCols[n]:nInitWidth + ;
               oSender:Porcion( nResto, oSender:aCols[n]:nPorcien )
         IF !oSender:aCols[n]:lVisible := a > oSender:aCols[n]:nMinLen
            nAnchoOcultas += oSender:aCols[n]:nInitWidth
            nPorcienOcultas += oSender:aCols[n]:nPorcien
         ELSE
            nColVisibles ++
         ENDIF
      ENDIF
   NEXT

Ahora ya sabemos:

-Cuántas columnas hay visibles ( nColVisibles)
-La suma del tamaño en pixels y el porcentaje que ocupan las columnas ocultas (nAnchoOcultas y nPorcienOcultas)

Nuestro siguiente paso será repartir el % que ocupan las columnas ocultas entre las columnas que son visibles, para que las columnas visibles ocupen el 100% del browse

nPorcienOcultas := nPorcienOcultas / Max( nColVisibles , 1 )  // % a añadir a cada columna visible

También tenemos que sumar al espacio libre del browse el tamaño de las columnas que no se van a mostrar

nResto := nResto + nAnchoOcultas

Ahora que ya sabemos las columnas que se van a mostrar y el espacio que nos queda libre, tan solo tenemos que ajustar cada columna a su tamaño.

      Por cada columna
              Si es elástica
                  anchura := su anchura original
                        + los pixel que corresponden a su porcentaje
                        + el porcentaje extra de las columnas ocultas (nPorcienOcultas)
  FOR n:= 1 TO  nColumnas
      IF oSender:aCols[n]:nPorcien > 0
         oSender:aCols[n]:nWidth := oSender:aCols[n]:nInitWidth + ;
                oSender:Porcion( nResto, oSender:aCols[n]:nPorcien + nPorcienOcultas )
      ENDIF
   NEXT

Y eso es todo, ni más ni menos.

Lo mismo que he hecho para el TArrayBrowse se puede hacer con el resto de browse.

Por último he de confesar que me considero pintor antes que fabricante de pinceles, así que, tiro la piedra y escondo la mano.

A ver si algún maestro de Xailer se anima y crea una colección de  controles «ElasticBrowse»

El código fuente lo tenéis en la sección de descargas

1 comentario en “Browses elásticos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *