Usando Crystal Report 8.5 a través del Report Designer Component
con Visual FoxPro
Por Luis Guerra Crystal_demo.zip
El presente artículo
está escrito con la intención de dar a los desarrolladores de Visual FoxPro una
introducción e ideas para poder integrar Crystal Reports (a través del RDC) con
Visual FoxPro y sacarle el máximo provecho a una herramienta que, sin lugar a
dudas, es una de las mejores para la creación de reportes.
Últimamente he
visto en forma creciente que muchos desarrolladores de VFP necesitan usar
Crystal Reports (CR) por sus excelentes capacidades entre las cuales se
encuentran, creo yo, las posibilidades de insertar gráficos estadísticos dentro
del reporte cuyos datos los obtiene del mismo reporte, tener subreportes, así
como poder exportar los reportes a varios formatos, entre ellos: HTML, RTF,
Excel, Word y otros sin tener que usar la automatización de Word, Excel o lo
que sea. Sin embargo, también he notado que la mayoría de los que usan el
Crystal Reports ActiveX (CRYSTL32.OCX), que, si bien es fácil de usar, no da al
desarrollador un gran control sobre el reporte. Crystal Decisions desde la
versión 7 de Crystal Reports recomienda el uso del Report Designer Component
(RDC) para todo desarrollo o integración con aplicaciones propias y dejará de
lado (léase, no actualizará) las demás tecnologías que antes soportaba.
Una de las
desventajas obvias que tiene, entre otras, el Crystal Report ActiveX es que no
permite exportar a formato PDF lo cual es algo que para muchos desarrolladores
es necesario hoy en día ya que dicho formato es uno de los más populares en
Internet para la presentación de documentación, manuales, etc. Para poder
realizar lo último y otras cosas más debemos usar la tecnología RDC que nos
proporciona Crystal Reports desde la versión 7. En este artículo vamos a usar
la versión 8.5 que tiene varias mejoras.
El RDC es una
tecnología que actualmente agrupa a cuatro controles:
-
El Designer Runtime Library (CRAXDRT.DLL) que es un servidor de
automatización COM que provee la manipulación del reporte así como de sus
funciones de impresión, y de exportación.
-
El Distributable Report Designer que es un control ActiveX que permite
diseñar y crear reportes dentro de una aplicación, aunque para usarlo se
necesitan licencias por usuario que hay que comprar por separado. Por lo menos
en este artículo no lo vamos a usar, quizás en otro artículo.
-
El Designer Designe Runtime Library (CRAXDDRT.DLL) que provee toda la
funcionalidad del primero pero que además soporta el Distributable Report
Designer.
-
Y por último el Crystal Report Viewer que es un control ActiveX que se
usa para las vistas previas de los reportes y que tiene muchas más
funcionalidades que la vista previa del Crystal Report ActiveX.
En este
artículo nos concentraremos en el uso del primer y cuarto controles para
enseñar cómo integrar el RDC con aplicaciones VFP. Para instanciar al Designer
Runtime Library debemos hacer lo siguiente:
** Crea una
instancia del RDC de Crystal Reports.
loCR= CREATEOBJECT(
"CrystalRuntime.Application")
Con eso
tendremos a disposición un completo modelo de objetos que para cuestiones
didácticas presento a continuación:
El gráfico
anterior se obtuvo del archivo de ayuda ‘CrystalDevHelp.CHM’
que se instala con CR y del cual se puede obtener mucha más información. Como
se ve, en la parte más alta del modelo está el objeto Application, seguido del objeto Report.
El lado izquierdo del modelo tiene que ver con el objeto Report, se muestran las secciones, áreas, campos y más. En el lado
derecho sobresale el acceso a datos así como los objetos y colecciones que
permiten la manipulación del reporte.
Para imprimir
un reporte lo primero que se debe hacer es crear el objeto Application, tal como se mostró arriba, y luego usar el método OpenReport() para abrir el reporte y
obtener una referencia del reporte como objeto:
** Crea una instancia del RDC de Crystal Reports.
loCR=
CREATEOBJECT(
"CrystalRuntime.Application")
El reporte que
se va a usar es un reporte con acceso directo a una tabla FoxPro 2.X,
lógicamente que para diseñar el reporte la tabla debe existir, por lo tanto antes
de crear el reporte debemos tener ya los datos creados si por algún motivo se
cambia la estructura de la tabla resultante, se debe actualizar el archivo de
reporte también, simplemente debemos volver a generar la tabla, abrir el
archivo de reporte en Crystal Reports y seleccionar la opción Verify Database que se encuentra dentro
de la opción Database del menú
principal para que el reporte reconozca la nueva estructura de la tabla y se
actualice. Esto al principio puede parecer un poco complicado, pero cuando ya
se tiene una clase creada que se preocupa de todo el manejo de la conversión
del cursor a la tabla que va a alimentar al reporte, el asunto se torna fácil.
Yo, por ejemplo, tengo una clase de Command
Button específica, en la que sólo me encargo de crear un cursor con los
datos deseados, ya sea desde tablas nativas, vistas remotas, SPT o cualquier
otra tecnología que soporte VFP para crear cursores de datos, y además poner en
una propiedad el nombre completo del archivo de reporte, el botón ya se encarga
de llamar a otra clase que es la que usa el RDC apropiadamente. El reporte del
ejemplo es más o menos similar al que se muestra a continuación en el entorno
de diseño de Crystal Reports:
** Abre el reporte y guarda una referencia en ‘loRpt’.
loRpt=
loCR.OpenReport( "Crystal_Demo.RPT")
Cuando se
diseña un reporte se tiene la opción de guardar los datos actuales junto con el
reporte. Esto se puede activar/desactivar dentro de CR en la opción File->Report Options:
Siempre es
bueno asegurarse que los reportes no van a usar los datos que se guardaron con
ellos. Lo siguiente muestra cómo se pueden descartar esos datos:
** Descarta los
datos que se hayan grabado con el reporte.
IF loRpt.HasSavedData
loRpt.DiscardSavedData()
ENDIF
Para imprimir
el reporte simplemente se llama al método PrintOut()
del objeto Report que se ha creado
antes:
** Imprimir.
loRpt.PrintOut()
Con lo cual se
mostrará una ventana de diálogo desde la cual el usuario puede seleccionar la cantidad
de copias que desea imprimir así como el rango de páginas que desea imprimir:
Si no desea
que aparezca la ventana de diálogo, el método PrintOut() tiene cinco parámetros, de los cuales el primero nos
permite indicar si deseamos que muestre o no la ventana de diálogo mientras que
el segundo permite especificar el número de copias, así tendríamos que…
loRpt.PrintOut( .F., 2)
…imprime dos
copias del reporte sin mostrar la ventana de diálogo. Además hay otros dos
métodos que son: PrinterSetup() que
muestra una ventana de diálogo que permite cambiar la impresora en la que se va
imprimir el reporte así como cambiar la orientación y el tamaño del papel; y SelectPrinter() que permite cambiar en
forma programática la impresora de destino del reporte. La ventana de diálogo
que PrinterSetup() muestra es la
siguiente:
En este
artículo vamos a ver el uso de reportes CR con acceso directo a tablas .DBF en
formato FoxPro 2.X ya que para el desarrollador VFP le va a ser fácil de usar
ya que, accediendo a cualquier fuente de datos, ya sea tablas nativas, vistas
remotas, SPT, ADO y XML podemos tenerlas como cursores VFP y luego en tablas
FoxPro 2.X que pueden ser leídas por CR. Una de las principales ventajas de
trabajar de esa forma es que si en caso se desea cambiar la base de datos desde
VFP a SQL Server u otra base de datos, no se tendrá que actualizar los archivos
de reportes uno por uno para poder usarlos con el nuevo tipo de base de datos,
con el consiguiente riesgo de que la sintaxis actual del SELECT que alimenta al
reporte no sea compatible con la nueva base de datos.
Volviendo al
tema, como es poco probable que la ubicación de los datos del reporte durante
el tiempo de desarrollo sea igual a la ubicación de los mismos cuando la aplicación
esté en producción, podemos cambiar la ubicación de los datos antes de
imprimir, exportar, o ver el reporte en pantalla, de la siguiente manera:
** Crea nombre
para tabla temporal que va a estar
** ubicada en la
carpeta TEMP de Windows.
lcTabla= ADDBS( GETENV("TEMP"))+ SYS(3)+ ".DBF"
Una pequeña
curiosidad: Como este ejemplo lo desarrollé en VFP 7, cuando puse el paréntesis
después de la función GETENV()
apareció una pequeña lista de las variables D.O.S. que se crean cuando arranca
Windows. Cuando navegué por las distintas variables al costado aparecía el
valor que tenía cada una de ellas. Sin duda esta versión de Intellisense es, a
mi parecer, la mejor que se ha creado hasta ahora. La figura siguiente muestra
el momento:
De lo
explicado anteriormente, entonces se podría crear un cursor desde un SELECT-SQL, por ejemplo, y el cursor
resultante se podría copiar a una tabla tipo FoxPro 2.X, la cual después de
imprimirse, verse o exportarse se borraría. Todo esto sin que el usuario final
tenga que hacerlo. El script quedaría más o menos así (tomando en cuenta que
las tablas Customer y Orders existen):
lcTabla= ADDBS( GETENV("TEMP"))+
SYS(3)+ ".DBF"
SELECT Customer.country, Customer.company,;
COUNT(
Orders.order_id) As Orders, Customer.cust_id;
FROM customer INNER JOIN
orders ;
ON Customer.cust_id = Orders.cust_id;
GROUP BY Customer.cust_id;
ORDER
BY Customer.country;
INTO
CURSOR curPedidos
SELECT curPedidos
COPY TO ( lcTabla ) TYPE FOX2X
AS 850
** Cambia la ubicación
de la tabla en el reporte.
loRpt.Database.Tables( 1).Location= lcTabla
** Imprime el
reporte con los datos de la nueva ubicación.
loRpt.PrintOut()
** Borra la tabla
temporal.
DELETE FILE ( lcTabla )
Una última acotación
respecto al script mostrado arriba: La cláusula AS en la línea del COPY TO
es para que los caracteres con acentos que están en la tabla temporal se vean
correctamente en el reporte.
Una de los
principales atractivos de CR es la posibilidad de exportar los reportes a
varios formatos. Como comenté al principio del artículo, el RDC permite
exportar al formato PDF que es muy popular, la habilidad de poder exportar a
este formato existe en el RDC desde la versión 8.5. Si se desea exportar a
cualquier formato soportado por CR se deben usar el método Export() del objeto Report
en lugar del método PrintOut():
** Muestra el
diálogo de exportación para el reporte.
loRpt.Export( .T. )
Diálogo de exportación
del reporte:
Si no deseamos
que se muestre el diálogo de exportación, podemos controlar el tipo de formato
a exportar así como el destino y si es necesario el nombre del archivo
exportado. Para poder realizarlo debemos usar algunas propiedades del objeto ExportOptions el cual forma parte del
objeto Report:
** Exportar a
formato PDF.
WITH loRpt.ExportOptions
** Destino: 1=Archivo, 2=MailMAPI,
5=Aplicación.
.DestinationType= 1
** Tipo formato: 35=RTF exacto, 27=Excel,
32=HTML, etc.
.FormatType= 31
** Nombre del archivo exportado. Necesario
si DestinationType <> de 5.
.DiskFileName= “abc.pdf”
ENDWITH
loRpt.Export( .F. )
En el script
anterior se hace que el reporte sea exportado al formato PDF en el archivo “abc.pdf”. Si se especificara DestinationType=5 el archivo se abriría
automáticamente en Adobe Acrobat Reader ™.
Para terminar
con el artículo entraremos en el tema de la vista previa, que quizás es lo que
más se va a utilizar dentro de una aplicación. Para esto se usa el Crystal
Reports Viewer que se puede pegar en un formulario. Este control ActiveX
permite visualizar un reporte CR a la vez que le da al usuario final varias
funcionalidades que el desarrollador puede activar o desactivar según sea el
caso, por ejemplo se puede hacer que el botón de impresión no esté visible, o
también se puede hacer que el botón de zoom esté o no disponible. A
continuación se muestra el script que define a una clase de formulario creada
para recibir un reporte como objeto y visualizarlo:
** Definición de
la clase del form de vista previa.
DEFINE CLASS vista_previa AS form
Top
= 0
Left
= 0
Height
= 288
Width
= 480
DoCreate
= .T.
Caption
= "Vista previa"
MinButton = .F.
WindowType = 1
Name
= "vista_previa"
Comment
= “Vista previa de reporte Crystal Reports”
** Se agrega el
Crystal Reports Viewer ActiveX.
ADD
OBJECT oleCRViewer AS
OleControl WITH
;
OleClass
= "CrViewer.CrViewer", ;
Top
= 0, ;
Left
= 0, ;
Height
= 288, ;
Width
= 480, ;
Name
= "oleCRViewer"
PROCEDURE
Error
LPARAMETERS
nError, cMethod, nLine
IF
nError != 1440
DODEFAULT()
ENDIF
ENDPROC
PROCEDURE
Resize
ThisForm.oleCRViewer.Height = This.Height
ThisForm.oleCRViewer.Width = This.Width
ENDPROC
PROCEDURE
Init
LParameter
toRpt
** Se pasa el objeto Report a
la propiedad ReportSource
** del CR Viewer.
This.oleCRViewer.ReportSource= toRpt
** Se muestra el reporte.
This.oleCRViewer.ViewReport()
ENDPROC
PROCEDURE
oleCRViewer.Init
** Muestra el botón de exportar.
This.EnableExportButton=
.T.
This.EnableProgressControl=
.T.
**
Oculta el treeview.
This.DisplayGroupTree=
.F.
ENDPROC
ENDDEFINE
Los que hayan
visto el script con detenimiento se habrán dado cuenta que hay código para el
evento Error de la clase, esto es para que el CR Viewer no muestre el mensaje
de error que aparece cuando se pasa el objeto Report (oRpt) a la propiedad ReportSource
del CR Viewer. De lo poco que he podido investigar para saber a qué se debe el
error, he concluido que este error tiene que ver con el soporte de interfaz
dual de ActiveX, que se activa o desactiva con la función SYS(2333), sin embargo a pesar del error, el control sigue
funcionando correctamente, así es que la forma más fácil de obviar ese error es
colocando el código en el evento Error()
del formulario tal como figura en el script de arriba.
Como se puede
deducir del script, para mostrar el reporte sólo basta pasarle el objeto Report a la propiedad ReportSource del CR Viewer y luego usar
el método ViewReport(). Además, se
pueden usar una serie de propiedades para activar o desactivar cada control que
aparece en la barra de herramientas del CR Viewer e incluso se puede desactivar
por completo dicha barra. Si usamos el botón de exportación, aparecerá la
ventana de diálogo idéntica a la que aparece cuando llamamos al método Export() del objeto Report. La vista previa del reporte se vería así:
En la parte
superior del CR Viewer que se encuentra dentro de un formulario se encuentra la
barra de herramientas con los botones más usados, entre los cuales está un
botón con binoculares que permite buscar un texto dentro de todo el reporte.
También se puede hacer que se inmovilice cierta área del reporte para que
cuando se haga un scroll del mismo dicha área no se mueva, muy parecida a la
función de inmovilizar de MS Excel ©:
Después de
activar la función, el reporte se vería como lo muestra la figura siguiente y
en donde se aprecia que la parte superior del reporte así como la pequeña franja
izquierda son inmóviles en forma vertical y horizontal respectivamente:
Además las
funciones que otorgan los botones también pueden ser realizadas
programáticamente mediante métodos y/o propiedades que están disponibles para
el desarrollador e inclusive se pueden crear nuevos botones con sus propios
íconos (en caso que no nos gusten los que aparecen en forma predeterminada).
Como hemos
visto, el RDC provee al desarrollador con una herramienta muy potente que
permite tener control total del reporte. Aún hay muchas características que no
hemos visto en este artículo pero que están disponibles en el RDC, entre las
cuales tenemos, por ejemplo, poder manipular cada parte de los reportes en
tiempo de ejecución, crear reportes programáticamente desde cero (se puede
crear un nuevo reporte y guardarlo con los métodos New() y SaveAs() del
objeto Application respectivamente), modificar las diversas secciones que tiene
un reporte, crear nuevas secciones, cambiar el comportamiento de secciones,
cambiar el orden en que se presentan los datos dentro del reporte, cambiar
valores de ciertos campos o fórmulas de un reporte, es decir, podemos disponer
de todas las facilidades que nos otorga el modelo de objetos del RDC. Además,
también se puede realizar el enlazado de eventos (Event Binding), ya que los
reportes cuando se ejecutan desde el RDC, ya sea impresión, exportación o vista
previa, generan eventos, los cuales se pueden enlazar con métodos de clases
VFP, aunque el manual y la ayuda en línea están orientados a Visual Basic,
(inclusive este soporte es nativo para VB), se pueden usar los ejemplos y
convertirlos para que funcionen en VFP.
Los archivos
necesarios para el tiempo de ejecución del RDC están en el archivo ‘Runtime.HLP’ que se instala con CR, los
archivos principales son los que se mencionaron al principio del artículo, los
cuales deben ser registrados en la PC del usuario final para su correcto
funcionamiento, además de los archivos ‘ExportModeller.dll’
y ‘crTslv.dll’ los cuales soportan la
características de exportación, esto lo descubrí en el sitio de soporte de CR, www.crystaldecisions.com, ya que no
me acuerdo haberlo visto en el archivo ‘Runtime.HLP’
que mencioné, estos archivos también deben ser registrados en la PC del
usuario final. Y por último están una serie de DLL’s que soportan
características tales como formatos de exportación, gráficos, etc. Por ejemplo,
el acceso directo a tablas .DBF requiere de DLL’s diferentes al acceso a través
de ODBC. Estos archivos son mencionados en el mismo archivo de ayuda. Yo, en mi
caso, uso todos los archivos que soportan todas las características (uno nunca
sabe), excepto los de acceso a través de ODBC, ADO o XML ya que aún no uso el
acceso a través de esas tecnologías. en las clases que he creado para usar RDC.
Espero haber colaborado con muchos desarrolladores VFP que ven en Crystal Reports una herramienta muy útil para dar a sus aplicaciones un acabado mucho más profesional y que al fin de cuentas puede dejar a los clientes satisfechos.
FoxPress – Junio de 2003
© Luis Guerra