Ejemplos de Crystal Report

Hola a tod@s,

En este artículo voy a explicar como hacer que un informe nos muestre los datos de una columna de una tabla de base de datos en múltiples columnas y voy a hacer un ejemplo de subreport.

Para realizar dichas tareas voy a crear un nuevo proyecto basado en lo que expuse en el artículo del 17 de septiembre "Crear Crystal Report sin problemas de conexión". En este caso voy a usar la base de datos NorthWind de Sql Server 2005 (es la misma que la de Sql Server 2000, solo la tenéis que importar a 2005).

Primer Ejemplo (multiples columnas)

 Voy a sacar un listado de productos en 3 columnas, para ello me creo una clase donde realizo la llamada a la base de datos y rellenaré un dataset que será el encargado de proporcionar los datos al report. La SQL que tengo es "SELECT ProductID, ProductName FROM dbo.Products ORDER BY 2". Genero el XML que dará forma al report

<?xml version="1.0" standalone="yes"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
        <xs:element name="productos">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="ProductID" type="xs:long" />
              <xs:element name="ProductName" type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
    <xs:unique name="Constraint1" msdata:PrimaryKey="true">
      <xs:selector xpath=".//productos" />
      <xs:field xpath="ProductID" />
    </xs:unique>
  </xs:element>
</xs:schema>

Y ahora creamos el report como si en el detalle solo tuvieramos una columna, nos tiene que quedar mas o menos como esto:

Ahora pinchamos con botón derecho sobre la sección detalle para que nos salga el siguiente menú contextual donde elegiremos "Section Expert"

Ahora pincharemos sobre el check "Format with Multiple Columns"

Ahora nos vamos a la pestaña Layout donde definiremos la anchura de la columna, la separación tanto vertical como horizontal entre celdas de la tabla resultante, si queremos que las columnas se rellenen primero hacia abajo y luego hacia la derecha o primero hacia la derecha y luego hacia abajo. En el ejemplo que he hecho, le he dado una anchura de 4 cm. a cada columna, he dejado medio centímetro de espacio entre celdas y he rellenado los datos primero hacia la derecha y luego hacia abajo.

Como podréis observar, si os fijáis en la primera imagen y en esta siguiente resultado de definir las columnas, el propio editor de crystal muestra la anchura de una columna.

Y por último compilamos y ejecutamos la aplicación con lo que nos resulta el informe desado en varias columnas.

Una aplicación evidente de ésto es si necesitamos crear etiquetas a partir de los datos que tengamos en base de datos.

Segundo Ejemplo (crear subinforme)

 Para este ejemplo voy a añadir una segunda tabla al dataset que alimenta de datos al crystal, pero no voy a hacer que las tablas estén relacionadas, simplemente al final del informe voy a mostrar en un subinforme algunos datos de la tabla "Employees" de la base de datos NorthWind. Para realizar mi tarea modifico el método DevuelveDataSet de la clase que uso para obtener los datos de la base de datos quedando lo siguiente:

        private DataTable dtResultado;
        private DataTable dtResultado2;

        public DataSet DevuelveDataSet()
        {
            //Creo la estructura de la tabla
            dtResultado = new DataTable("productos");
            dtResultado.Columns.Clear();
            dtResultado.Columns.Add(new DataColumn("ProductID", typeof(long)));
            dtResultado.Columns.Add(new DataColumn("ProductName", typeof(String)));
            //Le añado la PK
            dtResultado.PrimaryKey = new DataColumn[] { dtResultado.Columns["ProductID"] };

            dtResultado2 = new DataTable("empleados");
            dtResultado2.Columns.Clear();
            dtResultado2.Columns.Add(new DataColumn("EmployeeID", typeof(long)));
            dtResultado2.Columns.Add(new DataColumn("LastName", typeof(String)));
            dtResultado2.Columns.Add(new DataColumn("FirstName", typeof(String)));
            dtResultado2.Columns.Add(new DataColumn("Title", typeof(String)));
            //Le añado la PK
            dtResultado2.PrimaryKey = new DataColumn[] { dtResultado2.Columns["EmployeeID"] };

            //Relleno la tabla con datos de la base de datos
            SqlConnectionStringBuilder bldr = new SqlConnectionStringBuilder();
            bldr["Data Source"] = @".\SQLExpress";
            bldr["Initial Catalog"] = "NorthWind";
            bldr["Integrated Security"] = true;

            //Podemos rellenar una tabla como si de una vista se tratara
            string str = "SELECT ProductID, ProductName " +
                         "FROM dbo.Products " +
                         "ORDER BY 2";
            string str2 = "SELECT EmployeeID, LastName, FirstName, Title " +
                          "FROM dbo.Employees " +
                          "ORDER BY 3";

            //Realizo el acceso a datos
            using (SqlConnection con = new SqlConnection(bldr.ConnectionString))
            {
                using (SqlDataAdapter da = new SqlDataAdapter())
                {
                    da.SelectCommand = new SqlCommand();
                    da.SelectCommand.CommandType = CommandType.Text;
                    da.SelectCommand.CommandText = str;
                    da.SelectCommand.Connection = con;                   

                    da.Fill(dtResultado);

                    da.SelectCommand.CommandText = str2;
                    da.Fill(dtResultado2);
                }
            }

            //Defino el dataset
            DataSet ds = new DataSet();

            ds.Tables.Add(dtResultado);
            ds.Tables.Add(dtResultado2);

            //Descomentando esta línea nos sirve para crear el XML con
            //el esquema de tablas que necesitamos
            //ds.WriteXmlSchema(@"C:\report.xml");

            //Elimino de memoria la tabla
            dtResultado.Dispose();
            dtResultado = null;
            dtResultado2.Dispose();
            dtResultado2 = null;

            return ds;         }

Modifico el xml que ayuda a crear la estructura al crystal para contemplar la nueva tabla

<?xml version="1.0" standalone="yes"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="
http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
        <xs:complexType>
            <xs:choice minOccurs="0" maxOccurs="unbounded">
                <xs:element name="productos">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="ProductID" type="xs:long" />
                            <xs:element name="ProductName" type="xs:string" minOccurs="0" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="empleados">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="EmployeeID" type="xs:long" />
                            <xs:element name="LastName" type="xs:string" minOccurs="0" />
                            <xs:element name="FirstName" type="xs:string" minOccurs="0" />
                            <xs:element name="Title" type="xs:string" minOccurs="0" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:choice>
        </xs:complexType>
        <xs:unique name="Constraint1" msdata:PrimaryKey="true">
            <xs:selector xpath=".//productos" />
            <xs:field xpath="ProductID" />
        </xs:unique>
        <xs:unique name="empleados_Constraint1" msdata:ConstraintName="Constraint1" msdata:PrimaryKey="true">
            <xs:selector xpath=".//empleados" />
            <xs:field xpath="EmployeeID" />
        </xs:unique>
    </xs:element>
</xs:schema>

Verifico la base de datos en el report (desde el VS, Crystal Reports / Database / Verify Database), me voy a la conexion concurrente para corroborar que la nueva tabla empleados nos aparece. Y visto esto empezamos a crear el subreport.

Colocamos el objeto resultante del paso anterior (ver imagen anterior) donde queramos que aparezca, en mi ejemplo lo pongo en el pié del informe (es decir, en la última página del informe) y seguidamente nos aparece la siguiente imagen

Pero pulsamos en la opción "Create a subreport with the Report Wizard" (también podéis usar uno que ya tengáis hecho, pero a mi siempre me gusta mas así), le damos un nombre y pulsamos en el botón "Report Wizard".

Elegimos la/s tabla/s que necesitemos

Los campos que queremos que se muestren

 Y ya nos saldría el subinforme

El ejemplo que he hecho es un poco tonto, pero si necesitaramos usar un subinforme en un pie de grupo de tal forma que los datos del subinforme se tuvieran que filtrar por una foreign key al dato que se usa para crear el grupo, tendríamos que tener en cuenta lo siguiente según la estructura que estoy siguiendo:

1. En el código tendría que definir la foreign key

DataRelation dr = new DataRelation("relacion_1", ds.Tables["tablapadre"].Columns["columnaPK"], ds.Tables["tablahija"].Columns["columnaFK"]);
ds.Relations.Add(dr);
dr = null;

2. En el XML habría que incluir la relación

3. En el Crystal, botón derecho sobre el subinforme "Change Subreports Links…", elegimos el campo de la tabla padre que hace referencia a la relación. Tendremos algo parecido a

4. Si ahora nos vamos al subreport, en la opción de menú Crystal Report/Report/Selection Formula/Record (si lo hacemos desde el VS2005), nos aparecerá algo parecido a:

Espero que os haya parecido interesante. En mi opinión no me gusta mucho este producto, voy a empezar a aprender Reporting Services.

20 Responses to Ejemplos de Crystal Report

  1. René says:

    HOLA,
    Gracias por tu artículo, para mi muy bueno y de gran utlidad.
     
    Sigue así.
     
    Saludos

  2. Roberto says:

    Hola tu informacion me fue de mucha ayuda ya que no estoy muy familiarizado con crystal y solo me hacia falta lo de los sub reportes y gracias a que lo utilizastes eso me ayudo
     
    gracias …
    y saludos
     
     

  3. Kenneth says:

    Buenas Muchas Gracias por los ejemplos me han sido de gran ayuda, muy bueno el artículo

  4. Eduardo says:

    Necesito formatear esta cadena 106040567 y que me salga 10.604.056-7 ¿Como lo hago?SaludosEduardo

  5. weimar says:

    un Favor como hago para abrir bases de datos de lotus notes *.nsf  con Crystal reports 9 o 11 o 12

    • Carlos Frutos says:

      con cualquiera de las versiones se puede abrir bases de datos de lotus notes, solo tenes que tener instalado en tu equipo la versión odbc exacta para abrir los *.nsf.

  6. Carmen says:

    No tengo ni idea, hasta el momento nunca he trabajado con lotus notes

  7. Arturo says:

    Hola, Quiero felicitarte por tu espacio es de mucha ayuda, quiero ver si es posible me puedas ayudar, estoy haciendo un reporte en Crystal Report 8, en el cual le agrego 2 subInformes, pero no he logrado vincularlos, podrias ayudarme o mandarme un ejemplo??
     
    gracias por tu ayuda!
    saludos

  8. Santiago says:

    Gracias !!!me sirvieron d emucho sus post!!son muy buenos…me estaba crebando el coco con subreportes a patita,pero con los subreports.. todo se arregla jeje…Gracias!!!

  9. Alex says:

    HOla. tengo un problema, primero estoy utilizando el crystal para vs 2005,  y
    no entendi si al vincular los datos en la ventana de subreports links no tengo q enviarlos por codigo?
    si lo tengo q hacer, como es el codigo para realizar esto, ya q no me lee el dato SubreportObject….?
    se pueden vincular de parametro a parametro?
    se pueden vincular los parametros por la formula de seleccion de registro, porq en subreport links no aparecen los parametros del subreporte?
     
    Muchisimas gracias y espero q me puedas ayudar.

  10. Carmen says:

    Hola ALExxx,
     
    Si hemos hecho bien el report, cuando le pasemos por código el dataset él ya sabrá que tablas pertenecen al report principal y que tablas pertenecen a los subreports. Los parámetros se los podemos indicar tanto por código como por el diseñador de informes, y depende del subinforme que hagas, por ejemplo si queremos mostrar una lista de elementos que dependen de algún dato del informe principal le podremos indicar el filtro mediante la ventana de subreports links, que el subinforme es simplemente una lista de codigos y descripciones en el pie de cada hoja no necesita parámetro.
     
    Un saludo,
    Carmen

  11. alejandro says:

    Hola komo estas…keria saber si no tienes algun ejemplo d CR11 pero con visual basic 6?…estoy buscand ejemplos y solo entre lo basico…lo k necesito es komo hacer para conectar el CR11 a la bd desde VB6? xk cuando empaqueto me salta un cartel para k busque la BD…t lo agredeseria mucho.saludos alepd:mi correo es tzalejo@gmail.com

  12. Xavier E. says:

    Hola a todos y bueno lo q necesito saber como hacer para pasar parametros a un subreporte yo envio parametros al principal con lo siguiente : With crvReportes .ReportSource = Server.MapPath("nombre_reporte_prueba.rpt") .DataBind() .PrintMode = CrystalDecisions.Web.PrintMode.Pdf End With Dim parmfields As CrystalDecisions.Shared.ParameterFields = crvReportes.ParameterFieldInfo Dim parmfield As New CrystalDecisions.Shared.ParameterField() Dim parmValues As New CrystalDecisions.Shared.ParameterValues() Dim parmValue As New CrystalDecisions.Shared.ParameterDiscreteValue() parmValue.Value = "21" parmfield.Name = "@variable" parmfield.CurrentValues.Add(parmValue) parmfields.Add(parmfield) crvReportes.ParameterFieldInfo = parmfields y asi para todas las varibles corre normal pero cuando quiero enviar valores a sub reporte no los reconoce como hago??? si tienen la respuesta me ayudarian en mucho Saludos y ExitosXavierxavier_one18_fv@hotmail.com

  13. CarLuis says:

    porq tienen q relacionarse por el nombre del producto

  14. KAREN says:

    Muy buen artículo, alguien puede ayudarme con sumas grupales condicionales hacia arriba?? es decir tengo que organizar cuentas por ejemplo 1.2.01.02.001 esta cuenta tiene 5 niveles, su padre es 1.2.01.02.000, el padre de este es 1.2.01.00.000 y asi sucesivamente debo sumar primero por el 5 nivel, luego los de 4 nivel y 3 nivel hasta terminar con las de 1 nivel. Debo ir acumulando estas sumas . Si alguien sabe del tema porfavor me ayudaria en gran manera, el resultado sera un Balance General de contabilidad.

  15. jflores says:

    Me salvo la vida tu articulo, muchas gracias. !!!!

  16. chamako03 says:

    No sabes cuanto me ayudaste con el primer ejemplo , me salvaste la vida muchísimas gracias de verdad que estoy infinitamente agradecido

  17. Quetzalcoat says:

    Hola leí tu aporte para relacionar reportes con sub reporte, fue muy clara tu explicación pero la verdad, es que no se, la verdad aun no se ha solucionado el problema que tengo, tal vez sea por otra causa la cual a un no identifico y por ende tu solución no me sirvió de mucho, te explicare cual es mi problema, tengo un reporte en cristal report (.net visual studio 2010) en el cual muestro el detalle de unos residuos incinerados, pero debajo de este detalle debo mostrar el listado de tratamiento que se le aplico a cada residuos, entonces, cree un sub reporte para mostrar el listado de tratamientos; entonces voy y consulto el reporte por nro de acta y sale perfecto muestra ambos detalles pero cuando lo consulta por rangos entonces el listado de tratamientos solo lo muestra en la última acta generada.

    Te agradaré de todo corazón que me puedas colaborar y estaré al pendiente de tu respuesta.

    Gracias y espero que alguien me pueda colaborar.

  18. boris says:

    Buenisimo, donde pueso descargar el ejm, de ya gracias por too

  19. MeToplomo says:

    Excelente aporte Carmen, muy claro, y didactico. Mi saludos cordiales.

Replica a Santiago Cancelar la respuesta