domingo, 1 de diciembre de 2013

Pasar datos de un DataGridView a otro DataGridView ubicados en Forms diferentes

Hola a todos:

Motivado por uno de los usuarios del Foro de C# me tomo unos minutos parar mostrar como podemos fácilmente enviar valores de un DataGridView a otro DataGridView ubicado en un formulario diferente.
Como siempre pondré el código tanto para C# como para Vb.Net, y tratare de ser los mas explicito posible, si después de leer el articulo, descargar el proyecto de ejemplo y analizar su contenido usted no logra implementarlo, sírvase de hacer las consultas que crea necesarias proporcionando la mayor cantidad de información de su caso.

Recuerde que el objetivo del articulo no es otro mas que el de proporcionar una guía para lo Párvulos en .Net y que solo es una opción para la solución de un problema, recordando que un problema tiene muchas posibles soluciones.

Recuerde que al final de Articulo podrá encontrar los proyectos de ejemplo descargables tanto para C# y Vb.Net.

Entrando en contexto…

Crearemos un proyecto que simulara la carga de artículos de un Formulario a otro Formulario utilizando controles DataGridView para mostrar los datos.
  1. El proyecto arrancara con el Form1 como principal
  2. Al presionar el botón “Agregar” se mostrara el Form2 con un control DataGridView cargado con datos
  3. Al hacer doble Click sobre algún row del DataGridView del Form2 automáticamente se enviara la información contenida en sus celdas al DataGridView del form1, mostrándose también en los controles TextBox.
  4. El proceso antes de agregar el nuevo Row al DataGridView hará una validación sobre los datos ya agregados para descartar que este articulo no se encuentre ya en la lista, de ser así lanzara una mensaje de informando de la situación y no agregara el row, en caso contrario el row será agregado sin problemas.
Dicho todo lo anterior, procedamos a crear nuestro proyecto de ejemplo.
  1. Cree un proyecto del tipo WindowsForms
  2. Automáticamente le creo un formulario llamado Form1, dejemos el nombre de los controles por defecto para no complicarnos la vida nombrándolos en estos momentos, aunque recalco que en proyecto reales el nombre de los controles es vital.
  3. Agregue 4 controles labels y establezca en la propiedad Text, Número, Nombre, Precio y Cantidad respectivamente
  4. Agregue 4 controles TextBox
  5. Agregue un control Button y establezca en la propiedad Text, Cargar.
  6. Agregue un control DataGridView, agregue 4 columnas, establezca la propiedad SelectionMode en FullRowSelect, AllowUserToAddRows, AllowUserToDeleteRows en false
Su diseño podría ser similar a este:
frm1

Ahora, en el Form dos inserte un control DataGridView similar al que acaba de crear en el Form1, para optimizar el tiempo puede copiar y pegar el control del Form1, el diseño que se espera obtenga es similar a este:
frm2
Bien, una vez que tengamos el tema del diseño de nuestros Forms resueltos, procedamos a meter código.
Inserte una nueva clase a su proyecto y llámela EProducto, la clase tendrá una estructura como esta:
C# :
using System;

namespace DataGridViewsendRow
{
    public class EProducto
    {
        //Estas son propiedades Autoimplementadas y su uso requiere
        // del Framework 4.0 Client Profile como mínimo
        public int Numero { get; set; }
        public string Nombre { get; set; }
        public Decimal Precio { get; set; }
        public Decimal Cantidad { get; set; }
    }
}

Vb.Net :
Public Class EProducto
    'Estas son propiedades Autoimplementadas y su uso requiere
    'del Framework 4.0 Client Profile como mínimo
    Public Property Numero() As Integer
    Public Property Nombre() As String
    Public Property Precio() As Decimal
    Public Property Cantidad() As Decimal
End Class

Agregue una nueva clase y llámela IProducto, esta clase contendrá una interfaz que será el puente de comunicación entre ambos Forms, este es el Core de nuestro proyecto y ejemplo.

Dentro de la clase cree la siguiente estructura de código:

C# :
namespace DataGridViewsendRow
{
    //Nombre de la interfaz
    public interface IProducto
    {
        bool LoadDataRow(EProducto producto);
    }
}

Vb.Net :
'Nombre de la interfazPublic Interface IProducto
    Function LoadDataRow(producto As EProducto) As Boolean
End Interface

Esta es la estructura completa de código que necesitara para recibir los datos en el Form1:

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

namespace DataGridViewsendRow
{
    //
    //Heredamos la Interfaz IProducto, recuerde que este será nuestro puente de comunicación entre ambos Forms
    public partial class Form1 : Form, IProducto
    {
        //
        //Creamos una lista del tipo Eproducto, que será la encargada de
        //recibir la información del Form2 y almacenarla, posteriormente servirá
        //de fuente de datos para el DataGridView
        private static readonly List<EProducto> Products = new List<EProducto>();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //
            //Instanciamos el Form2
            Form2 frm = new Form2();
            //
            //Le indicamos quien lo mando a llamar usando la Propiedad Caller
            frm.Caller = this;
            //
            //Mostramos el Form2
            frm.ShowDialog();
        }

        /// <summary>
        /// Funcion encargada de agregar un nuevo Item a la lista siempre y cuando no exista
        /// </summary>
        /// <param name="product">instancia de la clase Eproduct</param>
        /// <returns>Si el Item fue agregado a la lista devuelve True si no fue agregado retorna False</returns>
        public bool LoadDataRow(EProducto product)
        {
            //Busca si el Articulo ya se encuentra en la lista
            bool exists = Products.Any(x => x.Numero.Equals(product.Numero));
            //
            //
            //Preguntamos por el resultado de la búsqueda del Articulo dentro de la lista
            if (!exists)
            {
                //
                //Si el articulo no existe dentro de la lista Mapeamos el item de la entidad Eproducto 
                //a los TextBox
                textBox1.Text = Convert.ToString(product.Numero);
                textBox2.Text = product.Nombre;
                textBox3.Text = Convert.ToString(product.Precio);
                textBox4.Text = Convert.ToString(product.Cantidad);
                //
                //Agregamos a la lista de productos el Item enviado por el Form2
                //
                Products.Add(product);
                //
                //
                //
                dataGridView1.AutoGenerateColumns = false;
                //
                //
                dataGridView1.DataSource = null;
                //
                //Establecemos el DataSource del DataGridView enlazándolo a la lista
                //genérica
                dataGridView1.DataSource = Products;
                //
                //Mapeamos las propiedades a las columnas
                dataGridView1.Columns["ColumnNumero"].DataPropertyName = "Numero";
                dataGridView1.Columns["ColumnNombre"].DataPropertyName = "Nombre";
                dataGridView1.Columns["ColumnPrecio"].DataPropertyName = "Precio";
                dataGridView1.Columns["ColumnCantidad"].DataPropertyName = "Cantidad";

                //
                //Retornamos True
                return true;
            }
            //
            //Si la condición exists es igual a False, es decir, que el producto SI existe en la lista
            //retornamos FALSE para mostrar un mensaje información
            return false;
        }
    }
}

Vb.Net :
Public Class Form1
    '
    'Heredamos la Interfaz IProducto, recuerde que este será nuestro puente de comunicación entre ambos Forms
    Implements IProducto

    'Creamos una lista del tipo Eproducto, que será la encargada de
    'recibir la información del Form2 y almacenarla, posteriormente servirá
    'de fuente de datos para el DataGridView
    Private Shared ReadOnly Products As New List(Of EProducto)()

    Private Sub button1_Click(sender As System.Object, e As System.EventArgs) Handles button1.Click
        '
        'Instanciamos el Form2
        Dim frm As New Form2()
        '
        'Le indicamos quien lo mando a llamar usando la Propiedad Caller
        frm.Caller = Me
        '
        'Mostramos el Form2
        frm.ShowDialog()
    End Sub

    ''' <summary>
    ''' Funcion encargada de agregar un nuevo Item a la lista siempre y cuando no exista
    ''' </summary>
    ''' <param name="product">instancia de la clase Eproduct</param>
    ''' <returns>Si el Item fue agregado a la lista devuelve True si no fue agregado retorna False</returns>
    Public Function LoadDataRow(product As EProducto) As Boolean Implements IProducto.LoadDataRow

        'Busca si el Articulo ya se encuentra en la lista
        Dim exists As Boolean = Products.Any(Function(x) x.Numero.Equals(product.Numero))
        '
        '
        'Preguntamos por el resultado de la búsqueda del Articulo dentro de la lista
        If Not exists Then
            '
            'Si el articulo no existe dentro de la lista Mapeamos el item de la entidad Eproducto 
            'a los TextBox
            textBox1.Text = Convert.ToString(product.Numero)
            textBox2.Text = product.Nombre
            textBox3.Text = Convert.ToString(product.Precio)
            textBox4.Text = Convert.ToString(product.Cantidad)
            '
            'Agregamos a la lista de productos el Item enviado por el Form2
            '
            Products.Add(product)
            '
            '
            '
            dataGridView1.AutoGenerateColumns = False
            '
            '
            dataGridView1.DataSource = Nothing
            '
            'Establecemos el DataSource del DataGridView enlazándolo a la lista
            'genérica
            dataGridView1.DataSource = Products
            '
            'Mapeamos las propiedades a las columnas
            dataGridView1.Columns("ColumnNumero").DataPropertyName = "Numero"
            dataGridView1.Columns("ColumnNombre").DataPropertyName = "Nombre"
            dataGridView1.Columns("ColumnPrecio").DataPropertyName = "Precio"
            dataGridView1.Columns("ColumnCantidad").DataPropertyName = "Cantidad"

            '
            'Retornamos True
            Return True
        End If
        '
        'Si la condición exists es igual a False, es decir, que el producto SI existe en la lista
        'retornamos FALSE para mostrar un mensaje información
        Return False
    End Function
End Class

Bien, ya tenemos la codificación del Form1, recuerde que este es el que hereda de la Interfaz, ahora vayamos a ver la codificación del Form2 encargado de enviar los datos del Row seleccionado:

C# :
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace DataGridViewsendRow
{
    public partial class Form2 : Form
    {
        //
        //Creamos una instancia de la interfaz IProducto para establecer el formulario llamador
        public IProducto Caller { private get; set; }

        public Form2()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Funcion encargada de devolver una lista de la Entidad EProducto cargada con datos
        /// </summary>
        /// <returns>Lista genérica de Eproucto</returns>
        private static List<EProducto> Products()
        {
            //Creamos la lista de Eproducto
            //
            List<EProducto> products = new List<EProducto>();

            //
            //Instanciamos la clase EProducto para establecerle datos
            //
            EProducto item1 = new EProducto()
            {
                Numero = 1,
                Nombre = "Nombre del producto 1",
                Precio = new decimal(5.5),
                Cantidad = new decimal(6.3)
            };
            EProducto item2 = new EProducto()
            {
                Numero = 2,
                Nombre = "Nombre del producto 2",
                Precio = new decimal(15.5),
                Cantidad = new decimal(2)
            };
            EProducto item3 = new EProducto()
            {
                Numero = 3,
                Nombre = "Nombre del producto 3",
                Precio = new decimal(25),
                Cantidad = new decimal(5)
            };
            EProducto item4 = new EProducto()
            {
                Numero = 4,
                Nombre = "Nombre del producto 4",
                Precio = new decimal(150.5),
                Cantidad = new decimal(12)
            };
            EProducto item5 = new EProducto()
            {
                Numero = 5,
                Nombre = "Nombre del producto 5",
                Precio = new decimal(4.5),
                Cantidad = new decimal(3)
            };
            EProducto item6 = new EProducto()
            {
                Numero = 6,
                Nombre = "Nombre del producto 6",
                Precio = new decimal(5),
                Cantidad = new decimal(6)
            };
            //
            //Agregamos las instancias con datos de Eproducto a la lista del mismo tipo
            //
            products.Add(item1);
            products.Add(item2);
            products.Add(item3);
            products.Add(item4);
            products.Add(item5);
            products.Add(item6);

            //
            //Devolvemos la lista
            //
            return products;
        }

        /// <summary>
        /// Método encargado de poblar el DatagridView con datos de una lista genérica, en este caso
        /// la devuelta por la función Products() creada previamente,
        /// </summary>
        private void FillDgv()
        {
            //Evitamos generar nuevas columnas a la izquierda de las que creamos en tiempo de diseño
            //
            dataGridView1.AutoGenerateColumns = false;
            //
            //Establecemos la fuente de datos, observe que únicamente mandamos a llamar a la función
            dataGridView1.DataSource = Products();
            //
            //Mapeamos las propiedades de la clase Eproducto devuelta por la función
            //Products() a las columnas del DataGridView
            //
            dataGridView1.Columns["ColumnNumero"].DataPropertyName = "Numero";
            dataGridView1.Columns["ColumnNombre"].DataPropertyName = "Nombre";
            dataGridView1.Columns["ColumnPrecio"].DataPropertyName = "Precio";
            dataGridView1.Columns["ColumnCantidad"].DataPropertyName = "Cantidad";

        }

        private void Form2_Load(object sender, EventArgs e)
        {
            //
             //Usamos el evento Load del Form2 para mostrar el DataGridView con datos
            FillDgv();
        }

        private void dataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            try
            {
                //
                //Si el row en el que hicimos doble click es el encabezado del DataGridView, nos retornamos.
                if (e.RowIndex == -1)
                    return;

                //
                //Obtenemos el row en el cual se hizo doble Click
                //
                DataGridViewRow row = dataGridView1.Rows[e.RowIndex];

                //Instanciamos la clase Eproducto para cargar los datos tomándolos de las celdas del row
                EProducto item = new EProducto
                {
                    //
                    //Recuerde convertir al tipo de dato correcto
                    //
                    Numero = Convert.ToInt32(row.Cells["ColumnNumero"].Value),
                    Nombre = Convert.ToString(row.Cells["ColumnNombre"].Value),
                    Precio = Convert.ToDecimal(row.Cells["ColumnPrecio"].Value),
                    Cantidad = Convert.ToDecimal(row.Cells["ColumnCantidad"].Value)
                };

                //
                //Si no existe llamador para nuestro formulario nos retornamos sin hacer ninguna acción
                //
                if (Caller == null)
                    return;

                //Si el Form1 devolvió false por haber encontrado el Producto dentro de la lista
                //Informamos de lo sucedido al usuario
                if (!Caller.LoadDataRow(item))
                {
                    MessageBox.Show("El Producto ya existe en la lista", "Atención", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Error : {0}", ex.Message), "Error Inesperado", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
    }
}

Vb.Net :
Public Class Form2
    '
    'Creamos una instancia de la interfaz IProducto para establecer el formulario llamador
    Public Property Caller() As IProducto


    ''' <summary>
    ''' Función encargada de devolver una lista de la Entidad EProducto cargada con datos
    ''' </summary>
    ''' <returns>Lista genérica de Eproucto</returns>
    Private Shared Function Products() As List(Of EProducto)
        'Creamos la lista de Eproducto
        '
        Dim _products As New List(Of EProducto)()

        '
        'Instanciamos la clase EProducto para establecerle datos
        '
        Dim item1 As New EProducto()
        item1.Numero = 1
        item1.Nombre = "Nombre del producto 1"
        item1.Precio = New Decimal(5.5)
        item1.Cantidad = New Decimal(6.3)

        Dim item2 As New EProducto()
        item2.Numero = 2
        item2.Nombre = "Nombre del producto 2"
        item2.Precio = New Decimal(15.5)
        item2.Cantidad = New Decimal(2)

        Dim item3 As New EProducto()
        item3.Numero = 3
        item3.Nombre = "Nombre del producto 3"
        item3.Precio = New Decimal(25)
        item3.Cantidad = New Decimal(5)

        Dim item4 As New EProducto()
        item4.Numero = 4
        item4.Nombre = "Nombre del producto 4"
        item4.Precio = New Decimal(150.5)
        item4.Cantidad = New Decimal(12)

        Dim item5 As New EProducto()
        item5.Numero = 5
        item5.Nombre = "Nombre del producto 5"
        item5.Precio = New Decimal(4.5)
        item5.Cantidad = New Decimal(3)

        Dim item6 As New EProducto()
        item6.Numero = 6
        item6.Nombre = "Nombre del producto 6"
        item6.Precio = New Decimal(5)
        item6.Cantidad = New Decimal(6)

        '
        'Agregamos las instancias con datos de Eproducto a la lista del mismo tipo
        '
        _products.Add(item1)
        _products.Add(item2)
        _products.Add(item3)
        _products.Add(item4)
        _products.Add(item5)
        _products.Add(item6)

        '
        'Devolvemos la lista
        '
        Return _products
    End Function

    ''' <summary>
    ''' Método encargado de poblar el DatagridView con datos de una lista genérica, en este caso
    ''' la devuelta por la función Products() creada previamente,
    ''' </summary>
    Private Sub FillDgv()
        'Evitamos generar nuevas columnas a la izquierda de las que creamos en tiempo de diseño
        '
        dataGridView1.AutoGenerateColumns = False
        '
        'Establecemos la fuente de datos, observe que únicamente mandamos a llamar a la función
        dataGridView1.DataSource = Products()
        '
        'Mapeamos las propiedades de la clase Eproducto devuelta por la función
        'Products() a las columnas del DataGridView
        '
        dataGridView1.Columns("ColumnNumero").DataPropertyName = "Numero"
        dataGridView1.Columns("ColumnNombre").DataPropertyName = "Nombre"
        dataGridView1.Columns("ColumnPrecio").DataPropertyName = "Precio"
        dataGridView1.Columns("ColumnCantidad").DataPropertyName = "Cantidad"

    End Sub

    Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        FillDgv()
    End Sub

    Private Sub dataGridView1_CellDoubleClick(sender As System.Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dataGridView1.CellDoubleClick
        Try
            '
            'Si el row en el que hicimos doble click es el encabezado del DataGridView, nos retornamos.
            If e.RowIndex = -1 Then
                Return
            End If

            '
            'Obtenemos el row en el cual se hizo doble Click
            '
            Dim row As DataGridViewRow = dataGridView1.Rows(e.RowIndex)

            'Instanciamos la clase Eproducto para cargar los datos tomándolos de las celdas del row
            '
            'Recuerde convertir al tipo de dato correcto
            '
            Dim item As New EProducto()
            item.Numero = Convert.ToInt32(row.Cells("ColumnNumero").Value)
            item.Nombre = Convert.ToString(row.Cells("ColumnNombre").Value)
            item.Precio = Convert.ToDecimal(row.Cells("ColumnPrecio").Value)
            item.Cantidad = Convert.ToDecimal(row.Cells("ColumnCantidad").Value)

            '
            'Si no existe llamador para nuestro formulario nos retornamos sin hacer ninguna acción
            '
            If Caller Is Nothing Then
                Return
            End If

            'Si el Form1 devolvió false por haber encontrado el Producto dentro de la lista
            'Informamos de lo sucedido al usuario
            If Not Caller.LoadDataRow(item) Then
                MessageBox.Show("El Producto ya existe en la lista", "Atención", MessageBoxButtons.OK, MessageBoxIcon.[Stop])
            End If
        Catch ex As Exception
            MessageBox.Show(String.Format("Error : {0}", ex.Message), "Error Inesperado", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try

    End Sub
End Class

Hasta este momento hemos creado nuestro diseño, nuestra clase entidad, la interfaz y nuestro estructura de código interno, ¿Que nos falta?, pues únicamente probar nuestro proyecto de ejemplo:
Presione F5 para realizar las pruebas.
Presione el botón “Agregar”
Si realizo las cosas de acuerdo a las instrucciones, podrá ver el DataGridView del Form2 poblado con datos:
frm3
Haga doble Click sobre cualquier Row del datagridView para enviar los datos al Form1: 
frm4
 Ahora haga doble Click sobre un Producto ya enviado previamente:
frm5
 Bueno hemos llegado al final del articulo, si siguió el articulo completo no tendrá problemas para la implementación y adaptación de este ejemplo en sus proyecto de desarrollo.
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 Client Profile

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