Mostrando entradas con la etiqueta Crystal Reports. Mostrar todas las entradas
Mostrando entradas con la etiqueta Crystal Reports. Mostrar todas las entradas

domingo, 27 de febrero de 2011

Crystal Reports – Cargar imagen usando una capa de reportes

 

Introducción


En esta oportunidad se profundizara el trabajo de imágenes pero apuntando a Reportes, concretamente con el uso de Crystal Reports.

Para este artículo se continua con el ejemplo de uno previo:

[WinForms] Edición Empleados – Grabar imagen en base de datos

En el anterior se listaba y editaba los datos del empleado, incluida sus fotografías, en cambio en este artículo se vera como listar en un reporte esta misma información.

El resultado del reporte final del reporte seria:

imagen1

Se analizara además como incluir las imágenes provenientes de una base de datos, y también un logo tomado de un archivo de imagen.

 

Capa de Reportes


Esto quizás aplique un poco mejor con una arquitectura en capas, pero en este caso aunque no las haya definido del todo, se puede separa en un proyecto concreto la responsabilidad de crear los reportes.

Es por eso que se observara en la solución un proyecto de nombre ReportsLayer, este será el encargado de:

- Encapsular la diseño del reporte

- Definición y estructura de datos que requieres el reporte, en este caso implementada en dataset tipados

- La carga de la información, conectándose para ello directamente a los datos, esta capa no hará uso del DataAccess, porque al usar dataset la carga de datos se torna particular, por lo tanto su funcionalidad requiere una conexión directa.

 imagen2

La idea con esto es separar funcionalidad y además cubrir un defecto que tiene Crystal Reports, en donde el diseñador solo toma como entidades objetos que estén local al proyecto donde se encuentra el rpt. Muchas veces poner en la Presentación un reporte implicaría además poner allí mismo los dataset tipados, lo cual ensucia el modelo.

 

Definición y carga de datos en el DataSet


Para este reporte se definió un dataset tipado con dos datatable en su interior.

imagen3

Para al carga de los empleados se hará uso de la funcionalidad de la clase EmpleadosDAL definida dentro del propio proyecto de Reportes.

internal static class EmpleadosDAL
    {

        internal static Empleados ObtenerTodos()
        {
            Empleados empleados = new Empleados();

            using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
            {
                conn.Open();

                string query = @"SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen
                                 FROM Empleados";

                SqlCommand cmd = new SqlCommand(query, conn);

                SqlDataAdapter da = new SqlDataAdapter(cmd);
                //es necesario indicar la tabla del dataset que se quiere cargar
                da.Fill(empleados, "Empleados"); 

            }

            return empleados;
        }
    }

Allí se define la consulta en donde las columnas coinciden en nombre con los definidos en timepo de diseño en el dataset tipado, además vale aclarar que la clase ha sido declarada como internal de forma intencional, para que solo la capa de Reportes pueda usar esta funcionalidad, es mas la idea es que se limite al máximo el acceso a funcionalidad que solo esta clase debería utilizar, por eso el dataset tipado también tiene el modificador de acceso asignado a internal.

 imagen4

Un punto adicional es la carga de una imagen externa que representa el logo de la empresa, el mismo no se encuentra en la db sino que es un archivo, es por eso que luego de cargar el reporte se observan las líneas:

Empleados.EmpresaRow row = empleado.Empresa.NewEmpresaRow();
row.Logo = ImageHelper.ImageToByteArray(ImageHelper.ObtenerImagenLogoEmpresa());
empleado.Empresa.Rows.Add(row);

encargadas justamente de crear una row en al datatable con la imagen del logo. En este caso se hace uso de la funcionalidad del Helper de Imágenes creado para tomar la imagen embebida como recurso.

 

Retorno del Reporte


Esta cada de reportes solo debería ser accedida por medio de la clase Reports con su metodo ObtenerReporteEmpleados() este devolverá la instancia del reporte con la información asignada lista para ser mostrada en pantalla, o exportada si es necesario.

public static class Reports
    {

        public static ReporteEmpleados ObtenerReporteEmpleados()
        {
            ReporteEmpleados report = new ReporteEmpleados();

            //
            // Se obtienen los datos de la lista de empleados
            //
            Empleados empleado = EmpleadosDAL.ObtenerTodos();

            //
            // Se agrega el logo de la empresa a la informacion del listado
            //
            Empleados.EmpresaRow row = empleado.Empresa.NewEmpresaRow();
            row.Logo = ImageHelper.ImageToByteArray(ImageHelper.ObtenerImagenLogoEmpresa());
            empleado.Empresa.Rows.Add(row);

            //
            // Se asigna los datos a la instancia del reporte
            //
            report.SetDataSource(empleado);

            return report;
        }

    }

Lanzar el Reporte en al Presentación


El ultimo punto por tratar es como se usara lo anteriormente explicado desde la presentación.

Por un lado contamos con un formulario especialmente creado para desplegar el reporte, el mismo solo cuanta con el CrystalReportViewer y recibe por parámetro la instancia del reporte que debe mostrar.

public partial class Reporte : Form
{
    private ReportClass _report = null;

    public Reporte()
    {
        InitializeComponent();
    }

    public Reporte(ReportClass report)
        :this()
    {
        _report = report;
    }

    private void Reporte_Load(object sender, EventArgs e)
    {
        crystalReportViewer1.ReportSource = _report;
    }
}

Se define un nuevo constructor del formulario para pasar la instancia del reporte al formulario y es en el Load del mismo que se asigna al Viewer, para desplegar el reporte en pantalla.

Por otro lado tenemos un botón en la pantalla de ListaEmpleados, el cual recupera el reporte, con los datos asignados, y se la pasa a la instancia del formulario para que la muestre en pantalla.

private void btnListar_Click(object sender, EventArgs e)
{

    ReporteEmpleados report = Reports.ObtenerReporteEmpleados();

    Reporte frmReporte = new Reporte(report);
    frmReporte.Show();

}

 

Código de ejemplo


La base de datos utilizada en el ejemplo es la Sql Server Express 2008 R2, como ver en la solución el mdf esta integrado al Visual Studio, por lo tanto con solo tener el sql server express instado esta debería funciona adjuntándose sola al servicio.

En la carpeta “script” del proyecto “DataAccess”  se encuentra un archivo .sql con las instrucciones para crear la estructura de tablas y datos que se requieren para este articulo.

 

[C#] 
[VB.NET] 

miércoles, 5 de mayo de 2010

Crystal Reports – Parameters – Como asignarlos desde código .net

 

Introducción


Un problema frecuente cuando se usa Crystal Reports es el pasaje de información por medio de parámetros al reporte.

El uso de parámetros es un aspecto importante para la creación de reportes, y suele suceder que no es tan intuitiva, ni directa la forma en como se debe programar esta funcionalidad.

 

Definición del parámetro en Crystal


En este ejemplo se usara el parámetro como filtro de los datos del reporte, por lo tanto se creara en el mismo momento en que se define la información.

Para esto se usara la opción “DataBase Expert”:

En el cuadro de dialogo que se despliega, se deber crear una nueva conexión a la base de datos Access usada en el reporte:

Haciendo uso en este cuadro, no directamente la tabla mostrada, sino la opción “Add Command”, la cual permitirá definir una consulta personalizada.

Es por eso que desplegara el cuadro:

 

Y se definirá un parámetro mediante la opción de la derecha.

Solo queda definir la consulta, haciendo uso del parámetro:

Seguramente cuando se acepte el dialogo, mostrara el mensaje pidiendo el ingreso de un valor al parámetro creado, se puede ingresar un valor cualquiera, o simplemente aceptar.

En cualquier momento la consulta escrita podrá ser modificada.

Luego de arrastrar los campos al diseñador del Crystal, podría visualizarse algo como lo siguiente:

En el “Field Explorer” se puede inspeccionar tanto los campos de la consulta, como los parámetros definidos para el reporte.

 

Pasaje de parámetros desde código


Al momento de ejecutar la aplicación será necesario pasarle al reporte desde el código el filtro que se ha definido, sino se hace seguramente el propio Crystal desplegara un cuadro pidiendo el ingreso de un valor.

En el siguiente código se muestra como llevar a cabo esta operación:

 

private void Form1_Load(object sender, EventArgs e)
{
          
		  //
          // Creo el parametro y asigno el nombre
          //
          ParameterField param = new ParameterField();
          param.ParameterFieldName = "CargoParam";

          //
          // creo el valor que se asignara al parametro
          //
          ParameterDiscreteValue discreteValue = new ParameterDiscreteValue();
          discreteValue.Value = "Developer";
          param.CurrentValues.Add(discreteValue);

          //
          // Asigno el paramametro a la coleccion
          //
          ParameterFields paramFiels = new ParameterFields();
          paramFiels.Add(param);

          //
          // Asigno la coleccion de parametros al Crystal Viewer
          //
          crystalReportViewer1.ParameterFieldInfo = paramFiels;

          //
          // Creo la instancia del reporte
          //
		  crListado report = new crListado();

          //
          // Cambio el path de la base de datos
          //
		  string rutadb = Path.Combine(Application.StartupPath, "TestDb.mdb");
		  report.DataSourceConnections[0].SetConnection("", rutadb, false);

          //
          // Asigno el reporte a visor
          //
		  crystalReportViewer1.ReportSource = report;

}

 

[C#]
[VB.NET]

domingo, 2 de mayo de 2010

[Crystal Reports] Exportar DataGridView a Crystal

 

Introducción


Comúnmente cuando uno confecciona un reporte la información que se usara proviene de una tabla o consulta a una base de datos, pero suele suceder que a veces se necesite información de orígenes distintos, como ser el caso de controles del propio formulario, donde el usuario cargado información.

Este el caso planteado en el articulo, como generar un reporte en Crystal Reprots, pero utilizando información que no se encuentra en una base de datos, sino que esta aun contenida en los controles del formulario, no hay una tabla a la cual se puede vincular el reporte.

Para resolver este problema se hará uso de DataSet Tipados, los cuales no solo brindaran el esquema necesario para definir la estructura de datos que usara el reporte, sino que además será contenedor de los datos que luego se asignaran como origen de datos.

 

Información Adicional


Como referencia adicional podría mencionar dos excelente artículos que han sido de mucha ayuda, los cuales detallan muy bien como hacer uso de los reportes vinculados a DataSet Tipados, pero en este caso si toman la información mediante una consulta a la db.

Informes Crystal Reports

Creación de Reportes con Crystal Reports en Visual Studio 2005/2008

 

Creación del reporte


Uno de los primeros paso consiste en la creación del reporte, para el mismo es necesario definir los datos o mejor dicho el esquema de los mismos, tarea que será responsabilidad del DataSet Tipado.

Es por ello que en el ejemplo podrán ver un archivo de nombre dtCompra.xsd, este es precisamente el DataSet que se usara en el reporte y definirá la estructura de datos del mismo.

crystal4

En este se han creado manualmente dos DataTable que representan los datos del cliente y los ítems que ha seleccionado en la compra.

Ambas tablas se han creado de forma manual, arrastrando los datatable de la toolbox de la izquierda, y luego agregando sus columnas. Al no existir base de dato alguna, es necesario crear estas de forma manual, no se tiene ayuda del “Solution Explorer” para arrastrar tablas y crearlas dinámicamente, justamente porque no existen.

Una vez creado el esquema de los datos se procede con el reporte, para ello se agrega uno nuevo en el proyecto, en este caso con el nombre de Factura.rpt, y se configuran los datos mediante la opciones del “Field Explorer”, agregando el origen de datos, como muestra la imagen:

crystal1

Del cuadro de dialogo siguiente se selecciona el DataSet creado en el paso previo:

crystal2

Por ultimo, el asistente de Crystal permitirá redefinir como se vinculan las tablas, en este caso por representar información a distinto nivel no existe una relación, pero si se desarrollara algún reporte del estilo maestro-detalles, podría existir un vinculo entre las tablas.

crystal3

Una vez concluidos los pasos para configurar el esquema de datos del reporte, arrastrando los campos al reporte, podría armar un reporte similar a este:

crystal5

 

Carga de los datos en el DataSet Tipado


El siguiente paso consiste en volcar la información ingresada por el usuario en los controles al dataset tipado, creando en dtComrpas.xsd

Para esto se codifico un método responsable de esta tarea:

private dtCompra GenerarFactura()
{
    dtCompra facturacion = new dtCompra();

    //
    // Agrego el registro con la info del cliente
    //
    dtCompra.DatosClienteRow rowDatosCliente = facturacion.DatosCliente.NewDatosClienteRow();
    rowDatosCliente.Nombre = txtNombre.Text;
    rowDatosCliente.Direccion = txtDireccion.Text;
    rowDatosCliente.Telefono = txtTelefono.Text;
    rowDatosCliente.DNI = txtDni.Text;

    facturacion.DatosCliente.AddDatosClienteRow(rowDatosCliente);

    //
    // Itero por cada fila del DataGridView creando el registro 
    // en el DataTabla 
    //
    foreach (DataGridViewRow row in dgvCompras.Rows)
    {
        dtCompra.ComprasRow rowCompra = facturacion.Compras.NewComprasRow();  
        rowCompra.Descripcion = Convert.ToString(row.Cells["Descripcion"].Value);
        rowCompra.PrecioUnitario = Convert.ToInt32(row.Cells["PrecioUnitario"].Value);
        rowCompra.Cantidad = Convert.ToInt32(row.Cells["Cantidad"].Value);

        facturacion.Compras.AddComprasRow(rowCompra);
    }


    return facturacion;
}

Este método es fundamental ya que será el encargado de cargar los datos en una instancia del dataset tipado, para ello tomara el contenido de los controles y los agregara como registros en el DataTable que corresponda.

Es de apreciar como opera con el DataGridView, recorriendo cada fila del mismo, tomando los valores de sus celdas y creando nuevos registros en el datatable que representa las Compras.

 

Visualización del Reporte


Una vez que tenemos los datos cargados en una instancia del DataSet, solo queda asignarlos como origen de datos del reporte.

Para esta operación el reporte será mostrador en un formulario especialmente creado para tal fin.

Pero hay un detalle, los datos han sido creado en un formulario distinto, es por eso usa la técnica por medio del constructor para pasar la información de un formulario a otro. Esto podrá visualizarse en el formulario frmVisorFactura.

public partial class frmVisorFactura : Form
{
    dtCompra _datosreporte;

    private frmVisorFactura()
    {
        InitializeComponent();
    }

    public frmVisorFactura(dtCompra datos): this()
    {
        _datosreporte = datos;
    }

    private void frmVisorFactura_Load(object sender, EventArgs e)
    {
        Factura _factura = new Factura();
        _factura.SetDataSource(_datosreporte);

        crwFactura.ReportSource = _factura;
    }
}

Allí se ha agregado un constructor que acepta como parámetro el dataset que requiere el reporte para visualizarse.

También puede verse como en el evento Load se hace uso de los datos pasados al formulario y como son asignados al DataSource de la instancia del reporte.

Desde el frmCompra (donde el Usuario de la aplicación ingresa los datos), solo basta con invocar al formulario de la siguiente manera:

private void btnReporte_Click(object sender, EventArgs e)
{
    dtCompra datos = GenerarFactura();

    frmVisorFactura frm = new frmVisorFactura(datos);
    frm.Show();
}

En al primer línea se invoca al método que carga la estructura de datos, y en las siguiente se crea la instancia del formulario que contienen el control Crystal Report Viewer, al cual se le pasa en el constructor la información a visualizar.

Hay que remarcar en este punto el porque de todo esta lógica en al comunicación de los formularios. Resulta que uno de ellos es el que contiene la información y el otro es el que permite la visualización, la idea es mantener bien clara la separación de responsabilidades de cada uno, y no tener que acceder desde un formulario a los controles del otro, eso no es correcto, pero si se usa un medio que permita la comunicación, como ser en este caso el dataset tipado ,se puede trabajar en cada formulario de forma transparente y prolija.

 

[C#]
[VB.NET]

sábado, 12 de diciembre de 2009

[Crystal Reports] Usar DataSet Tipado con dos DataTable

 

Introducción 

La finalidad de este articulo es muy puntual e intentara demostrar como hacer uso en un reporte de Crystal un DataSet Tipado que contiene dos DataTable en su interior.

En este caso se necesita listar los contactos de una empresa, pero para ello no solo se debe contar con los datos de los contactos, sino que además se desea enviar en el mismo origen de datos la información de la empresa.

Para esta operación es que se hace uso de un mismo DataSet Tipado, pero este contendrá dos tablas, las cuales serán cargadas de forma independiente, pero todo el conjunto será enviado como DataSource al reporte.

Para la operación se carga del dataset es que se ha creado un método que permite bajo la misma conexión cargar ambas tablas

public static dtoContactosEmpresa GetContactosEmpresa()
{
    dtoContactosEmpresa dtContactosEmpresa = new dtoContactosEmpresa();


    using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
    {

        #region Cargo Datos Empresa

        string sql = @"SELECT IdEmpresa
                          ,RazonSocial
                          ,Telefono
                          ,Direccion
                          ,CUIT
                      FROM Empresas";

        OleDbCommand command = new OleDbCommand(sql, conn);

        OleDbDataAdapter da = new OleDbDataAdapter(command);

        da.Fill(dtContactosEmpresa, "DatosEmpresa");

        #endregion

        #region Cargo Contactos

        sql = @"SELECT Nombre
                  ,Apellido
                  ,FechaNacimiento
                  ,Localidad
                  ,Calle
                  ,Numero
                  ,Mail
              FROM Contacto";


        command = new OleDbCommand(sql, conn);

        da = new OleDbDataAdapter(command);

        da.Fill(dtContactosEmpresa, "Contactos");

        #endregion

    }

    return dtContactosEmpresa;
}

Es importante remarcar como al hacer el Fill() de los datos se especifica el nombre del datatable donde se quiere volcar la información (líneas 22 y 42)

 

[C#]
[VB.NET]

sábado, 3 de octubre de 2009

C# - Crystal Reports – Sumatoria Condicional (Conditional Sum)

 
Introducción

Cuando se requiere trabajar con Crystal en la creación de reportes un punto interesante es poder agregar condiciones a los campos de agregación, como se una sumatoria al final de la pagina, para ello tenemos herramientas en Crystal que utilizaremos en este ejemplo

Desarrollo

Para el ejemplo haremos uso de un ambiente simple en donde tendremos una lista de usuarios y sus asistencias a determinado evento.

Para ello ene le ejemplo se hará uso como origen de datos un DataSet Tipado, el cual era cargado en este caso de forma manual, pero bien podría hacerse accediendo a una base de datos

Como próximo paso se creara el reporte agregando a la sección de detalle los campos definidos en el DataSet.

Una vez que tenemos el reporte armado, se procederá a la creación del campo de sumatoria, para ello se realiza un click sobre el campos al cual se le quiere aplicar la suma y se selecciona Insert –> Running Total …

En el dialogo que se visualizara se podrá cambiar el nombre en este caso se utilizo “AsistenciasTotal”, y la parte mas importante será definir la formula que usara, para ello se deberá utilizar la opción que dice “Use a formula”

Al presionarla se visualizara otro cuadro en donde se introducirá la formula que es requerida en este caso filtraremos por un nombre, por ejemplo “Carlos”, por último se acepta el cuadro con el botón superior “Save and close”.

Cuando se acepte el ultimo cuadro seguramente los campos pueden quedar encimados, pera solucionarlo solo hace falta moverlos, ubicándolos donde sea requerido.

El reporte final, luego de ubicar correctamente los campos, quedara de esta forma:

Y al ejecutarlo el resultado será el siguiente:

Debe notarse además que si se visualiza el “Field Explorer” se encontrara el campo de sumatoria recién creado.

 

[C#]