Informe en .NET con Crystal Reports y base de datos MySQL.

Vaya por delante que el tema de la creación de informes (o reports) siempre ha sido una de las partes de la programación que me ha dado más pereza. Pero está claro que pocas aplicaciones se salvan de requerirlos en mayor o menor grado, y la que actualmente tengo entre manos no es, en absoluto, una excepción. Así pues, estos últimos días he tenido que preparar un nuevo report y de paso he aprovechado para investigar un poco y descubrir una nueva manera de realizarlos. Paso a explicarme.

Como ya he comentado en ocasiones, mi entorno de desarrollo es VisualBasic.NET y una base de datos MySQL. Y los informes los hago con el propio Crystal Reports que incorpora el VisualStudio.NET. El tema está en que hasta ahora los hacía a través de una conexión ODBC que tenía que instalar en cada máquina apuntando hacia el servidor MySQL, pero este sistema no me gustaba demasiado. Y no me gustaba porque en realidad de bases de datos tengo distintas con distintos nombres pero con las mismas estructuras de tablas y datos parecidos, ya que una es la productiva (la real, la buena) y las otras son para desarrollo, para que los usuarios hagan pruebas sin que afecte a los datos reales, etc. Controlar la cadena de conexión en la aplicación para que ataque a una u otra base de datos no me supone el más mínimo problema, pero los informes cogen sus datos a través de una conexión ODBC concreta y ésta en cada máquina ataca a una única base de datos. Así que no me convence este sistema.

Y ahora he descubierto cómo puede crear informes a través de un DataTable y de una estructura en un archivo XML, sin necesidad de nada más. De hecho en lugar de un DataTable se puede utilizar perfectamente un DataSet y funciona sin ningún tipo de problemas ni apenas modificaciones (dejo en manos del lector probarlo si le interesa). Voy a pasar a explicar mediante un ejemplo y algunas imágenes cómo realizarlo desde cero.

Partiré de dos tables en mi base de datos MySQL en las que guardaré información de facturas. La primera tabla es de cabeceras de esas facturas y tiene estos datos:

+---------+------------+-------------------------+
| blh_num | blh_dat    | blh_cus                 |
+---------+------------+-------------------------+
|       1 | 2008-07-30 | CERAMICAS PEPE, S.A.    |
|       2 | 2008-07-30 | TALLERES GOMEZ, S.L.    |
|       3 | 2008-07-31 | DEPORTES DAMIAN, S.L.   |
|       4 | 2008-07-31 | SOFTWARE ALBERTMATA.NET |
+---------+------------+-------------------------+

La segunda tabla son las posiciones de cada factura y tiene estos otros datos:

+---------+---------+------------------------+---------+---------+
| blp_num | blp_pos | blp_art                | blp_pri | blp_qty |
+---------+---------+------------------------+---------+---------+
|       1 |       1 | RATON LOGITECH         |   15.95 |       1 |
|       2 |       1 | MONITOR LG 19 PULGADAS |   210.5 |       1 |
|       3 |       1 | ROUTER DLINK           |      56 |       1 |
|       4 |       1 | RATON LOGITECH         |   15.95 |       2 |
|       4 |       2 | TECLADO LOGITECH       |   12.95 |       1 |
|       4 |       3 | RECEPTOR GPS ZAPPA     |   59.95 |       1 |
|       4 |       4 | PAQUETE 500 FOLIOS     |     3.7 |       4 |
+---------+---------+------------------------+---------+---------+

Es algo muy sencillo y poco normalizado, pero nos servirá para el ejemplo. Concretamente vamos a crear un informe que será nada más que una impresión de una factura. Como voy a trabajar con un simple DataTable pero mi informe requerirá datos de dos tablas, me crearé primero una vista en MySQL con la siguiente instrucción:

CREATE VIEW zbl_bill2print AS 
(
SELECT
    blh_num AS BILL_NUMBER,
    blh_dat AS BILL_DATE,
    blh_cus AS BILL_CUSTOMER,
    blp_pos AS LINE_NUMBER,
    blp_art AS LINE_ARTICLE,
    blp_pri AS LINE_UNITPRICE,
    blp_qty AS LINE_UNITS,
    blp_pri * blp_qty AS LINE_TOTALPRICE
FROM
    blh_billheader LEFT JOIN blp_billposits ON blh_num = blp_num
WHERE
    blh_num = 4
);

Así, mi informe a partir de ahora se realizará sobre esta vista zbl_bill2print. Vamos pues a empezar con la parte que incumbe a .NET.

Paso 1. Creación del archivo XML con la estructura de la tabla/vista.

Para todo el ejemplo jugaremos con:

1) un formulario frmMain donde tendremos el objeto visualizador de informes.
2) una clase clsReportCreator que crearemos a continuación.
3) un informe rptBill que representará la factura que queremos imprimir.

Comenzamos pues creando la clase clsReportCreator, que constará de un único atributo (el nombre de la tabla o vista), un método constructor, un método para cargar el DataTable correspondiente y un último método para generar el archivo XML. El código completo de esta clase es el siguiente:

'--------------------------------------------------------------------
' Author:      Albert Mata (www.albertmata.net)
' Date:        20080731
' Needs:       MySQL.Data reference.
' Description: Class to create a report using just an XML file. 
'--------------------------------------------------------------------
Imports MySql.Data.MySqlClient

Public Class clsReportCreator

    '----------------------------------------------------------------
    ' Attributes.
    '----------------------------------------------------------------
    Private TableOrView As String

    '----------------------------------------------------------------
    ' Constructor method.
    '----------------------------------------------------------------
    Public Sub New(ByVal TableOrView As String)
        Me.TableOrView = TableOrView
    End Sub

    '----------------------------------------------------------------
    ' Returns DataTable corresponding to TableOrView.
    '----------------------------------------------------------------
    Public Function GetDataTable() As DataTable
        Dim DA As MySqlDataAdapter
        Dim DS As New DataSet
        Dim DT As DataTable
        Dim ConnectionString As String
        Dim SQL As String

        'Setting connection string to connect to MySQL database.
        ConnectionString = "Database = blog; " _
                         & "Data Source = localhost; " _
                         & "User ID = root; " _
                         & "Password = mypassword"

        'Setting SQL string.
        SQL = "SELECT * FROM " & Me.TableOrView

        'Getting data and filling DataSet and DataTable.
        DA = New MySqlDataAdapter(SQL, ConnectionString)
        DA.Fill(DS, Me.TableOrView)
        DT = DS.Tables(Me.TableOrView)

        'Returning DataTable.
        Return DT
    End Function

    '----------------------------------------------------------------
    ' Creates XML file in desired path.
    '----------------------------------------------------------------
    Public Sub CreateXMLFile(ByVal FilePath As String)
        Dim DT As DataTable

        'Creating DataTable.
        DT = Me.GetDataTable()

        'Writting XML file in desired path.
        DT.WriteXmlSchema(FilePath & Me.TableOrView & ".xml")
    End Sub

End Class

Y creamos también el formulario frmMain cuyo único código por el momento (después cambiará un poco) será el que sigue:

'--------------------------------------------------------------------
' Author:      Albert Mata (www.albertmata.net)
' Date:        20080731
' Description: Form to show how to create a report using just an XML
'              file. 
'--------------------------------------------------------------------
Public Class frmMain

    '----------------------------------------------------------------
    ' As a first step, creates XML file.
    '----------------------------------------------------------------
    Private Sub frmMain_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
       'Creating XML file.
        Dim RC As New clsReportCreator("zbl_bill2print")
        RC.CreateXMLFile("C:\")
    End Sub

End Class

Con esto tenemos una primera aplicación que al ejecutarla nos creará el archivo C:\zbl_bill2print.xml con la estructura de la vista zbl_bill2print. Lo probamos pues y obtenemos un archivo como éste.

Paso 2. Creación del informe e inserción del origen de datos.

Añadimos al proyecto un informe al que llamaremos rptBill.rpt y que crearemos seleccionando la alternativa Como informe en blanco y por tanto desestimando plantillas.

A continuación en el menú Explorador de campos seleccionamos la primera opción Campos de base de datos y en su menú contextual hacemos click en la opción Asistente de base de datos.

En el menú que se despliega (Orígenes de datos disponibles) seleccionamos Crear nueva conexión y a continuación la opción ADO.NET.

Con esto se nos abrira un nuevo formulario en el que nos solicitará la Ruta del archivo. Debemos aquí ir a buscar el archivo XML que hemos creado anteriormente (en mi caso el C:\zbl_bill2print.xml) y acto seguido pulsar en Finalizar. Con ello, en el menú anterior (Orígenes de datos disponibles) se nos mostrará ya la opción NewDataSet incluyendo el zbl_bill2print que acabamos de añadir.

Lo marcamos y le damos al botón para trasladarlo al menú de Tablas seleccionadas y pulsamos en Aceptar.

Con esto hemos conseguido que en el menú Explorador de campos nos aparezca ya la estructura de zbl_bill2print con sus campos, tal como se muestra a continuación:

Paso 3. Diseño del informe.

No tiene ningún misterio. Se trata de añadir los campos desde el menú Explorador de campos donde corresponda, insertarle los objetos de texto que nos parezcan adecuados, los totales cuando sea preciso, dar formato a los textos, incorporar imágenes y demás florituras a nuestro antojo…

Yo me inclino por un diseño sobrio como éste:

Paso 4. Últimos pasos para obtener la factura.

Por último vamos ya a crear la factura. Para ello en el formulario frmMain añadiremos un objeto de tipo CrystalReportViewer al que llamaremos por ejemplo crvBill. Y modificamos el código de frmMain para dejarlo como sigue:

'--------------------------------------------------------------------
' Author:      Albert Mata (www.albertmata.net)
' Date:        20080731
' Description: Form to show how to create a report using just an XML
'              file. 
'--------------------------------------------------------------------
Imports CrystalDecisions.CrystalReports.Engine

Public Class frmMain

    '----------------------------------------------------------------
    ' Creates XML file (just once) and creates and loads a report.
    '----------------------------------------------------------------
    Private Sub frmMain_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
        'Creating XML file.
        Dim RC As New clsReportCreator("zbl_bill2print")
        'RC.CreateXMLFile("C:\")

        'Creating report.
        Dim RD As ReportDocument = New rptBill()

        'Setting data source for report.
        Dim DT As DataTable = RC.GetDataTable()
        RD.SetDataSource(DT)

        'Setting data source for possible subreports.
        For Each SR As ReportDocument In RD.Subreports
            If SR.Database.Tables.Count > 0 Then
                SR.SetDataSource(DT)
            End If
        Next

        'Setting recently created report must be shown in viewer.
        Me.crvBill.ReportSource = RD
    End Sub

End Class

Nótese que ahora ya he comentado la línea en que creamos el archivo XML, puesto que sólo necesitamos crearlo una única vez para luego poder generar el origen de datos, pero a partir de aquí no necesitaremos andar creándolo cada vez.

En este código lo que estamos haciendo fundamentalmente es crear un objeto del tipo informe que hemos diseñado en el paso 3, obtener un DataTable con los datos que queremos mostrar (en este caso y tal como tenemos definida la vista de MySQL, queremos mostrar la factura número 4), establecer que el origen de datos del informe será este DataTable y finalmente solicitarle al CrystalReportViewer que nos muestre este informe.

Ejecutamos nuevamente la aplicación y obtenemos la factura que queríamos:

Por supuesto en una factura auténtica faltarían datos fiscales, logotipos, impuestos, condiciones de pago y demás, pero lo que aquí pretendía era únicamente mostrar cómo llevar a cabo el informe en sí mismo.

Con esto queda visto cómo simplemente utilizando un archivo XML podemos crear un informe en VisualBasic.NET. Por supuesto habría que mejorar muchas cosas, por ejemplo optimizar cómo se realiza la conexión con la base de datos (particularmente tengo un clase para llevar a cabo esa serie de cuestiones), también evitar poner la condición WHERE directamente en la vista de MySQL y sí por ejemplo cuando recuperamos el DataTable… en fin, unas cuantas cosas. Pero lo que buscaba con este ejemplo era hacer algo muy minimalista para que quedara claro el funcionamiento.

Actualización.

A raíz de uno de los comentarios he añadido una pequeña segunda parte a este post, en la que se explica cómo pasar parámetros desde el formulario hasta el informe.

15 Respuestas a “Informe en .NET con Crystal Reports y base de datos MySQL.”


  1. 1 Cristhian

    Hola amigo Albert Mata, realmente muchas gracias por esta clase, me ha servido bastante!!! porque era lo que andaba buscando, y a pesar de no ser tan complicado me anadaba haciendo un mundo.
    Cuando la realidad era algo sencillo (pero hay q saberlo) y nada reiterarte las gracias

    Saludos

  2. 2 Albert Mata

    Gracias a ti por el comentario, Cristhian. Realmente no es complicado, pero hasta que lo encuentras te puedes liar un poco, sí. Vamos, a mí me pasó. ¡Un saludo!

  3. 3 Iván

    También quería darte las gracias por este tutorial, aun no me ah surgido la necesidad de usar Crystal Reports pero seguro que pronto lo usare así que esto me vendrá muy bien. Muchas gracias.

  4. 4 Gabriel

    Gracias por el tutorial. Tengo un problemita quiero mostrar cada una de las filas del DataTable en páginas distintas en el reporte del crystal. Utilizando RD.SetDataSource(DT) y luego Me.crvBill.ReportSource = RD solo muestra la ultima fila del DT, cual sería la posible solución.
    saludos.

  5. 5 Albert Mata

    Si te soy sincero no termino de entender lo que quieres hacer: 1) que cada artículo de la factura aparezca en una página distinta (un artículo en cada página) o 2) que todos los artículos aparezcan en todas las páginas repetidamente. ¿Has probado a jugar con las distintas secciones del informe en Crystal Reports? Igual por ahí sacas algo. Si no, no dudes en volver a contactar conmigo y veremos qué podemos hacer.

  6. 6 Gabriel

    Albert lo que estoy haciendo es sencillo, en el datatable, en sus filas tengo distintas facturas por eso necesito mostrarlas en paginas distintas. Desde ya muchas gracias por tu tiempo.
    Gabriel, desde Argentina, Mendoza.

  7. 7 Gabriel

    Albert pude solucionar el problema, hay que crear grupos en el reporte de crystal report y que estos los muestre en paginas diferentes, desde ya muchas por tu tutorial.

  8. 8 maka7

    Muchas gracias por el tutorial, me ha sido de gran utilidad, me gustaria preguntarte si se puede quitar o cambiar lo de “Informe Principal” debajo de la barra de navegacion, ya que en mi programa voy a generar varios informes y no me parece adecuado que salga siempre “Informe Principal”. Gracias por la ayuda.

  9. 9 Albert Mata

    Si se puede, no sé hacerlo. He mirado las propiedades del objeto CrystalReportViewer si permite quitar ese botón y no lo he encontrado. Ahora bien… prepárate para leer la solución más poco elegante que habrás leído en tu vida… si añades al formulario una simple etiqueta (Label), le pones el AutoSize a False, le quitas el texto, la dimensionas como quieras y la ubicas donde corresponde… el efecto óptico final es idéntico que si ese botón (que ahora queda debajo de la etiqueta) no existiera.

  10. 10 maka7

    Gracias Albert, no es muy elegante pero es practico. Me gustaría preguntarte otra cosilla q me tiene un poco liada. Necesito pasar un parametro desde un textbox al reporte y cuanto mas leo en foros mas me lio… Este es mi codigo:

    Imports CrystalDecisions.CrystalReports.Engine

    Public Class Form22

    ‘—————————————————————-
    ‘ As a first step, creates XML file.
    ‘—————————————————————-
    Private Sub Form22_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ‘Creating XML file.
    Dim RC1 As New Class1(”documento”)
    ‘RC1.CreateXMLFile(”C:\”)

    ‘Creating report.
    Dim RD As ReportDocument = New ReportDocument()
    RD.Load(”C:\Informe1.rpt”)

    ‘Setting data source for report.
    Dim DT1 As DataTable = RC1.GetDataTable()

    RD.SetDataSource(DT1)

    ‘Setting data source for possible subreports.
    For Each SR As ReportDocument In RD.Subreports
    If SR.Database.Tables.Count > 0 Then
    SR.SetDataSource(DT1)
    End If
    Next

    ‘Setting recently created report must be shown in viewer.
    Me.CrystalReportViewer1.ReportSource = RD
    End Sub
    End Class

    Muchas gracias por su tiempo.

  11. 11 Albert Mata

    ¡Gracias por llamarme chapucero! ;-)

    Para pasar un parámetro desde un Textbox al reporte puedes probar si te sirve algo así:

    1) En el Explorador de campos del informe, te sitúas en Campos de parámetro, haces click derecho, seleccionas Nuevo, le informas un nombre (p.ej. Test) y le das a aceptar.
    2) Ahora dentro de Campos de parámetro tendrás el parámetro Test disponible para arrastrarlo hasta el punto del reporte que desees. Hazlo.
    3) Por último al final de todo del procedimiento (después de establecer el ReportSource = RD), añades las siguientes líneas:
    ‘Setting report’s parameter.
    RD.SetParameterValue(”Test”, “Sthg coming from Textbox”)

    Yo le he mandado un literal, pero obvio le puedes mandar lo que quieras (cuando creaste el campo de parámetro habrás observado que le podías indicar de qué tipo de datos se trataría).

  12. 12 maka7

    umm, fallo mio q no me e explicado bien, kiero pasar el parametro desde el textbox de mi form, lo que seria algo asi supongo
    RD.SetParameterValue(”Test”, textbox1.text) por lo que no quiero que salga la ventana para pedirme el valor.
    Pero la variables test, se la quiero asociar a uno de los campos de mi base de datos. Explico, por ejemplo si le paso a test el valor 1, pues generar el reporte factura cuyo numero sea 1, como asocio el campo del textbox con el de la base de datos??

  13. 13 Albert Mata

    Qué tal maka7. Te he enviado un correo con una posible solución, ya que al ser algo más compleja me parece buena idea crear una segunda parte de este post incorporando estas opciones. Pero como lo haré con algo más de calma mañana, te he avanzado el contenido de dicho post.

  14. 14 Luis Lopez

    Albert, muchas gracias es de gran utilidad este tutorial

  15. 15 Ivan Coronel

    Muchisimas gracias, me sirvio mucho no sabes las vueltas que di para poder conectar el bendito crystal con mysql, al hacerlo con odbc no tenia problemas pero al cambiarme y usar el conector para .net me hice lios. Muchas gracias nuevamente.

Añade un Comentario




Creative Commons License
El blog de Albert Mata by Albert Mata is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 2.5 España License.