domingo, 27 de octubre de 2013

ReportViewer y Rdlc, ejemplo Factura (Base de datos)

Hola a todos:

Este articulo es la continuación de : ReportViewer y Rdlc, ejemplo Factura (datos fijos) a si que si aun no lee ese articulo deberá de hacerlo antes de continuar con este, ya que aprovecharemos el diseño que ya tenemos creado, únicamente modificaremos el código existente en el Form1 que es el encargado de enviar los datos al Rdlc y  para extender el articulo tomaremos los datos de una Base de datos que para fines ilustrativos se hará uso de una Bd SqlCompact.

Antes de continuar descargue el proyecto de ejemplo y agregue una Base de datos Sqlcompact si no sabe como agregar una Bd sqlCompact sírvase de leer este articulo:

Agregar una Base de datos SqlCompact a un proyecto de Visual Studio

Agregue tres tablas con las siguientes características:

Tabla Articulo, contendrá toda la información de los artículos:
b1

Tabla Factura, contendrá los datos del encabezado de factura, el Id de la tabla será del tipo Identity autoincrementable, este campo representara el numero consecutivo de factura:
b2

Tabla DetalleFactura, será la tabla que conservara la relación entre la tabla Factura y Artículo, la importancia de esta tabla es que nos evita duplicar información al momento de crear un factura, ya que el Encabezado de la factura se almacena en la tabla Factura, y el detalle de la factura almacenara la relación de la factura con los artículos.
b3

Una vez que tengamos las tablas agreguemos algunos datos de ejemplo, puede descargar y estos datos aquí.

Después de que creamos las tablas y les insertamos estos datos de ejemplo, vayamos a lo que nos interesa, la codificación de nuestra aplicación.

Vaya al proyecto de Vs y agregue una nueva Clase. llámela DataAccess, esta clase será la encargada de hacer la comunicación de nuestra aplicación con la Base de datos y contendrá una función leerá y retornara información de nuestras tablas, simulando ser la capa de Acceso a Datos de una arquitectura en capas.
Recuerde agregar las referencias a los ensamblados System.Data.SqlCe y System.Configuration, si agrego una base de datos ya existente.

Dentro de la clase DataAccess crearemos una función que será la encargada de hacer la solicitud de la factura a nuestra Base de datos, quedando de la siguiente manera:

Código C#:
using System;
using System.Data.SqlServerCe;
using System.Configuration;

namespace ReportViewerInvoiceReport_CSharp
{
    public class DataAccess
    {
        /// <summary>
        /// Función encargada de hacer la solictud de información de una Factura a la base de datos
        /// </summary>
        /// <Autor>José Luis García Bautista</Autor>
        /// <param name="invoicenumber">Número de factura solicitada</param>
        /// <returns>Una instancia simple de EFactura</returns>
        public static EFactura GetFactura(int invoicenumber)
        {
            //
            //Instanciamos la clase Efactura
            //
            EFactura invoice = new EFactura();
            //
            //Abrimos la cadena de conexión, leyendoe el archivo de configuración
            //Recuerde siempre encerrar en un bloque Using la cadena de conexión para asegurar liberar
            //recuersos despues de terminar la ejecución
            //
            using (SqlCeConnection cnx = new SqlCeConnection(ConfigurationManager.ConnectionStrings["connString"].ToString()))
            {
                //
                //Abrimos la cadena de conexión
                //
                cnx.Open();
                //
                //Creamos una constante, a la cual le asignaremos nuestra consulta Sql parametrizada
                //
                const string sqlAction = @"SELECT F.Numero, F.Cliente, F.Rfc, F.Direccion, F.FechaFacturacion,  
                                                D.NumeroArticulo, A.Upc, A.Descripcion, A.Precio, D.Piezas, ROUND(A.Precio * D.Piezas, 2) AS Importe
                                         FROM Factura F
                                              INNER JOIN DetalleFactura D ON F.Numero = D.NumeroFactura
                                              INNER JOIN Articulo A ON D.NumeroArticulo = A.Numero
                                         WHERE F.Numero = @numFactura";
                //
                //Creamos nuestro comando, asignandole nuestra consulta y la conexión
                //
                using (SqlCeCommand cmd = new SqlCeCommand(sqlAction, cnx))
                {
                    //
                    //Asignamos los parametros y sus valores
                    //
                    cmd.Parameters.AddWithValue("@numFactura", invoicenumber);
                    //
                    //Creamos nuestro objeto DataReader encargada de atrapar el resultado de la ejecución de la consulta
                    //
                    SqlCeDataReader dataReader = cmd.ExecuteReader();
                    //
                    //si nuestro comando devolvio informacion, entonces nuestro DataReader contendra estos datos
                    //
                    while (dataReader.Read())
                    {
                        //
                        //Vamos asignando valores a nuestras propiedades contenidas en la instancia de EFactura
                        //tomando los valores de los campos del DataReader...
                        //
                        invoice.Numero = Convert.ToInt32(dataReader["Numero"]);
                        invoice.Nombre = Convert.ToString(dataReader["Cliente"]);
                        invoice.Rfc = Convert.ToString(dataReader["Rfc"]);
                        invoice.Direccion = Convert.ToString(dataReader["Direccion"]);
                        invoice.FechaFacturacion = Convert.ToDateTime(dataReader["FechaFacturacion"]);
                        //
                        //Creamos una instancia de EArticulo, recuerde que dentro de nuestra clase EFactura tenemos
                        //lista generica de EArticulo llamado Detail, Detail como su nombre lo indica sera la encargada de
                        //almacenar la lista de articulos que conformán el detalle de la factura, asi que crearemos una instancia
                        //de esta clase para poder agregarla a la lista
                        //
                        EArticulo articulo = new EArticulo();
                        articulo.Numero = Convert.ToInt32(dataReader["NumeroArticulo"]);
                        articulo.Upc = Convert.ToString(dataReader["Upc"]);
                        articulo.Descripcion = Convert.ToString(dataReader["Descripcion"]);
                        articulo.Precio = Convert.ToDecimal(dataReader["Precio"]);
                        articulo.Piezas = Convert.ToDecimal(dataReader["Piezas"]);
                        articulo.Importe = Convert.ToDecimal(dataReader["Importe"]);
                        invoice.Detail.Add(articulo);
                    }
                }
            }
            //
            //Por ultimo devolvemos la instancia de Efactura
            //
            return invoice;
        }

    }
}

Código Vb.Net:
Imports System.Data.SqlServerCe
Imports System.Configuration

Public Class DataAccess

    ''' <summary>
    ''' Función encargada de hacer la solictud de información de una Factura a la base de datos
    ''' </summary>
    ''' <Autor>José Luis García Bautista</Autor>
    ''' <param name="invoicenumber">Número de factura solicitada</param>
    ''' <returns>Una instancia simple de EFactura</returns>
    Public Shared Function GetFactura(invoicenumber As Integer) As EFactura
        '
        'Instanciamos la clase Efactura
        '
        Dim invoice As New EFactura()
        '
        'Abrimos la cadena de conexión, leyendoe el archivo de configuración
        'Recuerde siempre encerrar en un bloque Using la cadena de conexión para asegurar liberar
        'recuersos despues de terminar la ejecución
        '
        Using cnx As New SqlCeConnection(ConfigurationManager.ConnectionStrings("connString").ToString())
            '
            'Abrimos la cadena de conexión
            '
            cnx.Open()
            '
            'Creamos una constante, a la cual le asignaremos nuestra consulta Sql parametrizada
            '
            Const sqlAction As String = "SELECT F.Numero, F.Cliente, F.Rfc, F.Direccion, F.FechaFacturacion, " _
                                             & "D.NumeroArticulo, A.Upc, A.Descripcion, A.Precio, D.Piezas, " _
                                             & "ROUND(A.Precio * D.Piezas, 2) AS Importe " _
                                      & "FROM Factura F INNER JOIN DetalleFactura D ON F.Numero = D.NumeroFactura " _
                                      & "               INNER JOIN Articulo A ON D.NumeroArticulo = A.Numero " _
                                      & "WHERE F.Numero = @numFactura"
            '
            'Creamos nuestro comando, asignandole nuestra consulta y la conexión
            '
            Using cmd As New SqlCeCommand(sqlAction, cnx)
                '
                'Asignamos los parametros y sus valores
                '
                cmd.Parameters.AddWithValue("@numFactura", invoicenumber)
                '
                'Creamos nuestro objeto DataReader encargada de atrapar el resultado de la ejecución de la consulta
                '
                Dim dataReader As SqlCeDataReader = cmd.ExecuteReader()
                '
                'si nuestro comando devolvio informacion, entonces nuestro DataReader contendra estos datos
                '
                While dataReader.Read()
                    '
                    'Vamos asignando valores a nuestras propiedades contenidas en la instancia de EFactura
                    'tomando los valores de los campos del DataReader...
                    '
                    invoice.Numero = Convert.ToInt32(dataReader("Numero"))
                    invoice.Nombre = Convert.ToString(dataReader("Cliente"))
                    invoice.Rfc = Convert.ToString(dataReader("Rfc"))
                    invoice.Direccion = Convert.ToString(dataReader("Direccion"))
                    invoice.FechaFacturacion = Convert.ToDateTime(dataReader("FechaFacturacion"))
                    '
                    'Creamos una instancia de EArticulo, recuerde que dentro de nuestra clase EFactura tenemos
                    'lista generica de EArticulo llamado Detail, Detail como su nombre lo indica sera la encargada de
                    'almacenar la lista de articulos que conformán el detalle de la factura, asi que crearemos una instancia
                    'de esta clase para poder agregarla a la lista
                    '
                    Dim articulo As New EArticulo()
                    articulo.Numero = Convert.ToInt32(dataReader("NumeroArticulo"))
                    articulo.Upc = Convert.ToString(dataReader("Upc"))
                    articulo.Descripcion = Convert.ToString(dataReader("Descripcion"))
                    articulo.Precio = Convert.ToDecimal(dataReader("Precio"))
                    articulo.Piezas = Convert.ToDecimal(dataReader("Piezas"))
                    articulo.Importe = Convert.ToDecimal(dataReader("Importe"))
                    invoice.Detail.Add(articulo)
                End While
            End Using
        End Using
        '
        'Por ultimo devolvemos la instancia de Efactura
        '
        Return invoice
    End Function

End Class

Después de haber creado la función dentro de nuestra clase de acceso a datos, vayamos a crear la codificación para nuestro Formulario encargado de enviar los datos al Rdlc, hablo de nuestro form1, esta clase será la única que sufrirá cambios con respecto al articulo anterior, por favor remplace las líneas de código por estas existentes por las siguiente:

Código C#:
using System;
using System.Linq;
using System.Windows.Forms;

namespace ReportViewerInvoiceReport_CSharp
{
    public partial class Form1 : Form
    {
        //
        //Creamos la instancia de la clase Efactura
        //
        EFactura _factura = new EFactura();

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Función encargada de recuperar de la Base de datos la informacion de una Factura
        /// </summary>
        /// <Autor>José Luis García Bautista </Autor>
        /// <param name="invoiceNumber">Número de factura solicitada</param>
        private void FillDgv(int invoiceNumber)
        {
            try
            {
                //
                //Establecemo el valor de la instancia de la clase EFactura, el valor devuelto por la funcion GetFactura
                //de la clase DataAccess, recuerde que GetFactura devuelve un tipo EFactura.
                //
                _factura = DataAccess.GetFactura(invoiceNumber);

                //
                //Preguntamos si _factura contiene items en su detalle, si no contiene detalle, informamos
                //al usuario y nos salimos del método...
                //
                if (_factura.Detail.Count == 0)
                {
                    MessageBox.Show("La factura solicitada no existe", "Factura", MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);
                    return;
                }
                //
                //Si llegamos a este punto de ejecucion, significa que nuestra factura es valida
                //
                //Asignamos valores a los controles
                //
                txtnumero.Text = Convert.ToString(_factura.Numero);
                txtnombre.Text = _factura.Nombre;
                txtrfc.Text = _factura.Rfc;
                txtdireccion.Text = _factura.Direccion;
                dtpfecha.Value = _factura.FechaFacturacion;

                //Establecemos la propiedad AutoGenerateColumns en False para evitar que se agreguen
                //nuevas columnas a la derecha de las que creamos en tiempo de diseño.
                //
                dgvdetalle.AutoGenerateColumns = false;
                //
                //Establecemos el DataSource del control DataGridView usando la instancia EFactura / Detail
                //que previamente asignamos el valor devuelto por GetFactura
                //
                dgvdetalle.DataSource = _factura.Detail;
                //
                //Mapeamos las propiedades de la clase EArticulo, contenido en Efactura 
                //
                dgvdetalle.Columns["columnNumero"].DataPropertyName = "Numero";
                dgvdetalle.Columns["columnUpc"].DataPropertyName = "Upc";
                dgvdetalle.Columns["columnDescripcion"].DataPropertyName = "Descripcion";
                dgvdetalle.Columns["columnPiezas"].DataPropertyName = "Piezas";
                dgvdetalle.Columns["columnPrecio"].DataPropertyName = "Precio";
                dgvdetalle.Columns["columnImporte"].DataPropertyName = "Importe";

                //
                //Hacemos las sumatorias usando un método de extensión de Linq
                //como tenemos un objeto Efactura que dentro contiene una coleccion de tipo Earticulo y este objeto
                //tiene dentro la propiedad Importe que es lo que nos interesa sumar. Entonces
                //hacemos la sumatoria usando este objeto
                //
                decimal sum = _factura.Detail.Sum(x => x.Importe);
                //
                //Obtenemos el Iva (para los que no sean de México, el Iva es el Impuesto al Valor Agregado)
                //
                decimal iva = (Math.Round(((sum / 116) * 16), 2));
                //
                //Obtenemos el Subtotal
                //
                decimal subtotal = Math.Round(sum - iva, 2);

                txttotal.Text = Convert.ToString(Math.Round(sum, 2));
                txtiva.Text = Convert.ToString(iva);
                txtsubtotal.Text = Convert.ToString(subtotal);
            }
            catch (Exception e)
            {
                MessageBox.Show(String.Format("Error: {0}", e.Message), "Error inesperado", MessageBoxButtons.OK,
                MessageBoxIcon.Error);
            }
        }

        private void InvoiceGenerate()
        {
            //
            //Hamos los calculos de Total, Iva y Subtotal usando el objeto EFactura
            //
            _factura.Total = _factura.Detail.Sum(x => x.Importe);
            _factura.Iva = Math.Round(((_factura.Total/116)*16), 2);
            _factura.Subtotal = Math.Round((_factura.Total - _factura.Iva), 2);
            
            //
            //Creamos una instancia del Formulario que contiene nuestro
            //ReportViewer
            //
            FacturaRpt frm = new FacturaRpt();
            //
            //Usamos las propiedades publicas del formulario, aqui es donde enviamos el valor
            //que se mostrara en los parametros creados en el LocalReport, para este ejemplo
            //estamos Seteando los valores directamente pero usted puede usar algun control
            //
            frm.Titulo = "Este es un ejemplo de Factura";
            frm.Empresa = "Este es un ejemplo del Nombre de la Empresa";
            //
            //Recuerde que invoice es una Lista Generica declarada en FacturaRtp, es una lista
            //porque el origen de datos del LocalReport unicamente permite ser enlazado a objetos que 
            //implementen IEnumerable.
            //
            //Usamos el metod Add porque Invoice es una lista e invoice es una entidad simple
            frm.Invoice.Add(_factura);
            //
            //Enviamos el detalle de la Factura, como Detail es una lista e invoide.Details tambien
            //es un lista del tipo EArticulo bastara con igualarla
            //
            frm.Detail = _factura.Detail;
            frm.Show();
        }

        private void btnImprimir_Click(object sender, EventArgs e)
        {
            InvoiceGenerate();
        }

        private void txtnumero_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Enter & !string.IsNullOrWhiteSpace(txtnumero.Text)) FillDgv(Convert.ToInt32(txtnumero.Text));
        }
    }
}

Código Vb.Net:
Public Class Form1
    '
    'Creamos la instancia de la clase Efactura
    '
    Dim _factura As New EFactura()


    ''' <summary>
    ''' Función encargada de recuperar de la Base de datos la informacion de una Factura
    ''' </summary>
    ''' <Autor>José Luis García Bautista </Autor>
    ''' <param name="invoiceNumber">Número de factura solicitada</param>
    Private Sub FillDgv(invoiceNumber As Integer)
        Try
            '
            'Establecemo el valor de la instancia de la clase EFactura, el valor devuelto por la funcion GetFactura
            'de la clase DataAccess, recuerde que GetFactura devuelve un tipo EFactura.
            '
            _factura = DataAccess.GetFactura(invoiceNumber)

            '
            'Preguntamos si _factura contiene items en su detalle, si no contiene detalle, informamos
            'al usuario y nos salimos del método...
            '
            If _factura.Detail.Count = 0 Then
                MessageBox.Show("La factura solicitada no existe", "Factura", MessageBoxButtons.OK, MessageBoxIcon.Information)
                Return
            End If
            '
            'Si llegamos a este punto de ejecucion, significa que nuestra factura es valida
            '
            'Asignamos valores a los controles
            '
            txtnumero.Text = Convert.ToString(_factura.Numero)
            txtnombre.Text = _factura.Nombre
            txtrfc.Text = _factura.Rfc
            txtdireccion.Text = _factura.Direccion
            dtpfecha.Value = _factura.FechaFacturacion

            'Establecemos la propiedad AutoGenerateColumns en False para evitar que se agreguen
            'nuevas columnas a la derecha de las que creamos en tiempo de diseño.
            '
            dgvdetalle.AutoGenerateColumns = False
            '
            'Establecemos el DataSource del control DataGridView usando la instancia EFactura / Detail
            'que previamente asignamos el valor devuelto por GetFactura
            '
            dgvdetalle.DataSource = _factura.Detail
            '
            'Mapeamos las propiedades de la clase EArticulo, contenido en Efactura 
            '
            dgvdetalle.Columns("columnNumero").DataPropertyName = "Numero"
            dgvdetalle.Columns("columnUpc").DataPropertyName = "Upc"
            dgvdetalle.Columns("columnDescripcion").DataPropertyName = "Descripcion"
            dgvdetalle.Columns("columnPiezas").DataPropertyName = "Piezas"
            dgvdetalle.Columns("columnPrecio").DataPropertyName = "Precio"
            dgvdetalle.Columns("columnImporte").DataPropertyName = "Importe"

            '
            'Hacemos las sumatorias usando un método de extensión de Linq
            'como tenemos un objeto Efactura que dentro contiene una coleccion de tipo Earticulo y este objeto
            'tiene dentro la propiedad Importe que es lo que nos interesa sumar. Entonces
            'hacemos la sumatoria usando este objeto
            '
            Dim sum As Decimal = _factura.Detail.Sum(Function(x) x.Importe)
            '
            'Obtenemos el Iva (para los que no sean de México, el Iva es el Impuesto al Valor Agregado)
            '
            Dim iva As Decimal = (Math.Round(((sum / 116) * 16), 2))
            '
            'Obtenemos el Subtotal
            '
            Dim subtotal As Decimal = Math.Round(sum - iva, 2)

            txttotal.Text = Convert.ToString(Math.Round(sum, 2))
            txtiva.Text = Convert.ToString(iva)
            txtsubtotal.Text = Convert.ToString(subtotal)
        Catch e As Exception
            MessageBox.Show([String].Format("Error: {0}", e.Message), "Error inesperado", MessageBoxButtons.OK, MessageBoxIcon.[Error])
        End Try
    End Sub

    Private Sub InvoiceGenerate()
        '
        'Hamos los calculos de Total, Iva y Subtotal usando el objeto EFactura
        '
        _factura.Total = _factura.Detail.Sum(Function(x) x.Importe)
        _factura.Iva = Math.Round(((_factura.Total / 116) * 16), 2)
        _factura.Subtotal = Math.Round((_factura.Total - _factura.Iva), 2)

        '
        'Creamos una instancia del Formulario que contiene nuestro
        'ReportViewer
        '
        Dim frm As New FacturaRpt()
        '
        'Usamos las propiedades publicas del formulario, aqui es donde enviamos el valor
        'que se mostrara en los parametros creados en el LocalReport, para este ejemplo
        'estamos Seteando los valores directamente pero usted puede usar algun control
        '
        frm.Titulo = "Este es un ejemplo de Factura"
        frm.Empresa = "Este es un ejemplo del Nombre de la Empresa"
        '
        'Recuerde que invoice es una Lista Generica declarada en FacturaRtp, es una lista
        'porque el origen de datos del LocalReport unicamente permite ser enlazado a objetos que 
        'implementen IEnumerable.
        '
        'Usamos el metod Add porque Invoice es una lista e invoice es una entidad simple
        frm.Invoice.Add(_factura)
        '
        'Enviamos el detalle de la Factura, como Detail es una lista e invoide.Details tambien
        'es un lista del tipo EArticulo bastara con igualarla
        '
        frm.Detail = _factura.Detail
        frm.Show()
    End Sub

    Private Sub btnImprimir_Click(sender As System.Object, e As System.EventArgs) Handles btnImprimir.Click
        InvoiceGenerate()
    End Sub

    Private Sub txtnumero_KeyDown(sender As System.Object, e As System.Windows.Forms.KeyEventArgs) Handles txtnumero.KeyDown
        If e.KeyData = Keys.Enter And Not String.IsNullOrWhiteSpace(txtnumero.Text) Then
            FillDgv(Convert.ToInt32(txtnumero.Text))
        End If
    End Sub
End Class

Por ultimo nos queda probar nuestro reporte, genere el proyecto, ejecútelo, ponga un numero de factura de 1 al 5 (recuerde que solo insertamos 5 facturas de ejemplo), con el cursor en el TextBox número presione “Enter”, si hicimos las cosas con forme el articulo tendremos un formulario cargado con datos como este:

2

Ahora, presione el botón imprimir para ver nuestro reporte:
3

Listo, hemos terminado con nuestro reporte, recuerde que no siempre las cosas salen a la primera así que si algún punto no le quedo claro pues a leer de nuevo, si tiene algunas dudas siéntase con toda la confianza de hacer las preguntas pertinentes.

Por ultimo

En este articulo de ejemplo usamos una Base de datos SqlCe por la facilidad que brinda para agregarlo al proyecto, así que usamos todos los objetos de Ado.Net para manejo de Bases de datos propios para este motor, si usted cuenta con otra base de datos, recuerde que tiene que cambiar estos objetos, para ser mas claros hablo de cambiar:

SqlCeConnection por :
  • Access: OleDbConnection
  • SqlServer: SqlConnection
  • MySql : MySqlConnection
  • Sqllite : SQlLiteConnection
SqlCeCommand por:
  • Access: OleDbCommand
  • SqlServer: SqlCommand
  • MySql : MySqlCommand
  • Sqllite : SQlLiteCommand
y SqlCeDataReader  por:
  • Access: OleDataReader
  • SqlServer: SqlDataReader
  • MySql : MySqlDataReader
  • Sqllite : SQlLiteDataReader
Dicho todo lo anterior, nos leemos a la próxima

Saludos desde Monterrey, Nuevo León México!
Ejemplo C#
Ejemplo Vb.Net

Nota: El proyecto fue desarrollado en Vs2010 Ultimate usando una Bd SqlCe v3.5 y Framework 4.0

sábado, 26 de octubre de 2013

Agregar una Base de datos SqlCompact a un proyecto de Visual Studio

Hola a todos:

Para ir entrando en contexto…

Vamos a ver como agregar una Base de datos SqlCompact a un proyecto de desarrollo Vs, este ejemplo aplica tanto para proyectos de desarrollo C# o Vb.Net..

Para los que aun no conocen SqlCompact esta es la mejor definición de la misma pagina de descarga de Microsoft:

“SQL Server Compact 3.5 SP2 es una base de datos incrustada que permite a los desarrolladores crear aplicaciones sólidas para dispositivos móviles y equipos de escritorio con Windows”
Para descargar SqlCompact 3.5 Sp2 :

Service Pack 2 de Microsoft SQL Server Compact 3.5 para equipos de escritorio con Windows


SqlCompact es utilizado para desarrollo móviles, aunque tiene un potencia desde mi punto de vista superior al propio Access del paquete Microsoft Office comúnmente utilizado para pequeñas aplicaciones de escritorio, ya que puede fácilmente integrarse al ambiente de Sql Server Management Studio y operar tal cual como si se estuviera trabajando con una archivo Mdf (extensión de los archivo físicos de Bases de datos propios de Sql server), haciendo con esta una Base de datos muy potente, además de su fácil migración a Sql Server, el uso de muchas funciones propias de Sql Server lo hacen desde mi punto de vista la Bd por excelencia para una aplicación en crecimiento.

Bueno vayamos a lo que nos interesa….

Comience creando un proyecto del tipo WindowsForms, a este agregue dos botones a un llámelo btnTraer y al otro btnGuardar, un control TextBox y un control DataGridView.

Una vez que tenga creado el proyecto y agregado el botón, agregue un nuevo elemento al mismo:

Haga Click derecho sobre el nombre del Proyecto –> Agregar –> Nuevo Elemento
newItem

Del panel izquierdo seleccione Datos –>Del panel medio seleccione Base de datos Local –> Deje el nombre por default –>Presione Agregar
d1

Seleccione Conjunto de Datos –> Presione Siguiente
d2

Presione Finalizar
d3

Esto nos crea tres nuevos elementos en el proyecto que podremos observar por medio del Explorador de soluciones:
d4

El archivo de configuración App.cofig. Si abrimos el archivo dentro podremos observar la cadena de conexión:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
    </configSections>
    <connectionStrings>
        <add name="EjemploBdSqlCompact.Properties.Settings.Database1ConnectionString"
            connectionString="Data Source=|DataDirectory|\Database1.sdf"
            providerName="Microsoft.SqlServerCe.Client.3.5" />
    </connectionStrings>
</configuration>

Modifique por favor el Nombre de la cadena de conexión a.
<add name="cnnString"

DataBase1.Sdf. Este es el archivo físico de una Base de datos SqlCompact

DataBase1DataSet.xsd, este puede eliminarlo ya que nos nos servirá de nada (para la mayoría de los casos).

Ahora guarde y genere el proyecto, al generar el proyecto nuestra Bd que por defecto se crea en:

“…Documents\Visual Studio 2010\Projects\EjemploBdSqlCompact\EjemploBdSqlCompact”

se copia al directorio:

“…Documents\Visual Studio 2010\Projects\EjemploBdSqlCompact\EjemploBdSqlCompact\bin\Debug”

Bien, aquí hay un punto que resaltar y es que el Vs trabaja por defecto con la Bd que se crea en el primer directorio mencionado arriba y nuestro archivo de configuración apunta al archivo que se copia en el directorio “…Bin/Debug” después de generar el proyecto:

connectionString="Data Source=|DataDirectory|\Database1.sdf"

(al establecer el Data Source como DataDirectory, estaremos tomando la información de la Bd que se encuentra en el directorio desde donde se esta ejecutando la aplicación ósea la carpeta Debug).

Para poder utilizar un solo archivo desde el Vs, tendremos que cambiar el path de conexión con el fin de que apunte al archivo existente en Bin/Debug, para esto:

Desde el Explorador de soluciones haga doble Click sobre el elemento DataBase1.Sdf, para abrir el Explorador de Servidores.

Haga Click derecho sobre el archivo de Base de datos –> Seleccione Modificar Conexión:
d10

Seleccione Examinar –> Diríjase a la carpeta …Bin/Debug y seleccione el archivo de Base de datos –> Abrir –> Aceptar
d11

Echo lo anterior ya tenemos nuestro Vs configurado para que trabaje con el mismo archivo que nuestro App.Cofig,

Ahora Haga Click derecho sobre la Base de datos –> Seleccione Agregar Tabla
d5

Cree una tabla como la siguiente, observe que el campo Id es del tipo int, funge como clave principal y es Identity o identidad autoincrementable:

d6

Click derecho sobre la tabla que acaba de crear –> Seleccione Mostrar Datos de tabla
d7

Agregue algunos datos de ejemplo:
d8

El archivo de Base de datos que agregamos al proyecto se crea con la propiedad “Copiar en el directorio de resultados” con la opción “Copiar si es posterior” por default, si dejamos esta configuración nos traerá problemas mas adelante puesto que por cada vez que compilemos el proyecto copiara el archivo original a la carpeta Bin/Debug eliminando con esto cualquier cambio que hayamos echo con anterioridad (en realidad no elimina los cambios, sino que reemplaza el archivo por el orginal), como los datos que hayamos insertado, actualizado, cambios de tablas, etc. dando una sensación de que los cambios y actualizaciones no se estuvieran efectuando, para corregir esto:

En el Explorador de Soluciones –> Click derecho sobre el archivo de Base de datos –> Propiedades –> Establezca a la propiedad Copiar en el directorio de resultados la opción No Copiar:

d9

Después de haber echo lo anterior solo nos queda usar nuestra App.Cofig para leer nuestra Base de datos Sql Compact Edition:

Como agregamos la Bd desde el asistente de Vs automáticamente tendremos lista las referencias a la librería System.Data.SqlServerCe, en caso de agregar un archivo de Bd ya existente tendrá que crear las referencia a estas librería manualmente.

Para esto Haga Click derecho sobre le nombre del proyecto –> Seleccione Agregar Referencia –> Diríjase a la ficha .Net –> Muévase por los items de la lista hasta ubicar la librería System.Data.SqlServerCe –> Selecciónela y presione Aceptar.

Para usar el archivo de configuración tiene que hacer la referencia a la librería System.Configuration:

App. Config, cadenas de conexión

En el evento Click de uno de los botones que pusimos en el Formulario copie y pegue las siguiente líneas de código:

Código C#:
private void btnTraer_Click(object sender, EventArgs e)
{
    //
    //Encerramos la conexion en un Bloque using para asegurarse de destruir todos los objetos utilizados dentro
    //ademas de cerrar la conexion despues de ejectuar la consulta
    //
    using (SqlCeConnection cnx = new SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].ToString()))
    {
        //Creamos una variable que contendra la consulta a ejecutar
        //
        String SqlAction = "SELECT * FROM Estados";
        //
        //Creamos un comeando del tipo SqlCeCommand y le pasamos la variable que contiene
        //la consulta y la conexion
        //
        using (SqlCeCommand cmd = new SqlCeCommand(SqlAction, cnx))
        {
            //
            //Creamos un objeto DataAdapter este objeto se encarga de abrir la conexion a la Bd
            //
            SqlCeDataAdapter da = new SqlCeDataAdapter(cmd);
            //
            //Creamos un objeto DataTable que contendra los daos recuperados por el DataAdapter
            //
            DataTable dt = new DataTable();

            //
            //Llenamos el objeto DataTable con los datos recuperados por el DataAdapter
            //
            da.Fill(dt);
            //
            //Establecemos el DataSource del Control DataGridView
            //
            dataGridView1.DataSource = dt;
        }

    }
}

Código Vb.Net:
    Private Sub btnTraer_Click(sender As System.Object, e As System.EventArgs) Handles btnTraer.Click
        '
        'Encerramos la conexion en un Bloque using para asegurarse de destruir todos los objetos utilizados dentro
        'ademas de cerrar la conexion despues de ejectuar la consulta
        '
        Using cnx As New SqlCeConnection(ConfigurationManager.ConnectionStrings("cnnString").ToString())
            'Creamos una variable que contendra la consulta a ejecutar
            '
            Dim SqlAction As String = "SELECT * FROM Estados"
            '
            'Creamos un comeando del tipo SqlCeCommand y le pasamos la variable que contiene
            'la consulta y la conexion
            '
            Using cmd As New SqlCeCommand(SqlAction, cnx)
                '
                'Creamos un objeto DataAdapter este objeto se encarga de abrir la conexion a la Bd
                '
                Dim da As New SqlCeDataAdapter(cmd)
                '
                'Creamos un objeto DataTable que contendra los daos recuperados por el DataAdapter
                '
                Dim dt As New DataTable()

                '
                'Llenamos el objeto DataTable con los datos recuperados por el DataAdapter
                '
                da.Fill(dt)
                '
                'Establecemos el DataSource del Control DataGridView
                '
                dataGridView1.DataSource = dt

            End Using
        End Using
    End Sub

Ejecute la aplicación y presione el botón Traer, si siguió el articulo obtendrá un resultado como el siguiente:

d12

Ahora, probemos guardar un nuevo registro:

Copie y pegue el siguiente código en el botón Guardar:

Código C#:
private void btnGuardar_Click(object sender, EventArgs e)
{
    //
    //Encerramos la conexion en un Bloque using para asegurarse de destruir todos los objetos utilizados dentro
    //ademas de cerrar la conexion despues de ejectuar la consulta
    //
    using (SqlCeConnection cnx = new SqlCeConnection(ConfigurationManager.ConnectionStrings["cnnString"].ToString()))
    {
        //
        //Abrimos la conexion a la Base de datos
        //
        cnx.Open();
        //Creamos una variable que contendra la consulta a ejecutar
        //
        String SqlAction = "INSERT INTO Estados (Nombre) VALUES (@nombre)";
        //
        //Creamos un comeando del tipo SqlCeCommand y le pasamos la variable que contiene
        //la consulta y la conexion
        //
        using (SqlCeCommand cmd = new SqlCeCommand(SqlAction, cnx))
        {
            //
            //Establecemos valores a los parametros
            //
            cmd.Parameters.AddWithValue("@nombre", textBox1.Text.Trim());
            //
            //Ejecutamos la consulta
            //
            cmd.ExecuteNonQuery();
        }

    }
}

Código Vb.Net:
    Private Sub btnGuardar_Click(sender As System.Object, e As System.EventArgs) Handles btnGuardar.Click
        '
        'Encerramos la conexion en un Bloque using para asegurarse de destruir todos los objetos utilizados dentro
        'ademas de cerrar la conexion despues de ejectuar la consulta
        '
        Using cnx As New SqlCeConnection(ConfigurationManager.ConnectionStrings("cnnString").ToString())
            '
            'Abrimos la conexion a la Base de datos
            '
            cnx.Open()
            'Creamos una variable que contendra la consulta a ejecutar
            '
            Dim SqlAction As String = "INSERT INTO Estados (Nombre) VALUES (@nombre)"
            '
            'Creamos un comeando del tipo SqlCeCommand y le pasamos la variable que contiene
            'la consulta y la conexion
            '
            Using cmd As New SqlCeCommand(SqlAction, cnx)
                '
                'Establecemos valores a los parametros
                '
                cmd.Parameters.AddWithValue("@nombre", textBox1.Text.Trim())
                '
                'Ejecutamos la consulta
                '
                cmd.ExecuteNonQuery()

            End Using
        End Using
    End Sub

Al ejecutar la consulta inmediatamente la Bd se vera afectada:

d13

Recuerde que si trabaja sobre el archivo ubicado el Bin/Debug al momento de crear su proyecto de Setup deberá de copiar este archivo a la carpeta por default osea a:

“…Documents\Visual Studio 2010\Projects\EjemploBdSqlCompact\EjemploBdSqlCompact”

Ya que al momento de crear el proyecto este tomara la Bd de ahí para armar el paquete de instalación y cuando lo instale dejara esta archivo en el mismo directorio que el ejecutable de su aplicación…

Saludos desde Monterrey, Nuevo León México!
Ejemplo C#
Ejemplo Vb.Net
Nota: El proyecto fue desarrollado en Vs2010 usando una Bd SqlCe v3.5 y Framework 4.0 Client Profile

domingo, 20 de octubre de 2013

App. Config, cadenas de conexión

Hola a todos:

En este articulo haremos una incursión al archivo de configuración o App.Cofig para definir una cadena de conexión a una Base de Datos:
  • Agregar un archivo de configuración a un proyecto
  • Definir una cadena de conexión a una Base de datos
  • Utilizar el archivo de configuración para obtener la cadena de conexión
Entrando en contexto..
Imagine, que hoy desarrolla un sistema con 20 Formularios, la cual va a instalar en 50 Pc’s que utilizaran para la administración de su empresa, su conocimiento sobre las ventajas de usar un archivo de configuración es nula, así que usted uso lo que muchos programadores llegamos a utilizar en nuestro inicios, establecer la cadena de conexión en cada método o función donde necesitemos comunicarnos con la Bd, algo como esto:

C#:

using System.Data.SqlClient;
private void btnProbar_Click(object sender, EventArgs e)
{
    using(SqlConnection cnx = new SqlConnection("Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True"))
    {
        cnx.Open();
        MessageBox.Show(cnx.State.ToString());
    }
}

Vb.Net:

    Private Sub btnProbar_Click(sender As System.Object, e As System.EventArgs) Handles btnProbar.Click
        Using cnx = New SqlConnection("Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True")
            cnx.Open()
            MessageBox.Show(cnx.State.ToString())
        End Using
    End Sub

y para los que tienen mas experiencia pero aun no llegan a conocer los beneficios del archivo de configuración, establecer la cadena de conexión en una variable global del tipo String podría aparentar ser una buena solución ya que se asigna una sola vez y se usa en todas las consultas requeridas.

C#:

using System.Data.SqlClient;

namespace sinAppConfig
{
    public class Connection
    {
        public static SqlConnection Conexion()
        {
            return new SqlConnection("Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True");
        }
    }
}

Usamos la clase Connection:

private void btnProbar_Click(object sender, EventArgs e)
{
    //Utilizamos la clase connection para abrir la cadena de conexion
    using(SqlConnection cnx = Connection.Conexion())
    {
        cnx.Open();

        MessageBox.Show(cnx.State.ToString());
    }
}

Vb.Net:

Imports System.Data.SqlClient

Module Module1

    Public Function Connection() As SqlConnection
        Return New SqlConnection("Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True")
    End Function

End Module

Usamos la función Connection declarado en el modulo Module1:

Public Class Form1

    Private Sub btnProbar_Click(sender As System.Object, e As System.EventArgs) Handles btnProbar.Click

        'Usamos la funcion publica Connection declarada en el modulo Module1
        Using cnx = Connection()
            cnx.Open()

            MessageBox.Show(cnx.State.ToString())
        End Using
    End Sub
End Class

Bien, continuando con nuestro caso hipotético, imagínese que después de algún tiempo se le notifica de la necesidad de modificar el nombre del server o la ubicación física del mismo, trayendo con esto la necesidad de modificar nuestra cadena de conexión pero, como declaro su cadena de conexión en alguna clase publica o modulo para los de Vb.net y esta clase o modulo se comprimió junto con el Exe al momento de crear su installer ya no tiene acceso a ella desde fuera del proyecto, así que no tendrá otra opción que abrir su proyecto desde el Vs y modificar su cadena de conexión en el lugar donde lo haya declarado, si es en una clase publica o modulo solo tendrá que modificar una sola vez pero, si declaro la cadena en cada método o función donde se requería la comunicación con la Bd tendrá un problema mas grande (aunque no monstruoso), en realidad modificar la cadena de conexión no será el problema puesto que usando la combinación de teclas Ctrl + F podrá fácilmente buscarla y reemplazarla por la nueva.

El problema viene al momento de pasar esta actualización a todas las instalaciones de su Software, ya que como sabrá, al momento de hacer la modificación tendrá que compilar de nuevo los proyectos (el que contiene su desarrollo y el proyecto Setup) e ir a cada equipo donde su software este instalado, desinstalar y posteriormente instalar de nuevo su software, este es el verdadero problema ya que como se abra dado cuenta, no es tarea sencilla ya que la actividad no se resume en una acción si no en varias, ahora si consideramos la disponibilidad de los usuarios para dejarnos trabajar a nosotros mientras ellos se retrasan en sus actividades, estaremos en un problema.

¿ Solución? usar el archivo de configuración….

Si utiliza un archivo de configuración no tendrá tantos problemas, ya que el utilizarlo simplifica la tarea a únicamente ir al directorio de instalación ubicar el archivo App.Config, abrirlo con un editor de texto como NotePad, modificar su cadena de conexión, guardar y listo a seguir usando su aplicación sin mas ni mas; no necesita abrir el proyecto en el Vs ni compilar, mucho menos instalar de nuevo.

¿Comprende ahora la gran utilidad de este archivo tan simple?…

Hasta este punto creo que ya esta convencidos de la utilidad del archivo de configuración, ahora vayamos viendo como agregarlo a nuestro proyecto y como utilizarlo:

Agregar un archivo de configuración a un proyecto

En este ejemplo utilizaremos una Bd creada en SqlServer Express 2008 y Vs2010 Ultimate, la Bd es únicamente para probar la conexión, puede crear cualquiera en su Server.

Antes de comenzar cree un proyecto del tipo Windows Forms, abra el formulario y agregue un botón.

Para crear el archivo de configuración tenemos dos opciones:

Utilizar el “Asistente para la configuración de orígenes de base de datos” o insertar un nuevo elemento al proyecto del tipo App.Config, veamos paso a paso cada uno de ellos,

Utilizar el “Asistente para la configuración de orígenes de base de datos”

Menú Datos –> Agregar nuevo origen de datos

a1

a2

a3

a4

a5

a6

En esta parte, el asiste nos pregunta el nombre que asignaremos a la cadena de conexión, podemos dejar el nombre por default o poner el nombre que creamos conveniente, en esta ocasión dejemos el que nos pone por default mas adelante lo modificaremos:

a7

a8

Después de realizar todo lo anterior podrá ver en el “Explorador de soluciones” dos nuevos archivos, que son el archivo de configuración y un DataSet:

a9

El archivo DataSet por favor elimínelo, ya que no nos servirá de nada…

Si abre el archivo de configuración que acabamos de agregar a nuestro proyecto, podrá ver dentro una estructura de código Xml como el siguiente:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
    </configSections>
    <connectionStrings>
        <add name="connectionString_CSharp.Properties.Settings.DirectorioConnectionString"
             connectionString="Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True"
            providerName="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Veamos que representa cada línea:

Esta línea representa el nombre de nuestra cadena de conexión, si recuerda en uno de los pasos anteriores el asistente le preguntaba el nombre de la cadena de conexión, como dejamos el nombre por default es este el que aparece como nombre de nuestra cadena:

<add name="connectionString_CSharp.Properties.Settings.DirectorioConnectionString"

Modifiquemos esta línea con el fin de tener un nombre mas corto (con este nombre será como obtendremos el valor de nuestra Cadena):

<add name="cnxString"

La siguiente línea representa el valor de nuestra cadena de conexión:


connectionString="Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True"

y por ultimo tendremos el nombre del motor de Base de Datos que estaremos usando:
providerName="System.Data.SqlClient" />

Bien, ya vimos como usar el asistente para definir una cadena de conexión…ahora vayamos a ver

Insertar un nuevo elemento al proyecto del tipo App.Config

Haga Click derecho sobre el proyecto –> Agregar –> Nuevo Elemento:

a10

Seleccione “Archivo de configuración de aplicaciones”, deje el nombre por default y presione el botón “Aceptar”

a11

Observe que tenemos un nuevo archivo en el “Explorador de soluciones” ábralo dentro podrá ver una estructura de código como la siguiente:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

Dentro de la etiqueta <configuration> usted tendrá que agregar el nombre de la cadena de conexión, el valor de la cadena de conexión y el nombre del motor de Base de datos que estará utilizando, esto ultimo no siempre es necesario ya que depende mucho del motor que estemos utilizando porque para Bases de datos como Access, SqlCe…son necesarios para SqlServer no lo es.

Al final usted deberá de tener un archivo con la siguiente estructura de código Xml:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="cnxString"
         connectionString="Data Source=LUISESCOBAR-PC;Initial Catalog=Directorio;Integrated Security=True"
         providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

Utilizar el archivo de configuración para obtener la cadena de conexión

Ahora veamos como utilizar la cadena de conexión que acabamos de crear por cualquiera de los pasos anteriores.

Antes de poder utilizar el valor de cadena de conexión almacenada en la etiqueta connectionString de nuestro archivo de configuración, debe de agregar una referencia al ensamblado System.Configuration para esto:

Haga click derecho sobre el proyecto –> Agregar referencia –> Ficha .Net –> seleccione System.Configuration –> Aceptar

a12

Una vez echo esto, vayamos a la clase donde queramos abrir la conexión e importemos el espacio de nombres System.Configuration, echo lo anterior estamos listos para utilizar nuestro App.Config.

para C#:

using System;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Configuration;

namespace connectionString_CSharp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnconectar_Click(object sender, EventArgs e)
        {
            TestConnection();
        }

        private void TestConnection()
        {
            using(SqlConnection cnx = new SqlConnection(ConfigurationManager.ConnectionStrings["cnxString"].ToString()))
            {
                cnx.Open();

                MessageBox.Show(cnx.State.ToString());
            }
        }
    }
}

Para Vb.Net:

Imports System.Data.SqlClient
Imports System.Configuration
Public Class Form1

    Private Sub btnProbar_Click(sender As System.Object, e As System.EventArgs) Handles btnProbar.Click
        TestConnection()
    End Sub

    Private Sub TestConnection()
        Using cnx As New SqlConnection(ConfigurationManager.ConnectionStrings("cnxString").ToString())
            cnx.Open()
            MessageBox.Show(cnx.State.ToString())
        End Using
    End Sub
End Class

Bien, cuando compile su proyecto, lo instale y se le presente la necesidad de modificar la cadena de conexión, simplemente diríjase al directorio de instalación ubique el archivo de configuración (una vez instalado su software la nomenclatura de su archivo será NombredelProyecto.Exe.Config), ábralo con el notepad, modifique su cadena, guarde los cambios y sin mas ni mas podrá seguir trabajando…

Saludos desde Monterrey, Nuevo León México!
Ejemplo C#
Ejemplo Vb.Net
Nota: El proyecto fue desarrollado en Vs2010 usando una Bd SqlServer 2008 Express y Framework 4.0 Client Profile


jueves, 10 de octubre de 2013

ReportViewer y Rdlc, ejemplo Factura (datos fijos)

Hola a todos:
El siguiente articulo pretende mostrar la manera de crear un reporte local usando ReportViewer y Local Report o Rdlc enlazando su origen datos (DataSource) a una fuente proveniente de una Lista Genérica de propiedades. Como se que esto puede resultar un tema demasiado complicado cuando no se tiene experiencia en el manejo de reportes tratare de que el articulo sea lo mas descriptivo posible y para ello me apoyare en la mayor cantidad de imágenes posibles, traten de realizar el ejemplo siguiendo el tutorial, si un paso no les queda claro siéntanse con toda confianza de hacer las consultas necesarias, al final del articulo tendrán el link de descarga del proyecto de ejemplo.

Los datos los enviaremos desde un formulario simulando ser los datos de una Factura comercial, que contiene Datos del cliente y los artículos adquiridos.

Bien, comencemos creando un Formulario y arrastrando los controles necesarios hasta obtener un formulario como este:
dgvReportViewer

Agregue un segundo formulario, llámelo FacturaRpt.cs este contendrá el control ReportViewer al cual enlazaremos el LocalReport, asi que agregue uno arrastrándolo desde el cuadro de herramientas.

l19

Agregue un LocalReport, deje el nombre default:

newItem

newReport

Ahora, crearemos las clases contenedoras de las entidades, recuerde que una entidad esta compuesta por campos y propiedades.

Inserte una nueva clase y nómbrela EArticulo, la cual contendrá todas las propiedades de la entidad Articulo, la clase deberá de quedar con esta estructura:

Código C#:
namespace ReportViewerInvoiceReport_CSharp
{
    public class EArticulo
    {
        public int Numero { get; set; }
        public string Upc { get; set; }
        public string Descripcion { get; set; }
        public decimal Piezas { get; set; }
        public decimal Precio { get; set; }
        public decimal Importe { get; set; }
    }
}

Código Vb.Net:
Public Class EArticulo
    Public Property Numero() As Integer
    Public Property Upc() As String
    Public Property Descripcion() As String
    Public Property Piezas() As Decimal
    Public Property Precio() As Decimal
    Public Property Importe() As Decimal
End Class

Después inserte una nueva clase y nómbrela EFactura, esta clase contendrá todas las propiedades del encabezado de la factura, la clase deberá de quedar con esta estructura:

Código C#:
using System;
using System.Collections.Generic;

namespace ReportViewerInvoiceReport_CSharp
{
    public class EFactura
    {
        public int Numero { get; set; }
        public string Nombre { get; set; }
        public string Rfc { get; set; }
        public string Direccion { get; set; }
        public decimal Subtotal { get; set; }
        public decimal Iva { get; set; }
        public decimal Total { get; set; }
        public DateTime FechaFacturacion { get; set; }

        //Creamos una lista con una nueva Instancia de la clase Articulo
        //esta lista contendra el detalle de la factura
        public List<EArticulo> Detail = new List<EArticulo>();
    }
}

Código Vb.Net:
Imports System.Collections.Generic
Public Class EFactura
    Public Property Numero() As Integer
    Public Property Nombre() As String
    Public Property Rfc() As String
    Public Property Direccion() As String
    Public Property Subtotal() As Decimal
    Public Property Iva() As Decimal
    Public Property Total() As Decimal
    Public Property FechaFacturacion() As DateTime

    'Creamos una lista con una nueva Instancia de la clase Articulo
    'esta lista contendra el detalle de la factura
    Public Detail As New List(Of EArticulo)()

End Class

Configurar el Reporte del informe “Report1.rdlc”

Para poder usar las clases de EFactura para llenar el Encabezado del reporte y EArticulo para el detalle del mismo, primero debemos de generar el proyecto, para ello localice el menú Generar –> Generar Solución

 Establecer la fuente de datos del Reporte

Active el cuadro Datos del informe, Menú Ver –>Datos del informe

dataSet

Establezca el Nombre del conjunto de datos y haga click sobre el botón Nuevo:

dataS1

Seleccione Objeto y haga Click sobre el botón Siguiente:

 obje

Seleccione la clase EFactura, recuerde que esta clase contiene las propiedades para el encabezado de la factura y haga Click sobre el botón Finalizar (Si no hubiera generado el proyecto estas clases no estarían visibles, así que si no las ve por favor cierre la ventana y genere la solución):

 sele

Observe que ya tiene una fuente de datos seleccionada, solo haga Click en el botón Aceptar:

l1

Bien, ya tiene una Fuente de datos configurada:

l2

Siga los mismos pasos para agregar la fuente de datos para el detalle del reporte solo que ahora seleccionara la clase EArticulo, al final tendrá un resultado como el siguiente:

l3

Configurar tamaño y encabezado del informe

Active la regla del informe para ello menú Informe –> Regla

Configure el tamaño del informe, menú Informe –> Propiedades del informe

l4

Un punto muy importante en este paso es tener presente el Ancho o alto del reporte dependiendo que tipo de orientación se haya elegido, en este caso el ancho es lo que interesa, considere que tiene un ancho máximo de 21.59 cm. a este espacio le restara el valor total de los márgenes que son 2.0 cm en total ya que configuro un margen de 0.5 cm, dando un espacio de trabajo real disponible de 19.59 cm., si en el diseño de nuestro reporte nos pasamos de este espacio disponible nuestro reporte en lugar de mostrarnos una hoja nos mostrar ara dos, normalmente la segunda hoja sin o con pocos datos.

Bien, continuemos…

Agregue un encabezado de reporte, menú Informe –> Agregar encabezado de pagina

Active el cuadro de herramientas, menú Ver –> Cuadro de herramientas:

l5

Arrastre tantos Cuadros de texto como se requieran, recuerde que nuestros datos provienen de una clase, entonces por cada campo-propiedad de la clase tiene que crear un control Cuadro de texto para poder visualizar el dato…esto de crear un control por cada propiedades de la clase origen es únicamente para este reporte, en realidad usted puede elegir que datos mostrar y cuales no.

Configurar control cuadro de texto:

Click derecho sobre el control cuadro de texto que deseemos configurar:

Recuerde que establecer un nombre a los controles que vayamos utilizando es de vital importancia porque será en base a este como los identificaremos, en proyectos pequeños tal vez no tenga problemas si deja los nombres asignados por default pero, en proyectos grandes esta mala practica nos provocara constantes dolores de cabeza, entonces, nunca olvide ponerle un nombre a los controles. Considere que el nombre debe de identificar fácilmente al control al que hace referencia y al dato que contendrá o al cual estará relacionado, en este caso txtFactura es muy descriptivo, ya que se entiende que es un control textBox o cuadro de texto y que contendrá el numero de factura.

Para definir el origen del dato que mostrara el control cuadro de texto despliegue el ComboBox Valor, vera que este estará cargado con las propiedades de las clases las cuales definimos como fuente de datos, no le será difícil identificar a que clase pertenece cada una ya que al final podra ver el nombre que le puso al origen de datos.

l10

Desactivar la casilla “Permitir aumentar el alto” puede resultar muy importante para conservar un buen diseño, tener activada esta casilla permite que el control cambie de tamaño en tiempo de ejecución adecuándose al tamaño del valor que contiene, modificando con esto el diseño hasta el punto de generar hojas innecesarias.

l6

l7

l8

En este ejemplo el campo Factura será del tipo numérico por lo cual para conservar un formato de numero correcto, tendrá que configurar el lenguaje que utilizara el campo, para mi ubicación (México) la configuración de lenguaje será “es-Mex”, para configurar esta propiedad:

  Seleccione el control cuadro de texto –> Presione F4:

l9

Si no hiciera esto, un formato numérico podría no mostrarse correctamente por ejemplo la cantidad Mil doscientos cincuenta y seis con cincuenta y seis decimales en México será 1,256.56 pero en España se escribiría de esta manera 1.256,56. Esta configuración debemos de hacerla aun si en la clase Main de nuestro proyecto hayamos definido un lenguaje de manera predeterminada.

Agregar parámetros al Reporte:

Para abarcar mas sobre el tema de ReportViewer y local report, subamos un poco mas el nivel de nuestro proyecto agregando parámetros al mismo, esto no siempre es necesario así que en este articulo solo lo haremos para fines ilustrativos, es decir, para cuando lleguemos a necesitarlo tendremos el conocimiento disponible de como usarlos.

En el Datos del informe –> Click derecho sobre Parametros –> Agregar parametro:

l11

De nuevo, recuerde que nombrar correctamente a cada control que agregamos es de vital importancia:

l12

Agregue un  nuevo parámetro y nómbrelo parametroEmpresa.

l13

Ahora, arrastre dos controles Cuadro de texto al informe, y vaya a las propiedades para configurar el valor a mostrar:

l14

Haga lo mismo para el segundo control agregado, veamos como va nuestro diseño del informe:

l15

Para el detalle del informe, agregue un Tabla arrastrándola desde el cuadro de herramientas, vaya a las propiedades de la tabla recién agregada, ubique la propiedad DataSetName par establecer el origen de datos y seleccione Detalle, recuerde que ese fue el nombre que establecimos al origen de datos:

l16

Agregue las columnas que falten para completar el cuerpo del informe, para ello haga Click derecho sobre la tabla, Insertar columna, Izquierda o derecha.

Después establezca el valor que mostrara cada columna, para ello tiene dos opciones, seleccionar el valor desde el icono que vera en la esquina superior derecha de cada celda o ir a propiedades del cuadro de texto tal cual como lo hizo anteriormente:

l17

Establezca el tamaño de fuente, borde, tipo de dato, lenguaje a utilizar (recuerde lo comentado anteriormente sobre el formato de numero/moneda)

¿Como va nuestro diseño de informe?

l18

Si tienen un resultado como este, pueden dar su tarea por terminada en cuanto al diseño del informe si aun no lo logran, todo es cuestión que revisen como configurar un control TextBox o cuadro de texto como gusten llamarlo y ponerse a ver Alineación, tipo de fuente, borde, relleno, etc…

Hasta aquí daremos por terminado nuestro diseño, ahora vayamos a:

Establecer una Lista Genérica como DataSource de un local report:

Ubique el Formulario contenedor del ReportViewer llamado FacturaRpt, seleccione el icono superior derecho del reportViewer –> Seleccione el local report que utilizaremos (el único que hemos diseñado)

l20

Una vez echo lo anterior, genere el evento Load del Formulario, para ello puede hacer doble click sobre la barra de titulo o seleccionar el form y presionar la tecla de función F4, esto abrirá la caja de propiedades, en la parte superior podrá ver un icono con forma de “rayo” de color amarillo, haga click sobre el para ver los eventos que implementa el control, después ubique el Evento que le interese implementar en este caso nos interesa el evento “Load”.

Copie y pegue el siguiente código:

Código C#:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using Microsoft.Reporting.WinForms;

namespace ReportViewerInvoiceReport_CSharp
{
    public partial class FacturaRpt : Form
    {
        //
        //Cree dos listas una para el Encabezado y otra para el detalle
        //
        public List<EFactura> Invoice = new List<EFactura>();
        public List<EArticulo> Detail = new List<EArticulo>();
        //
        //Cree las propiedades publicas Titulo y Empresa
        //
        public string Titulo { get; set; }
        public string Empresa { get; set; }

        public FacturaRpt()
        {
            InitializeComponent();
        }

        private void FacturaRpt_Load(object sender, EventArgs e)
        {
            //Limpiemos el DataSource del informe
            reportViewer1.LocalReport.DataSources.Clear();
            //
            //Establezcamos los parámetros que enviaremos al reporte
            //recuerde que son dos para el titulo del reporte y para el nombre de la empresa
            //
            ReportParameter[] parameters = new ReportParameter[2];
            parameters[0] = new ReportParameter("parameterTitulo", Titulo);
            parameters[1] = new ReportParameter("parameterEmpresa", Empresa);

            //
            //Establezcamos la lista como Datasource del informe
            //
            reportViewer1.LocalReport.DataSources.Add(new ReportDataSource("Encabezado", Invoice));
            reportViewer1.LocalReport.DataSources.Add(new ReportDataSource("Detalle", Detail));
            //
            //Enviemos la lista de parametros
            //
            reportViewer1.LocalReport.SetParameters(parameters);
            //
            //Hagamos un refresh al reportViewer
            //
            reportViewer1.RefreshReport();
        }
    }
}

Código Vb.Net:
Imports Microsoft.Reporting.WinForms

Public Class FacturaRpt
    '
    'Cree dos listas una para el Encabezado y otra para el detalle
    '
    Public Invoice As New List(Of EFactura)()
    Public Detail As New List(Of EArticulo)()
    '
    'Cree las propiedades publicas Titulo y Empresa
    '
    Public Property Titulo() As String
    Public Property Empresa() As String

    Private Sub FacturaRpt_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load

        'Limpiemos el DataSource del informe
        ReportViewer1.LocalReport.DataSources.Clear()
        '
        'Establezcamos los parametros que enviaremos al reporte
        'recuerde que son dos para el titulo del reporte y para el nombre de la empresa
        '
        Dim parameters As ReportParameter() = New ReportParameter(1) {}
        parameters(0) = New ReportParameter("parameterTitulo", Titulo)
        parameters(1) = New ReportParameter("parameterEmpresa", Empresa)

        '
        'Establezcamos la lista como Datasource del informe
        '
        ReportViewer1.LocalReport.DataSources.Add(New ReportDataSource("Encabezado", Invoice))
        ReportViewer1.LocalReport.DataSources.Add(New ReportDataSource("Detalle", Detail))
        '
        'Enviemos la lista de parametros
        '
        ReportViewer1.LocalReport.SetParameters(parameters)
        '
        'Hagamos un refresh al reportViewer
        '
        ReportViewer1.RefreshReport()
    End Sub
End Class


Hagamos lo ultimo que falta, enviar los datos desde el formulario principal:

Genere el Evento load del Formulario principal,

VsEvent

Después de generado el evento trate de seguir estas líneas de código, escriba linea por linea si copia y pega será mas complicado que comprenda (suponiendo que su experiencia en desarrollo no es amplia):


Código C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace ReportViewerInvoiceReport_CSharp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Funcion encargada de llenar el control DataGridView
        /// <autor>José Luis García Bautista</autor>
        /// </summary>
        /// <returns>Una lista generica de la clase artículo</returns>
        private static List<EArticulo> FillDgv()
        {
            //
            //Cree una lista generica de la entidad EArticulo
            //
            List<EArticulo> listaArticulos = new List<EArticulo>();

            //
            //Instancie la clase EArticulo para agregar datos a la lista
            //
            EArticulo item = new EArticulo
                                {
                                    //Establezca valores a cada una de las propiedades
                                    Numero = 1,
                                    Upc = "7501020405680",
                                    Descripcion = "Descripción del artículo 1",
                                    Piezas = 6,
                                    Precio = new decimal(12.50),
                                    Importe = (decimal)(6 * 12.5),
                                };
            //
            //Agregamos el Item a la lista
            //
            listaArticulos.Add(item);

            EArticulo item1 = new EArticulo
                                 {
                                     Numero = 2,
                                     Upc = "7501040805610",
                                     Descripcion = "Descripción del artículo 2",
                                     Piezas = 3,
                                     Precio = new decimal(22.50),
                                     Importe = (decimal)(3 * 22.5),
                                 };
            listaArticulos.Add(item1);

            EArticulo item2 = new EArticulo
            {
                Numero = 3,
                Upc = "0412200805610",
                Descripcion = "Descripción del artículo 3",
                Piezas = 20,
                Precio = new decimal(52.80),
                Importe = (decimal)(20 * 52.80),
            };
            listaArticulos.Add(item2);

            return listaArticulos;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //
            //La funcion GenerateNumber() se utiliza unicamente para generar un Número
            //aleatorio que simulara ser el numerod e factura
            txtnumero.Text = GenerateNumber().ToString();

            //Establecemos la propiedad AutoGenerateColumns en False para evitar que se agreguen
            //nuevas columnas a la derecha de las que creamos en tiempo de diseño.
            //
            dgvdetalle.AutoGenerateColumns = false;
            //
            //Establecemos el DataSource del control DataGridView
            //
            dgvdetalle.DataSource = FillDgv();
            //
            //Mapeamos las propiedades de la clase devuelta por la Funcion FillDgv()
            //recuerde que esta funcion devuelve una lista del tipo EArticulo
            //
            dgvdetalle.Columns["columnNumero"].DataPropertyName = "Numero";
            dgvdetalle.Columns["columnUpc"].DataPropertyName = "Upc";
            dgvdetalle.Columns["columnDescripcion"].DataPropertyName = "Descripcion";
            dgvdetalle.Columns["columnPiezas"].DataPropertyName = "Piezas";
            dgvdetalle.Columns["columnPrecio"].DataPropertyName = "Precio";
            dgvdetalle.Columns["columnImporte"].DataPropertyName = "Importe";

            //
            //Hacemos las sumatorias usando un método de extensión de Linq
            //
            decimal sum = FillDgv().Sum(x => x.Importe);
            decimal iva = (Math.Round(((sum / 116) * 16), 2));
            decimal subtotal = Math.Round(sum - iva, 2);

            txttotal.Text = Convert.ToString(Math.Round(sum, 2));
            txtiva.Text = Convert.ToString(iva);
            txtsubtotal.Text = Convert.ToString(subtotal);
        }

        private static int GenerateNumber()
        {
            Random rdm = new Random();

            return rdm.Next();
        }


        private void InvoiceGenerate()
        {
            //
            //Hacemos una instancia de la clase EFactura para
            //llenarla con los valores contenidos en los controles del Formulario
            EFactura invoice = new EFactura();
            invoice.Numero = Convert.ToInt32(txtnumero.Text);
            invoice.Nombre = txtnombre.Text;
            invoice.Rfc = txtrfc.Text;
            invoice.Direccion = txtdireccion.Text;
            invoice.FechaFacturacion = dtpfecha.Value.Date;
            invoice.Subtotal = Convert.ToDecimal(txtsubtotal.Text);
            invoice.Iva = Convert.ToDecimal(txtiva.Text);
            invoice.Total = Convert.ToDecimal(txttotal.Text);

            //Recorremos los Rows existentes actualmente en el control DataGridView
            //para asignar los datos a las propiedades
            foreach (DataGridViewRow row in dgvdetalle.Rows)
            {
                EArticulo article = new EArticulo();
                //
                //Vamos tomando los valores de las celdas del row que estamos 
                //recorriendo actualmente y asignamos su valor a la propiedad de la clase intanciada
                //
                article.Numero = Convert.ToInt32(row.Cells["columnNumero"].Value);
                article.Upc = Convert.ToString(row.Cells["columnUpc"].Value);
                article.Descripcion = Convert.ToString(row.Cells["columnDescripcion"].Value);
                article.Piezas = Convert.ToDecimal(row.Cells["columnPiezas"].Value);
                article.Precio = Convert.ToDecimal(row.Cells["columnPrecio"].Value);
                article.Importe = Convert.ToDecimal(row.Cells["columnImporte"].Value);

                //
                //Vamos agregando el Item a la lista del detalle
                //
                invoice.Detail.Add(article);
            }

            //
            //Creamos una instancia del Formulario que contiene nuestro
            //ReportViewer
            //
            FacturaRpt frm = new FacturaRpt();
            //
            //Usamos las propiedades publicas del formulario, aqui es donde enviamos el valor
            //que se mostrara en los parametros creados en el LocalReport, para este ejemplo
            //estamos Seteando los valores directamente pero usted puede usar algun control
            //
            frm.Titulo = "Este es un ejemplo de Factura";
            frm.Empresa = "Este es un ejemplo del Nombre de la Empresa";
            //
            //Recuerde que invoice es una Lista Generica declarada en el FacturaRtp, es una lista
            //porque el origen de datos del LocalReport unicamente permite ser enlazado a objetos que 
            //implementen IEnumerable.
            //
            //Usamos el metod Add porque Invoice es una lista e invoice es una entidad simple
            frm.Invoice.Add(invoice);
            //
            //Enviamos el detalle de la Factura, como Detail es una lista e invoide.Details tambien
            //es un lista del tipo EArticulo bastara con igualarla
            //
            frm.Detail = invoice.Detail;
            frm.Show();
        }

        private void btnImprimir_Click(object sender, EventArgs e)
        {
            InvoiceGenerate();
        }
    }
}

Código Vb.Net:
Public Class Form1

    ''' <summary>
    ''' Funcion encargada de llenar el control DataGridView
    ''' <autor>José Luis García Bautista</autor>
    ''' </summary>
    ''' <returns>Una lista generica de la clase artículo</returns>
    Private Shared Function FillDgv() As List(Of EArticulo)
        '
        'Cree una lista generica de la entidad EArticulo
        '
        Dim listaArticulos As New List(Of EArticulo)()
        '
        'Instancie la clase EArticulo para agregar datos a la lista
        '
        'Establezca valores a cada una de las propiedades
        Dim item As New EArticulo()
        item.Numero = 1
        item.Upc = "7501020405680"
        item.Descripcion = "Descripción del artículo 1"
        item.Piezas = 6
        item.Precio = New Decimal(12.5)
        item.Importe = CDec(6 * 12.5)
        '
        'Agregamos el Item a la lista
        '
        listaArticulos.Add(item)

        Dim item1 As New EArticulo()
        item1.Numero = 2
        item1.Upc = "7501040805610"
        item1.Descripcion = "Descripción del artículo 2"
        item1.Piezas = 3
        item1.Precio = New Decimal(22.5)
        item1.Importe = CDec(3 * 22.5)

        listaArticulos.Add(item1)

        Dim item2 As New EArticulo()
        item2.Numero = 3
        item2.Upc = "0412200805610"
        item2.Descripcion = "Descripción del artículo 3"
        item2.Piezas = 20
        item2.Precio = New Decimal(52.8)
        item2.Importe = CDec(20 * 52.8)
        listaArticulos.Add(item2)

        Return listaArticulos
    End Function


    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        '
        'La funcion GenerateNumber() se utiliza unicamente para generar un Número
        'aleatorio que simulara ser el numerod e factura
        txtnumero.Text = GenerateNumber().ToString()

        'Establecemos la propiedad AutoGenerateColumns en False para evitar que se agreguen
        'nuevas columnas a la derecha de las que creamos en tiempo de diseño.
        '
        dgvdetalle.AutoGenerateColumns = False
        '
        'Establecemos el DataSource del control DataGridView
        '
        dgvdetalle.DataSource = FillDgv()
        '
        'Mapeamos las propiedades de la clase devuelta por la Funcion FillDgv()
        'recuerde que esta funcion devuelve una lista del tipo EArticulo
        '
        dgvdetalle.Columns("columnNumero").DataPropertyName = "Numero"
        dgvdetalle.Columns("columnUpc").DataPropertyName = "Upc"
        dgvdetalle.Columns("columnDescripcion").DataPropertyName = "Descripcion"
        dgvdetalle.Columns("columnPiezas").DataPropertyName = "Piezas"
        dgvdetalle.Columns("columnPrecio").DataPropertyName = "Precio"
        dgvdetalle.Columns("columnImporte").DataPropertyName = "Importe"
        '
        'Hacemos las sumatorias usando un método de extensión de Linq
        '
        Dim sum As Decimal = FillDgv().Sum(Function(x) x.Importe)
        Dim iva As Decimal = (Math.Round(((sum / 116) * 16), 2))
        Dim subtotal As Decimal = Math.Round(sum - iva, 2)

        txttotal.Text = Convert.ToString(Math.Round(sum, 2))
        txtiva.Text = Convert.ToString(iva)
        txtsubtotal.Text = Convert.ToString(subtotal)
    End Sub

    Private Shared Function GenerateNumber() As Integer
        Dim rdm As New Random()

        Return rdm.[Next]()
    End Function

    Private Sub InvoiceGenerate()
        '
        'Hacemos una instancia de la clase EFactura para
        'llenarla con los valores contenidos en los controles del Formulario
        Dim invoice As New EFactura()
        invoice.Numero = Convert.ToInt32(txtnumero.Text)
        invoice.Nombre = txtnombre.Text
        invoice.Rfc = txtrfc.Text
        invoice.Direccion = txtdireccion.Text
        invoice.FechaFacturacion = dtpfecha.Value.[Date]
        invoice.Subtotal = Convert.ToDecimal(txtsubtotal.Text)
        invoice.Iva = Convert.ToDecimal(txtiva.Text)
        invoice.Total = Convert.ToDecimal(txttotal.Text)

        'Recorremos los Rows existentes actualmente en el control DataGridView
        'para asignar los datos a las propiedades
        For Each row As DataGridViewRow In dgvdetalle.Rows
            Dim article As New EArticulo()
            '
            'Vamos tomando los valores de las celdas del row que estamos 
            'recorriendo actualmente y asignamos su valor a la propiedad de la clase intanciada
            '
            article.Numero = Convert.ToInt32(row.Cells("columnNumero").Value)
            article.Upc = Convert.ToString(row.Cells("columnUpc").Value)
            article.Descripcion = Convert.ToString(row.Cells("columnDescripcion").Value)
            article.Piezas = Convert.ToDecimal(row.Cells("columnPiezas").Value)
            article.Precio = Convert.ToDecimal(row.Cells("columnPrecio").Value)
            article.Importe = Convert.ToDecimal(row.Cells("columnImporte").Value)

            '
            'Vamos agregando el Item a la lista del detalle
            '
            invoice.Detail.Add(article)
        Next

        '
        'Creamos una instancia del Formulario que contiene nuestro
        'ReportViewer
        '
        Dim frm As New FacturaRpt()
        '
        'Usamos las propiedades publicas del formulario, aqui es donde enviamos el valor
        'que se mostrara en los parametros creados en el LocalReport, para este ejemplo
        'estamos Seteando los valores directamente pero usted puede usar algun control
        '
        frm.Titulo = "Este es un ejemplo de Factura"
        frm.Empresa = "Este es un ejemplo del Nombre de la Empresa"
        '
        'Recuerde que invoice es una Lista Generica declarada en FacturaRtp, es una lista
        'porque el origen de datos del LocalReport unicamente permite ser enlazado a objetos que 
        'implementen IEnumerable.
        '
        'Usamos el metod Add porque Invoice es una lista e invoice es una entidad simple
        frm.Invoice.Add(invoice)
        '
        'Enviamos el detalle de la Factura, como Detail es una lista e invoide.Details tambien
        'es un lista del tipo EArticulo bastara con igualarla
        '
        frm.Detail = invoice.Detail
        frm.Show()
    End Sub

    Private Sub btnImprimir_Click(sender As System.Object, e As System.EventArgs) Handles btnImprimir.Click
        InvoiceGenerate()
    End Sub
End Class
Ahora solo queda probar nuestra aplicación:

Llenamos los datos:
l21

Presione el botón Imprimir, si siguió al pie de la letra el Articulo tendrá un reporte como este:

l22

Observe, en el reporte el Formato numérico y Módena, al igual que no tenemos hojas de mas sin datos…

Ahora, exporte el reporte a Pdf y observe que los márgenes están dentro del limite, sin generar hojas de mas, ni salirse de los márgenes:

l23

Bueno, hemos llegado al final de este tutorial, en un entrega posterior abordaremos las imágenes en Reportes Locales y utilizaremos una Bd como fuente de datos para nuestro reporte.

Saludos desde Monterrey, Nuevo León México!

Ejemplo C#
Ejemplo Vb.Net
Nota: El proyecto fue desarrollado en Vs2010 Ultímate usando Framework 4.0