lunes, enero 30, 2006

Un poco de Xailer sin IDE.

Estoy a punto de terminar mi primer aplicación desarrollada totalmente con XAILER, y la verdad, me está gustando mucho como está quedando.



Independientemente de que la aplicación vaya quedando de maravilla, estoy realmente encantado con la facilidad de uso del IDE de Xailer que me ha ahorrado miles y miles de líneas código fuente que de no ser por el IDE tendría que haber escrito a mano, sobre todo por el tema de validación de TextBoxes (Gets hablando en XBase) que utilizo ampliamente.

Sin embargo, hay algunas cosas que definitivamente no se pueden hacer con el IDE, como el reto de programación al que me enfrenté para desarrollar una rutina.

El programa que estoy desarrollando es una aplicación de contabilidad, y necesitaba desarrollar un formulario que contuviera un folder, ese folder con un número variable de pestañas (con un máximo de 13), y dentro de cada pestaña un DBFBrowse y una StatusBar y aquí comenzó el reto.

Lo primero que hice fue crear un formulario meterle un folder dentro, y unas cuantas pestañas, guardé el formulario y luego, abrí el archivo con la extensión XFM que crea Xailer para los formularios y ahí venia el código que yo necesitaba para montarme manualmente el folder y las pestañas.

El primer reto fue hacer que el folder tuviera un número de pestañas que pueda crecer dinámicamente, es decir, el IDE no se puede utilizar para esto, porque el número de pestañas en el IDE es fijo, así pues, lo que hice fue crear con el IDE un formulario tipo ventana MDI y lo unico que le puse fue la barra de herramientas dentro:




El resto del reto fue completado programando manualmente TODO lo demás, pero creanme fue algo sumamente simple de hacer si se conoce un poquito la filosofía que sigue Xailer para construir los controles.

Lo primero que hice fue crear el componente FOLDER manualmente en la definicón de la clase, agregandolo manualmente en la definición de la clase:

CLASS Polizas FROM TForm

COMPONENT oReBar1
COMPONENT oFolderPolizas

DATA aFolderPages AS ARRAY(13)
.....

....
....

El folder no era posible crearlo en el IDE y posteriormente agregarle pestañas, porque al momento de crearlo se hace una llamada al método ::Create() que impide su posterior manipulación, mas adelante verás porqué es importante el método ::Create()

Luego, en el evento ON INITIALIZE construí manualmente el FOLDER y las pestañas necesarias:

WITH OBJECT ::oFolderPolizas := TFolder():New( Self )
:SetBounds( 64, 44, 200, 160 )
FOR nContador := 1 TO LEN(aTitulos)
WITH OBJECT ::aFolderPages[nContador] := TFolderPage():New(::oFolderPolizas)
:cText := aTitulos[nContador]
:Create()
END
NEXT
:nAlign := alCLIENT
:nIndex := 1
:Create()
END


Analicemos el código detalladamente:

Primero, procedemos a instaciar el objeto Folder, el llamado a ::SetBounds() no es realmente necesario porque mas adelante haremo un ::nAlign, bien, una vez creado el folder, hay que ponerle pestañas, y aquí empieza lo divertido, el numero de pestañas se ha cargado previamente en el arreglo aTitulos, de esta manera, yo cargo el array previamente con el número de elementos que voy a necestiar y tengo exactamente el número de pestañas que quiero.

Bien, al ser dinamica la creación de las pestañas, yo necesitaba de algún mecanismo que me permitiera hacer referencia a ellas posteriormente, si yo hubiese creado las pestañas desde el IDE, este les hubiera asignado los nombres directamente a las variables de los objetos que contendrían las pestañas, les hubiera puesto algo así como "oFolderPage1, oFolderPage2...." etc. Así que pense que seria mas rapido crear una DATA dentro de la clase llamada aFolderPages, para guardar cada una de las nuevas pestañas creadas en vez de crear 13 nombres de variables distintas.

Hay una cosa importante de los controles de Xailer, y es también quizá una de las partes mas confusas en cuanto al tratamiento de controles, y es el llamado al metodo ::Create(), que es el encargado de crear el control. Cuando comencé a trabajar con esto, según yo creaba el folder llamaba al ::Create() y luego le metia las pestañas.... ERROR!!!!!, primero se crea el folder, se le meten las pestañas, que por cierto también necesitan llamar a su método :Create() ya ahora si, una vez que estén metidas todas las pestañas necesarias, procedemos con el ::Create() del folder, como puedes apreciar en el trozo de código anterior.

Por último, y para que el folder me quedara perfectamente ajustado al área cliene del formulario, le asigné la data ::nAlign a alCLIENT, que quiere decir "adjust client" es decir, que el control debe ocupar toda el area cliente del contenedor, de esta manera, cuando cambia de tamaño el formulario, el control se ajusta al tamaño de su contenedor.

Listo, ya tengo los 12 folders dinámicos, y ahora ¿ cómo pongo dentro mas controles ?, en mi caso yo necesitaba un DBFBrowse y un StatusBar dentro de cada pestaña, así que de la misma forma como hice para crear el folder y sus pestañas, hice para crear los browses y las barra de estado, es decir, con un FOR:

FOR nContador := 1 TO LEN(aTitulos)
WITH OBJECT ::aStatusBar[nContador]:=TStatusBar():New(::aFolderPages[nContador])
:SetBounds( 0, 243, 442, 22 )
:cText := "Total de pólizas: ";
+ALLTRIM(STR((aPublics[nContador+10])->(AdsKeyCount())))

:Create()
END
WITH OBJECT ::aBrowPoli[nContador]:= TDbfBrowse():New(::aFolderPages[nContador])
:SetBounds( 0, 0, 350, 166 )
:nAlign := alCLIENT
:nColDividerStyle := blFORECOLOR
:nMarqueeStyle := bmHIGHLROW
:lAllowEdit := .F.
:lAllowColSwapping := .F.
:lAllowColHiding := .F.
:lRecordSelector := .F.
:Create()
:SetDbf(aPublics[nContador+10],;
{"numpoli2","tipopoli2","fecha","concepto","total"})

:lFooter := .T.
nSuma := 0
(aPublics[nContador+10])->(DBGOTOP())
(aPublics[nContador+10])->(DBEVAL({|| nSuma += total}))
(aPublics[nContador+10])->(DBGOTOP())
:aCols[5]:cFooter := TRANSFORM(nSuma,"999,999,999.99")
:OnDblClick := "PoliDeta"
END
NEXT


¿ Porqué primero el folder y las pestañas y luego el DBFBrowse y las StatusBar ? ¿ no se puede hacer todo de una sola sentada ?, eso fue lo que yo pensé en un principio, porque claro, si en un solo FOR puedo hacerme todo el trabajo de creacion de los controles, me ahorro molestias, ¿ si ?, pues va a ser que no, y aquí entra en acción el tema del método ::Create() otra vez, sucede que para que yo pueda meter los controles dentro de una pestaña, la pestaña tiene que existir, hasta aquí todo bien, porque la pestaña ya la hemos configurado y llamado a su ::Create(), sin embargo NO PUEDO AÑADIRLE CONTROLES, porque en realidad, la pestaña no esta creada, no existe, existe hasta que se crea su contenedor es decir el FOLDER, y como el Folder no se crea hasta que se han terminado de crear todas las pestañas estamos en un circulo vicioso, así que la forma mas rápida de solucionar el problema, fue partir las rutinas en 2, una para crear folder y pestaña, y otra para crear el DBFBrowse y los StatusBar de cada una.

Una cosa interesante a tomar en cuenta, es que en Xailer, el orden de los factores si altera el producto, los controles se tienen que crear en el orden en que se necesitan ya que deben ocupar un espacio predeterminado dentro del area cliente de su contenedor, asi mismo su orden de creación es el orden de su TABSTOP, en mi caso primero tuve que crear el StatusBar y luego el DBFBrowse de cada perstaña, no se puede hacer al revés, porque al momento de desplegar el formulario y pintar el DBFBrowse antes de pintar la barra de estado, este se dibujará sobre la barra y luego entonces la barra no sera visible hasta que se realice un cambio de tamaño del formulario; también en el caso del StatusBar, el ::SetBounds() sale sobrando, porque a final de cuentas, siempre irá en la parte inferior del formulario.

Crear el browse fue mucho mas facil de lo que yo pensaba, ya que con anterioridad yo habia definido una variable PUBLIC tipo arreglo, con los ALIAS que cada BROWSE iba a necesitar, con lo cual el asignar los alias a cada control fue tarea sumamente rápida. ¿ Porqué no usé un DataSet y luego un DataControl DBFBrowse ? bueno, porque los DataSets operan sobre directorios fijos de la computadora, y en mi caso las bases de datos estan dispersas en distintos directorios, lo cual me habria tenido que llevar a tener varios DataSets para poder atacar el problema, así que mejor opté por algo mas simple, que fue manejar las bases de datos al mas puro y viejo estilo Clipperiano, es decir, abrirlas y asginarles un alias.

Finalmente necesitaba que todos mis browses tuvieran un footer con la sumatoria de todos los importes de las pólizas (apuntes) contables, asi que una vez mas, al viejo estilo Clipper 5.2, un DBEVAL() y un code block, hicieron el trabajo.

El resultado me ha gustado mucho:





Para ver estas imagenes a tamaño completo, simplemente haz click sobre ellas.

Este fue uno de mis primero retos de programación con Xailer y la verdad no me ha costado nada de trabajo resolverlo, simplemente un poco de estudio del producto y unos pocos mas de experimentos y he aquí el resultado. La verdad esto de programar con Xailer me está gustando mucho.

1 comentario:

Anónimo dijo...

René,

enhorabuena! Te está quedando muy bien.

Un saludo,

José F. Giménez