FoxPress – Junio 2003

 

Usando Crystal Report 8.5 a través del Report Designer Component con Visual FoxPro

 

http://www.fpress.com/

 

 

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