TextBox personalizado (TextBoxUniversal)


El control TextBoxUniversal se creo con la finalidad de validar las entradas de información a los controles TextBox, el cual tiene la particularidad de validar números, letras, decimales. Y nos evita estar creando rutinas de código para hacer las validaciones antes mencionadas, ya que el control TextBoxUniversal lo hace por nosotros. También tiene una propiedad WaterMarkText.

Para poder implementarlo hay que descargar el proyecto que esta hospedado en GitHub TextBox-ComboBox una vez descargado el proyecto agregaremos la carpeta Libreria a muestro proyecto la cual contiene la dll GlobalTech.TextBoxControl.dll

Sigamos los siguientes pasos:

  • Agregamos la carpeta Librería a nuestro proyecto a nivel de solución

textbox

  • Nos dirigimos al Explorardor de soluciones para agregar la dll al proyecto, haga click derecho sobre el proyecto> Agregar> Referencia, se abrira la siguiente ventana.

Examinar

  • Presiona el botón Examinar como muestra la imagen y le das la ruta donde esta la carpeta Librería  y seleccionas la dll GlobalTech.TextBoxControl.dll
  • Ahora nos dirigimos a la ventana ToolBox(Cuadro de Herramientas) ,

agregar

  • Seleccionamos la pestaña General click derecho> Agregar pestaña le ponemos un nombre a la nueva pestaña como MyControlesPersonalizados.

elegir

  • Le saldrá la siguiente ventana.

examinar2

  • Le da en el botón Examinar como muestro en la imagen  a partir de ahí le da la ruta donde se encuentra la dll que esta contenida en la carpeta Librería.
  • Le da en el botón Aceptar y ya tendría los nuevos controles en la nueva pestaña creada, tal como muestro en la siguiente imagen.

textboxuniversal

Si todo ha salido bien hasta acá ya tendríamos los nuevos controles personalizado en nuestro Cuadro de Herramientas listo para ser usados.

Pasemos a hablar de las nuevas propiedades del control TextBoxUniversal

  • NúmeroDecimales, esta propiedad permite mostrar la cantidad de decimales que debe tener un número.
  • TipoControl, esta propiedad permite validar la información ingresada al control TextBoxUniversal si es de tipo númerico, letras, decimales.
  • WaterMarkText, esta propiedad permite ingresar un texto al control para indicarle al usuario algún mensaje como Nuevo el cual desaparecera al ingresar datos.

TipoControl pasamos a describir sus opciones.

Enteros,  Permite ingresar solo números, no va a permitir ingresar otro caracter que no sea números.

Letras, Permite ingresar solo letras.
Númeric, Permite ingresar solo números y al pasar al siguiene control le agrega los decimales, el cual se puede manejar la cantidad de decimales con una propiedad que se le agrego al control de nombre NumeroDecimales en el cual usted ingresa la cantidad de decimales que desee agregar.

TextBoxUniversal

Espero la información brindada sea de utilidad.

Comunicar Formularios usando Easy.MessageHub

 

Easy.MessageHub es un framework que por dentro viene implementado el patrón Event Aggregator, que justamente basandose en eventos permite una comunicación desacoplada entre formularios sin relación directa en nuestra aplicación.

EventAggregator: Por el nombre es fácil decir que los agregados de eventos un Publisher/Subscriber trabajan con EventAggregator como un centro cuya tarea consiste en agregar todos los mensajes publicados y enviar el mensaje a los suscriptores interesados.

EventAggregator

Antes de comenzar hay que instalar Easy.MessageHub ejecutar el siguiente comando en la consola de administación de paquetes.

Install-Package Easy.MessageHub

Una vez instalado pasamos a crear una clase como contrato para dichos eventos.

 public class VariableSelected
      {
          public int Codigo { get; set; }
          public string StrValor { get; set; }
      }
Desde el formulario hijo implementamos MessageHub
public partial class FrmBusqueda : Form
    {

        private readonly IMessageHub _eventHub;

        public FrmBusqueda(string title, EnumAsignacionTablas t)
        {
            _titulo = title;
            _table = t;
            CompleteEvent.Complete += CompleteEvent_Complete;
            InitializeComponent();
        }

        public FrmBusqueda(string title, EnumAsignacionTablas t, IMessageHub evenHub)
            : this(title, t)
        {
            _eventHub = evenHub;
        }

	private void ObtenerId()
        {
            if (dgvBusqueda.Rows.Count > 0)
            {
                if (dgvBusqueda.CurrentRow != null)
                {
                    _idRow = Convert.ToInt32(dgvBusqueda.CurrentRow.Cells[0].Value);
                    _desRow = Convert.ToString(dgvBusqueda.CurrentRow.Cells[1].Value);
                }
                _eventHub.Publish(new VariableSelected {Codigo = _idRow, StrValor = _desRow});
            }
        }
El padre recibe los datos y los procesa
private void btnBuscarColor_Click(object sender, EventArgs e)
         {
             var argNombreEntidad = new ConstructorArgument("title", StrColour.NameEntity);
             var argT = new ConstructorArgument("t", EnumAsignacionTablas.Colour);
             var frm = CompositionRoot.Resolve<FrmBusqueda>(argNombreEntidad, argT);
             _eventHub.Subscribe<VariableSelected>(OnColorSelected);
             frm.ShowDialog();
         }

        private void OnColorSelected(VariableSelected colorEvent)
         {
             if (colorEvent.Codigo != 0 && !string.IsNullOrEmpty(colorEvent.StrValor))
             {
                 if (DetalleItemColour.Count != 1)
                 {
                     var entity = new Colour()
                     {
                         ColourId = colorEvent.Codigo,
                         Nombre = colorEvent.StrValor
                     };
                     _detalleColour.Add(entity);
                     dgvColor.AutoGenerateColumns = false;
                     dgvColor.DataSource = DetalleItemColour;

                     //De suscribir
                     _eventHub.ClearSubscriptions();
                 }
                 else
                 {
                     MessageBoxEx.EnableGlass = false;
                     MessageBoxEx.Show(this, "Solo se permite un solo Color", StrColour.NameEntity,
                         MessageBoxButtons.OK, MessageBoxIcon.Information);
                     vBool = false;
                     colorEvent.Codigo = 0;
                     colorEvent.StrValor = String.Empty;
                 }
             }
         }

Cargar combobox vinculado a una tabla

 

Hola, en esta oportunidad voy a mostrar como cargar un ComboBox vinculado a una tabla, en el cual nos conectaremos a una base de datos para poder obtener los registros almacenados en dicha tabla.

Estoy usando Visual Studio 2013 y SQL Server 2014, pero voy a poner el script para crear la base de datos y la tabla en el script estoy usando un INSERT multiple el cual se puede realizar desde SQL Server 2008 hacia delante.

CREATE DATABASE Prueba
GO
USE Prueba
GO
CREATE TABLE Usuario
(
Id VARCHAR(6) NOT NULL PRIMARY KEY,
Descripcion VARCHAR(80)
)
GO
INSERT INTO Usuario(Id, Descripcion)
VALUES('000001', 'MIGUEL'), ('000002', 'JOSE'), ('000003', 'PABLO'),
('000004', 'MARTIN'), ('000005', 'JOSTIN'), ('000006', 'ISMAEL')

GO

Teniendo la base de datos con los registros, pasamos a crear nuestro proyecto y codificar para poder cargar el ComboBox con datos vinculados a una tabla.

Lo primero que tenemos que hacer es crear la conexión a la base de datos para eso usaremos el archivo App.conf el cual es un xml.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<connectionStrings>
<add name="default" connectionString="Data Source=.;Initial Catalog=Prueba;User ID=sa;Password=xxx"/>
</connectionStrings>
</configuration>

Paso siguiente Creamos la conexión a la base de datos, en el cual tenemos que agregarle una referencia System.Configuration

Referencia

Configuracion

C#

using System.Configuration; // Se agrega esta referencia para trabajar con ConfigurationManager.
using System.Data.SqlClient; // Se agrega esta referencia para trabajar con SQL Server.

namespace WindowsFormsUI
{
public class Conexion
{
public static SqlConnection Conectar(string con)
{
try
{
string strCon = ConfigurationManager.ConnectionStrings["default"].ToString();
SqlConnection cn = new SqlConnection(strCon);
return cn;
}
catch (Exception ex)
{
throw new ArgumentException("Error de Conexión", ex);
}
}
}
}

VB.NET

Imports System.Data.SqlClient
Imports System.Configuration
Public Class Conexion
Public Shared Function Conectar(con As String) As SqlConnection
Try
Dim strCon As String = ConfigurationManager.ConnectionStrings("default").ConnectionString()
Dim cn As New SqlConnection(strCon)
Return cn
Catch ex As Exception
Throw New ArgumentException("Error de Conexion", ex)
End Try
End Function
End Class

Pasamos a hacer el mapping tal cual esta en la tabla creando las entidades.

C#

namespace WindowsFormsUI
{
public class UsuarioEntity
{
public string Id { get; set; }
public string Descripcion { get; set; }
}
}

VB.NET

Public Class UsuarioEntity
Public Property Id As String
Public Property Descripcion As String

End Class

Pasamos a trabajar con el repositorio de datos para persistir contra la base de datos y obtener los datos que vamos a cargar en nuestro ComboBox.

C#

namespace WindowsFormsUI
{
public class UsuarioRepository
{
public static List<UsuarioEntity> listUsu()
{
using(SqlConnection cn = Conexion.Conectar("default"))
{
cn.Open();
using(SqlCommand cmd = cn.CreateCommand())
{
cmd.CommandText = "SELECT Id, Descripcion FROM Usuario";
List<UsuarioEntity> usuario = new List<UsuarioEntity>();
using(SqlDataReader reader = cmd.ExecuteReader())
{
while(reader.Read())
{
UsuarioEntity u = new UsuarioEntity()
{
Id = Convert.ToString(reader["Id"]),
Descripcion = Convert.ToString(reader["Descripcion"])
};
usuario.Add(u);
}
}
return usuario;
}

}
}

public static List<UsuarioEntity> listUsuario()
{
List<UsuarioEntity> listaItem = listUsu();
listaItem.Insert(0, new UsuarioEntity() { Id = Convert.ToString(0), Descripcion = "<Seleccionar>" });
return listaItem;
}

}
}

VB.NET

Imports System.Data
Imports System.Data.SqlClient
Public Class UsuarioRepository
Public Shared Function listUsu() As List(Of UsuarioEntity)
Using cn As SqlConnection = Conexion.Conectar("default")
cn.Open()
Using cmd As SqlCommand = cn.CreateCommand
cmd.CommandText = "SELECT Id, Descripcion FROM Usuario"
Dim usuario As New List(Of UsuarioEntity)
Using reader As SqlDataReader = cmd.ExecuteReader
While reader.Read
Dim u As New UsuarioEntity
u.Id = Convert.ToString(reader("Id"))
u.Descripcion = Convert.ToString(reader("Descripcion"))
usuario.Add(u)
End While
End Using
Return usuario
End Using
End Using
End Function

Public Shared Function listUsuario() As List(Of UsuarioEntity)
Dim listaItem As List(Of UsuarioEntity) = listUsu()
listaItem.Insert(0, New UsuarioEntity() With {
.Id = Convert.ToString(0), _
.Descripcion = "<Seleccionar>" _
})
Return listaItem
End Function
End Class

Pasamos a llamar los métodos desde el formulario que contiene nuestro ComboBox.

C#

namespace WindowsFormsUI
{
public partial class frmUsuario : Form
{
public frmUsuario()
{
InitializeComponent();
}

private void frmUsuario_Load(object sender, EventArgs e)
{
LOADComboUsuario();
}

void LOADComboUsuario()
{
List<UsuarioEntity> list = Usuario.listUsuario();
cboUsuario.DataSource = list;
cboUsuario.ValueMember = "Id";
cboUsuario.DisplayMember = "Descripcion";
}
}
}

VB.NET

Public Class frmUsuario

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
LOADComboUsuario()
End Sub

Sub LOADComboUsuario()
Dim list As List(Of UsuarioEntity) = Usuario.listUsuario
cboUsuario.DataSource = list
cboUsuario.ValueMember = "Id"
cboUsuario.DisplayMember = "Descripcion"
End Sub
End Class

Al ejecutar nuestro proyecto tendremos cargado nuestro ComboBox así como muestra la imagen.

Combo

Proyecto C#               Proyecto VB.NET

Entity Framework 6 operaciones CRUD

 

Entity Framework es un potente ORM creado por Microsoft que facilita el trabajo del mapeo a las entidades y facilita la implementación de la capa de datos usando Linq to Entities. Entity Framework tiene varios enfoques como, Code First, Model First, Database First.

Code First, es un enfoque mas de Entity Framework que permite crear una base de datos a partir de código(C#, VB.NET), por defecto se utiliza el nombre de nuestras clases y correspondientes propiedades para crear nombres de tablas y campos.

Model First, le da la posibilidad de diseñar toda la lógica del negocio, le permite crear un modelo nuevo mediante Entity Framework Designer y después genera un esquema de la base de datos a partir del modelo.

Database First, el modelo conceptual se crea a partir de una base de datos existente, el cual se almacena en una archivo .edmx y este se podrá ver y editar en un diseñador, para actualizar nuevos cambios que surjan en nuestra base de datos.

En esta ocasión voy a usar DataBase First pero antes se debe crear una base de datos de prueba.

CREATE DATABASE PruebaEF
GO
USE PruebaEF
GO
CREATE TABLE [dbo].[Personal](
 [Id] [varchar](6) NOT NULL,
 [Nombre] [varchar](80) NULL,
 [Direccion] [varchar](100) NULL,
 [Telefono] [varchar](9) NULL,
 [lEstado] [smallint] NULL,
 CONSTRAINT [PK_Personal] PRIMARY KEY CLUSTERED
(
 [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

Para este ejemplo estoy usando Visual Studio 2013 y SQL 2014.

Una vez teniendo la base de datos creada pasamos a crear una solución distribuida en una arquitectura de 3 capas, Presentación(Proyecto Windows Forms),  Dominio(Proyecto Class Library), Persistencia Datos(Proyecto Class Library), se hace las referencia entre capas Persistencia en Dominio, Persistencia y Dominio en Presentación, referencio Persistencia en Presentación por que el mapeo a la infraestructura de la DB esta en esa capa solo por eso, pero la lógica de la aplicación va en el Dominio.

Se instala en la capa de Persistencia y Presentación Entity Framework desde Nuguet, hecho eso se agrega un nuevo elemento desde el proyecto de Persistencia, ADO.NET Entity Data Model.

entity data model

Ahora nos conectamos a la base de datos para obtener el modelo.

conectando a la db

Seleccionamos las tablas que se requieren para el modelo.

escojer las tablas

Escogemos el Framework a trabajar en mi caso lo dejo como esta.

escoger el EF

Obtenemos el modelo.

modelo

Y también tenemos el mapping de la infraestuctura de la DB.

//------------------------------------------------------------------------------
// <auto-generated>
// Este código se generó a partir de una plantilla.
//
// Los cambios manuales en este archivo pueden causar un comportamiento inesperado de la aplicación.
// Los cambios manuales en este archivo se sobrescribirán si se regenera el código.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Prueba.PersistenciaDatos.Modelo
{
 using System;
 using System.Collections.Generic;

 public partial class Personal
 {
 public string Id { get; set; }
 public string Nombre { get; set; }
 public string Direccion { get; set; }
 public string Telefono { get; set; }
 public Nullable<short> lEstado { get; set; }
 }
}

Ya podemos comenzar a trabajar comenzando con la implementación vamos a ubicar el context que es el que se conecta a la base de datos para poder persistir contra ella.

context

Esto se encuentra ubicado en el Model1.Context, voy a implementar la capa de persistencia usando linq to entities, me olvidaba agregar la referencia System.Data.Entity

using Prueba.PersistenciaDatos.Modelo;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Prueba.PersistenciaDatos
{
 public class PersonalRepository
 {

 public List<ModeloMinimo.PersonalMinimalModel> GetPersonal()
 {
 using(var context = new PruebaEFEntities())
 {
 return (from p in context.Personal
 select new ModeloMinimo.PersonalMinimalModel
 {
 Id = p.Id,
 Nombre = p.Nombre
 }).ToList();
 }
 }

 public string GenerarCodigo()
 {
 using(var context = new PruebaEFEntities())
 {
 int codigo;
 var ultimoId = Convert.ToInt32(context.Personal.Max(x => x.Id)) + Convert.ToInt32(1);
 codigo = ultimoId;
 return string.Format("{0:000000}", codigo);
 }
 }

 public bool Existe(string codigo)
 {
 using(var context = new PruebaEFEntities())
 {
 int resultado = context.Personal.Where(x => x.Id == codigo).Count();
 if (resultado == 0)
 return false;
 else
 return true;
 }
 }
 public Personal Buscar(string codigo)
 {
 using(var context = new PruebaEFEntities())
 {
 var strSQL = from p in context.Personal
 where p.Id == codigo
 select p;
 return strSQL.First();
 }
 }

 public void Guardar(Personal model)
 {
 try
 {
 using(var context = new PruebaEFEntities())
 {
 context.Personal.Add(model);
 context.SaveChanges();
 }
 }
 catch (Exception ex)
 {
 throw new Exception("No se puede guardar el registro", ex);
 }
 }

 public void Actualizar(Personal model)
 {
 try
 {
 using(var context = new PruebaEFEntities())
 {
 context.Entry(model).State = EntityState.Modified;
 context.SaveChanges();
 }
 }
 catch (Exception ex)
 {
 throw new Exception("No se puede actualizar el registro", ex);
 }
 }

 public void Eliminar(string codigo)
 {
 using(var context = new PruebaEFEntities())
 {
 var entities = (from p in context.Personal
 where p.Id == codigo
 select p).Single();
 context.Personal.Remove(entities);
 context.SaveChanges();
 }

 }

 }
}

Implementando el dominio en el cual uso una técnica para desacoplar la capa de Presentación con la capa de Dominio.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prueba.PersistenciaDatos;
using System.Linq.Expressions;

namespace Prueba.Dominio
{
 public class Personal
 {
 public string MensajeLogica;
 public string MensajeError;

 PersonalRepository personal = new PersonalRepository();

 public List<PersistenciaDatos.ModeloMinimo.PersonalMinimalModel> GetPersonal()
 {
 return personal.GetPersonal();
 }

 public PersistenciaDatos.Modelo.Personal Buscar(string codigo)
 {
 return personal.Buscar(codigo);
 }

 public void Guardar(PersistenciaDatos.Modelo.Personal model)
 {
 BusinessException.Clear();
 if (string.IsNullOrEmpty(model.Nombre)) BusinessException.Add("Debe ingresar el nombre");

 if(BusinessException.Count() == 0)
 {
 if(string.IsNullOrEmpty(model.Id))
 model.Id = personal.GenerarCodigo();

 try
 {
 if(personal.Existe(model.Id))
 {
 personal.Actualizar(model);
 MensajeLogica = "Registro actualizado";
 }
 else
 {
 personal.Guardar(model);
 MensajeLogica = "Registro guardado";
 }
 }
 catch (Exception ex)
 {
 BusinessException.Add(ex.Message);
 }

 }
 }

 public void Eliminar(string codigo)
 {
 personal.Eliminar(codigo);
 }

 }
}

Implementando la capa de Presentación, se debe copiar el app.config de la capa de Persistencia a la Presentación.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Prueba.PersistenciaDatos;
using Prueba.Dominio;

namespace Prueba.WindowsUI
{
 public partial class frmPersonal : Form
 {
 private string strCodigo;

 Dominio.Personal personal = new Dominio.Personal();

 public frmPersonal()
 {
 InitializeComponent();
 }

 private void frmPersonal_Load(object sender, EventArgs e)
 {
 LoadDGVPersonal();

 }

 private void btnGuardar_Click(object sender, EventArgs e)
 {
 PersistenciaDatos.Modelo.Personal model = new PersistenciaDatos.Modelo.Personal();
 model.Id = lblCodigo.Text;
 model.Nombre = txtNombre.Text;
 model.Direccion = txtDireccion.Text;
 model.Telefono = txtTelefono.Text;
 model.lEstado = Convert.ToInt16(chkEstado.Checked ? "1" : "0");
 personal.Guardar(model);
 LoadDGVPersonal();
 }

 void LoadDGVPersonal()
 {
 List<PersistenciaDatos.ModeloMinimo.PersonalMinimalModel> list = personal.GetPersonal();
 dgvPersonal.AutoGenerateColumns = false;
 dgvPersonal.DataSource = list;
 }

 void ObtenerId()
 {
 strCodigo = Convert.ToString(dgvPersonal.CurrentRow.Cells[0].Value);
 }

 void Buscar()
 {
 if(strCodigo != string.Empty)
 {
 PersistenciaDatos.Modelo.Personal model = personal.Buscar(strCodigo);
 lblCodigo.Text = model.Id;
 txtNombre.Text = model.Nombre;
 txtDireccion.Text = model.Direccion;
 txtTelefono.Text = model.Telefono;
 chkEstado.Checked = Convert.ToBoolean(model.lEstado);
 }
 }

 void Eliminar()
 {
 ObtenerId();
 string msg = string.Format("Se va a ELIMINAR el registro: {0} {1} {0} Código: {2} {0} Nombre: {3}",
 Environment.NewLine,
 new string('-', 50), dgvPersonal.CurrentRow.Cells[0].Value,
 dgvPersonal.CurrentRow.Cells[1].Value);

 if (MessageBox.Show(msg, "Personal", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
 {
 personal.Eliminar(strCodigo);
 LoadDGVPersonal();
 }
 }

 private void dgvPersonal_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
 {
 ObtenerId();
 Buscar();
 tabControl1.SelectedTab = tabPage2;
 }

 private void frmPersonal_KeyDown(object sender, KeyEventArgs e)
 {
 if(e.KeyCode == Keys.F7)
 {
 Eliminar();
 }
 }

 }
}

Al ejecutar la aplicación queda de esta manera.

frmPersonal datos

lista

Adjunto el proyecto para que lo puedan descargar, el próximo post será Inversión de Control IoC con Ninject.

Proyecto C#

 

 

 

 

 

 

 

Crystal Reports con Store Procedure

 

Hola en esta ocasión mostrare como implementar un reporte con store procedure, pero con un reporte creado con crystal reports 8 y teniendo una ruta de ubicación, que permite tener el reporte fuera del IDE de visual studio, esto nos da la facilidad de darle mantenimiento al reporte sin necesidad de crear un nuevo ejecutable.

En esta ocasión solo pondré código, ya que en anteriores entradas se ha hablado de como implementarlo.

Para persistir contra la base de datos se tiene un método en el cual se llama a un store procedure que se le pasa un parámetro para obtener un registro especifico.

[C#]

public static List<PersonalEntity> ReporteBuscarPersonal(string codigo)
        {
            using (SqlConnection cn = Conexion.Conectar(“default”))
            {
                cn.Open();
                using (SqlCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = “SP_Personal”;
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Parameters.AddWithValue(“@PersonalId”, codigo);
                    List<PersonalEntity> personal = new List<PersonalEntity>();
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        PersonalEntity p = new PersonalEntity();
                        if (reader.Read())
                        {
                            p.Nombre = Convert.ToString(reader[“Nombre”]);
                            p.Direccion = Convert.ToString(reader[“Direccion”]);
                        };
                        personal.Add(p);
                    }
                    return personal;
                }
            }
        }

[C#] 

Se llama al método desde la capa de dominio.

[C#]

public static DataTable crBuscarPersonal(string id)
        {
            return PersonalRepository.crBuscarPersonal(id);
        }

[C#]

Por ultimo la implementación desde la presentación, el método SetDBLogonForReport el cual se encarga de pasarle los datos de conexión a la base de datos.

[C#]

private void button1_Click(object sender, EventArgs e)
        {
            string FilePath = @”G:\Reportes\PersonalParametro.rpt”;
            ReportDocument rptListaPersonal = new ReportDocument();
           
            rptListaPersonal.Load(FilePath);
            DataTable tablaDatos = Personal.crBuscarPersonal(txtId.Text);

            rptListaPersonal.SetDataSource(tablaDatos);

            frmParametro frm = new frmParametro();
           
            frm.CrystalReportViewer.ReportSource = rptListaPersonal;
            frm.CrystalReportViewer.Dock = DockStyle.Fill;
           
            ConnectionInfo cn = new ConnectionInfo();
            cn.ServerName = “.”;
            cn.DatabaseName = “Prisma”;
            cn.UserID = “sa”;
            cn.Password = “lim@01”;
            cn.Type = ConnectionInfoType.SQL;
            SetDBLogonForReport(cn, rptListaPersonal);
          
            frm.CrystalReportViewer.Refresh();
            frm.Text = “Probando”;
            frm.Show();
        }

[C#]

Crystal Reports con DataSet tipado

 

Vamos a crear un reporte con DataSet tipado, en su interior un DataTable, se usara una base de datos Access adjuntada al proyecto.

Una vez que tienen creado su proyecto y su conexión a la DB, pasaremos a crear el DataSet. Para esto se hace click derecho en el proyecto/Agregar/Nuevo elemento/Datos/Conjunto de datos. Ponen el nombre de su DataSet y listo.

Luego al DataSet le agrega un DataTable en el cual tiene que poner el nombre de la tabla que va obtener los datos, el nombre tiene que ser igual al nombre de la tabla que tiene en la DB, luego le agrega las columnas que va a consultar mediante la query.

dataset

Una vez obtenido nuestro DataSet, procedemos a crear el reporte, hacemos click derecho en el proyecto/Agregar/Nuevo elemento/Reporting/Crystal Reports. Ponen el nombre del reporte y tocaría darle la ruta donde se encuentra nuestro DataSet creado en el paso anterior.

Crystal

Crystal2

Ahora pasamos a armar el reporte de la siguiente manera se arrastra los campos que queremos mostrar en el reporte se colaca en la pate de detalles.

rpt

Ahora pasamos a codificar vamos a hacer la consulta SQL contra la DB.

using System.Text;
using System.Data;
using System.Data.OleDb;
using CristalReports.AccesoDatos;

namespace CristalReports.Reportes
{
    public class ClienteRepository
    {
        internal static dsClientes ObtenerClientes()
        {
            dsClientes _dsClientes = new dsClientes();
            using (OleDbConnection cn = Conexion.Conectar("NorthwindConnectionString"))
            {
                cn.Open();
                using (OleDbCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT Id, Nombre, Apellidos FROM Clientes";
                    OleDbDataAdapter da = new OleDbDataAdapter(cmd);
                    da.Fill(_dsClientes, "Clientes");
                }
            }
            return _dsClientes;
        }
    }
}

Imports System.Data
Imports System.Data.OleDb

Public Class ClienteRepository

    Friend Shared Function ObtenerClientes() As dsCliente
        Dim _DsClientes As New dsCliente()
        Using cn As OleDbConnection = Conexion.Conectar("NorthwindConnectionString")
            cn.Open()
            Using cmd As OleDbCommand = cn.CreateCommand()
                cmd.CommandText = "SELECT Id, Nombre, Apellidos FROM Clientes"
                Dim da As New OleDbDataAdapter(cmd)
                da.Fill(_DsClientes, "Clientes")
            End Using
        End Using
        Return _DsClientes
    End Function

End Class

Ahora implementamos al método ObtenerReporteCliente.

public class ClienteReporte
    {
        public static ReporteClientes ObtenerReporteCliente()
        {
            ReporteClientes reporte = new ReporteClientes();
            dsClientes cliente = ClienteRepository.ObtenerClientes();
            reporte.SetDataSource(cliente);
            return reporte;
        }
    }

Public Class ClienteReporte

    Public Shared Function ObtenerReporteCliente() As ReporteClientes
        Dim reporte As New ReporteClientes
        Dim cliente As dsCliente = ClienteRepository.ObtenerClientes()
        reporte.SetDataSource(cliente)
        Return reporte
    End Function
End Class

Ahora se agrega un formulario para colocar el CrystalReportViewer, que lo encuentra en el Toolbox en la sección Creación de informe. Preparamos el formulario.

using System.Text;
using System.Windows.Forms;
using CrystalDecisions.CrystalReports.Engine;

namespace CristalReports
{
    public partial class frmReporte : Form
    {
        private ReportClass report;
        public frmReporte()
        {
            InitializeComponent();
        }

        public frmReporte(ReportClass _report)
        {
            InitializeComponent();
            report = _report;
        }

        private void frmReporte_Load(object sender, EventArgs e)
        {
            this.crystalReportViewer1.ReportSource = report;
        }
    }
}

Imports CrystalDecisions.CrystalReports.Engine

Public Class frmReporte
    Dim report As ReportClass

    Public Sub New(_report As ReportClass)
        InitializeComponent()
        report = _report
    End Sub
    Private Sub frmReporte_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Me.CrystalReportViewer1.ReportSource = report
    End Sub
End Class

Por ultimo llamamos al reporte para su impresión.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CristalReports.Reportes;

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

        private void btnImprimir_Click(object sender, EventArgs e)
        {
            ReporteClientes _reporte = ClienteReporte.ObtenerReporteCliente();
            frmReporte frm = new frmReporte(_reporte);
            frm.Show();
        }
    }
}

Public Class Form1

    Private Sub btnImprimir_Click(sender As System.Object, e As System.EventArgs) Handles btnImprimir.Click
        Dim _reporte As ReporteClientes = ClienteReporte.ObtenerReporteCliente()
        Dim frm As New frmReporte(_reporte)
        frm.Show()
    End Sub
End Class

 

El resultado sería el siguiente.

reporte

Descargar App C#   Descargar App VB

NOTA : Los proyectos están desarrollados en VS 2010, les aconsejo descargar la versión Express de VS 2012 Visual Studio 2012 Express ya que uso la versión 2012.

Filtrar datos en DataGridView

Vamos a filtrar datos en un DataGrigView, pero antes de eso se ha debido cargar los registros al DGV, previa búsqueda de por ejemplo, Clientes que su nombre o apellido comience con M, una vez cargada la data, se procede a filtrar los datos, hacer la búsqueda en el DGV, más no en la DB.

Sería una mala practica tener los datos cargados en el DGV y hacer el filtro en la DB, lo que se pretende con esta técnica es liberar recursos en el servidor de datos y pretender una mejor performance de la aplicación.

Vamos a crear un método con DataTable en el cual le vamos asignar los datos que recuperamos de la DB.

public class ClienteRepository
    {
        public static DataTable GetCliente()
        {
            using (SqlCeConnection cn = Conexion.Conectar("NorthwindConnectionString"))
            {
                cn.Open();
                using (SqlCeCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT [Customer ID], [Company Name] FROM Customers";

                    using (SqlCeDataAdapter da = new SqlCeDataAdapter(cmd))
                    {
                       DataTable dt = new DataTable();
                        da.Fill(dt);
                        return dt;
                    }
                }
            } 
        }

Ahora vamos a llamar el método GetCliente desde el evento Load del formulario para cargar el DataGridView.

public partial class frmCliente : Form
    {
        private DataTable dt = new DataTable();
        private DataSet ds = new DataSet();
        public frmCliente()
        {
            InitializeComponent();
        }

        private void frmCliente_Load(object sender, EventArgs e)
        {
            dt = ClienteRepository.GetCliente();
            ds.Tables.Add(dt);
            dgvCliente.DataSource = dt;
            FormatoGrilla();
        }

Ya esta cargado el objeto DGV con los datos ahora pasamos a hacer la búsqueda en los datos que tiene cargado el DGV, mas no en la DB. Como decía líneas arriba hemos utilizado un objeto DataTable, para poder usar el DataView.

El DataView representa una vista personalizada que puede enlazar datos de un DataTable para ordenación, filtrado, búsqueda, edición y navegación. El DataView no almacena datos, si no que representa una vista conectada de su correspondiente DataTable.

private void txtDescripcion_TextChanged(object sender, EventArgs e)
        {
            string fieldName = string.Concat("[", dt.Columns[1].ColumnName,"]");
            dt.DefaultView.Sort = fieldName;
            DataView view = dt.DefaultView;
            view.RowFilter = string.Empty;
            if (txtDescripcion.Text != string.Empty)
                view.RowFilter = fieldName + " LIKE '%" + txtDescripcion.Text + "%'";
            dgvCliente.DataSource = view;
        }

El filtro lo realizamos en el evento txtDescripcion_TextChanged de la caja de texto que vamos usar para ingresar los datos que queremos filtrar o buscar. Quedaría de esta manera.

DGV

Descargar Proyecto C#

 

Maestro – Detalle

En esta oportunidad vamos a realizar una app Maestro – Detalle, usando dos DataGridView, ADO.NET y LINQ to DataSet.

Vamos a trabajar con la DB Northwind, tablas Customers(Maestro) y Orders(Detalle), tenemos que fijarnos el campo en común que tienen estas dos tablas que en le Maestro es el PK y en el Detalle es el FK, por esos campos es que relacionamos ambas tablas.

public class CustomersOrdersRepository
    {
        public static DataSet MaestroDetalle()
        {
            // Agregamos la key que define al config
            using (SqlCeConnection cn = Conexion.Conectar("NorthwindConnectionString"))
            {
                cn.Open();
                using (SqlCeCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT [Customer ID], [Company Name], City, Country FROM Customers";
                    using (SqlCeDataAdapter da = new SqlCeDataAdapter(cmd))
                    {
                        DataSet ds = new DataSet();
                        // Llenar DataTable en el DataSet
                        da.Fill(ds, "Customers");
                        cmd.CommandText = "SELECT [Order ID], [Customer ID], [Employee ID], [Order Date]," +
                                          " [Required Date] FROM Orders";
                        da.Fill(ds, "Orders");

                        // Establecer relaiones
                        DataColumn colPadre = ds.Tables["Customers"].Columns["Customer ID"];
                        DataColumn colHijo = ds.Tables["Orders"].Columns["Customer ID"];
                        DataRelation relation = new DataRelation("Detalle de Pedidos", colPadre, colHijo);
                        ds.Relations.Add(relation);

                        return ds;
                    }
                }

            }
        }
    }

 

Llamaremos al método MaestroDetalle desde el Load del formulario frmMaestroDetalle, para esto vamos a declarar dos variables a nivel de formulario ds y código, para ser llamadas desde cualquier parte del formulario para nuestros propósitos.

public partial class frmMaestroDetalle : Form
    {
        // Declaramos variables a nivel de formulario
        DataSet ds = new DataSet();
        private string codigo;

        public frmMaestroDetalle()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            CargarGrillas();
        }

        private void dgvMaestro_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            ObtenerID();
            // Aplicamos las bondades de LINQ to DataSet.
            DataTable DetalleItem = ds.Tables["Orders"];
            EnumerableRowCollection<datarow> query = from _detalle in DetalleItem.AsEnumerable()
                                                     where _detalle.Field<string>("Customer ID") == codigo
                                                     select _detalle;
            DataView view = query.AsDataView();
            dgvDetalle.DataSource = view;

        }

        private  void CargarGrillas()
        {
            // Le asignamos al DataSet el método que hace la consula a la DB.
            ds = CustomersOrdersRepository.MaestroDetalle();
            dgvMaestro.DataSource = ds;
            dgvMaestro.DataMember = "Customers";

            dgvDetalle.DataSource = ds;
            dgvDetalle.DataMember = "Orders";
        }

       private void ObtenerID()
       {
           codigo = Convert.ToString(dgvMaestro.CurrentRow.Cells[0].Value);
       }

    }

Para poder cargar los datos en las grillas estoy creando un método de nombre CargarGrillas, el cual lo llamo en el evento Load. En el cual utilizo la variable ds que esta declarada a nivel del formulario para poder ser llamada desde cualquier lado que se le necesite, a la variable ds nos representa un DataSet, le asignamos el método MaestroDetalle que es el encargado de hacer la persistencia contra la DB y traernos los datos solicitados de Maestro y Detalle, una vez que el DataSet tiene los datos solicitados, se lo asignamos al DataSource del DataGridView, utilizamos el DataMember para darle el nombre de la tabla en este caso como estamos trabajando con la grilla de Maestro le ponemos el nombre “Customers”, de igual manera hacemos para Detalle.

Hasta allí ya tenemos cargadas las dos grillas, ahora vamos a trabajar en el evento dgvMaestro_CellClick del DataGridView, para poder hacer el filtro, en el cual llamamos al método ObtenerID el cual nos obtiene la primera posición de una fila, obteniendo la clave del campo en comúm, teniendo el código hacemos el filtro mediante LINQ to DataSet.

El resultado final que debemos de obtener es como muestra la imagen.

MaestroDetalle

Descargar Proyecto C#

PropertyGrid

Hace poco conocí este control, por que me mandaron en mi trabajo a implementarlo en el desarrollo de una aplicación que estaba haciendo y bueno no encontré mucha información, me puse a investigar y logre utilizarlo, les mostrare como implementarlo.

PropertyGrid

PropertyGrid, es el mismo control que se usa para ver las propiedades de un control en el IDE de VS.NET, muestra las propiedades de cualquier objeto o tipo, y recupera las propiedades del elemento, principalmente mediante la reflexión. (La reflexión es una tecnología que proporciona información de tipo en tiempo de ejecución). El control PropertyGrid se compone de dos columnas y, como mínimo, tantas filas como propiedades editables contiene el objeto. La columna de la izquierda identifica la propiedad por su nombre, y la de la derecha ofrece un espacio para ver y/o editar la propiedad. Es muy útil para el uso del usuario final que le permite editar las propiedades de un objeto,  lo cual, sin duda, añade elegancia y valor a la aplicación. Sin mas preámbulo manos a la obra.

Se utiliza una clase para establecer las propiedades del objeto, algo muy importante hay que declarar el namespace ComponentModel, de la siguiente manera using System.ComponentModel.

public class Producto
    {
        // Se implementan los enum para poder cargar los combobox
        public enum Colors
        {
            AZUL = 1,
            VERDE = 2,
            AMARILLO = 3
        }

        public enum Medida
        {
            SMALL = 1,
            MEDIUM = 2,
            LARGE = 3
        }

        [Category("Producto"), DescriptionAttribute("Código del producto")]
        public string Código { get; set; }

        [Category("Producto"), DescriptionAttribute("Color que tiene el producto")]
        public Colors Color { get; set; }

        [Category("Producto"), DescriptionAttribute("Tamaño, medida"), TypeConverter(typeof(ConvertTalla))]
        public string Talla { get; set; }

        [Category("Producto"), DescriptionAttribute("Modelo del cuello")]
        public string Cuello { get; set; }

        [Category("Producto"), DescriptionAttribute("Indica si se encuentra activo")]
        public bool Estado { get; set; }

    }

Ahora vamos al load del formulario para instanciar la clase producto para asignársela al control PropertyGrid.

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

        private void Form1_Load(object sender, EventArgs e)
        {
            // Instanciamos la clase Producto
            Producto producto = new Producto();
            
            // Le pasamos la instancia al objeto
            propertyGrid1.SelectedObject = producto; 
        }
    }

Ahora les paso a explicar de los atributos que se aplicaron a la propiedades de la clase producto, estos atributos se pueden usar gracias al namespace que declaramos en la clase. Los atributos son etiquetas declarativas que se aplican a la propiedades que se pueden recuperar en tiempo de ejecución mediante la reflexión.

  • DescriptionAttribute . Establece el texto de la propiedad que se muestra en el panel de ayuda siguiente descripción de las propiedades. Esta es una manera útil para proporcionar texto de ayuda de la propiedad activa (la propiedad que tiene el foco).
  • CategoryAttribute . Establece la categoría que la propiedad se encuentra en la red. Esto es útil cuando se desea una propiedad agrupados por un nombre de categoría. Al Misc categoría. Aplique este atributo a todas las propiedades.
  • BrowsableAttribute – Indica si la propiedad se muestra en la cuadrícula. Esto es útil cuando se quiere ocultar una propiedad de la red. De forma predeterminada,
  • ReadOnlyAttribute – Indica si la propiedad es de sólo lectura. Esto es útil cuando se desea mantener la propiedad de ser editable en la red. De forma predeterminada, una propiedad pública con obtener y establecer funciones de acceso se puede editar en la cuadrícula.
  • DefaultValueAttribute – Identifica el valor predeterminado de la propiedad. Esto es útil cuando se desea proporcionar un valor predeterminado para una propiedad y luego determinar si el valor de la propiedad es diferente al predeterminado. Aplique este atributo a todas las propiedades.
  • DefaultPropertyAttribute – Identifica la propiedad predeterminada de la clase. La propiedad predeterminada de una clase obtiene el foco por primera vez cuando se selecciona la clase en la cuadrícula.

Segunda parte Acceso a Datos

Ahora la talla que se carga con el enum Medida, lo cargamos con datos reales que están en la Base de datos, para eso vamos a comentar el enum Medida que esta en la Clase Producto, hacemos la consulta SQL contra la base de datos, de la siguiente manera.

public class TallaRepository
    {
        public static List<tallaentity> GetTalla()
        {
            using(SqlCeConnection cn = Conexion.Conectar("PruebaConnectionString"))
            {
                cn.Open();
                using(SqlCeCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT idTalla, descripcion FROM Tallas";
                    List<tallaentity> talla = new List<tallaentity>();
                    using(SqlCeDataReader reader = cmd.ExecuteReader())
                    {
                        while(reader.Read())
                        {
                            TallaEntity t = new TallaEntity()
                                {
                                    idTalla = Convert.ToInt32(reader["idTalla"]),
                                    descripcion = Convert.ToString(reader["descripcion"])
                                };
                            talla.Add(t);
                        }
                    }
                    return talla;
                }
            }
        }

Ahora vamos a crear una clase donde se le asigna los datos al ComboBox a través de una lista y en donde se le asigna propiedades.

internal class ConvertTalla : StringConverter
    {
        // Permite alternar dinámicamente entre el ComboBox, donde el usuario debe elegir entre 
        // las opciones disponible, y TextBox, donde el usuario introduce libremente un valor.
        public override bool
           GetStandardValuesSupported(
           ITypeDescriptorContext context)
        {
            //True - means show a Combobox
            //and False for show a Modal 
            return true;
        }

        // Determina si el usuario debe limitarse a los valores de la lista o si, aun disponiendo de ella,
        // Puede introducir un valor nuevo.
        public override bool
            GetStandardValuesExclusive(
            ITypeDescriptorContext context)
        {
            //False - a option to edit values 
            //and True - set values to state readonly
            return true;
        }

        // Entrega la lista de miembros del ComboBox.
        public override StandardValuesCollection
           GetStandardValues(
           ITypeDescriptorContext context)
        {
            List<tallaentity> lista = TallaRepository.GetTalla(); 
            return new StandardValuesCollection(lista.Select(x =&gt; x.descripcion).ToArray());

        }
    }

Ahora solo nos toca modificar la propiedad de la Clase Producto

[Category("Producto"), DescriptionAttribute("Tamaño, medida"), TypeConverter(typeof(ConvertTalla))]
        public string Talla { get; set; }

Esta clase reimplementa tres funciones:

GetStandardValuesSupported permite alternar dinámicamente entre el ComboBox, donde el usuario debe elegir entre las opciones disponibles, y el TextBox, donde el usuario introduce libremente un valor.

GetStandardValuesExclusive determina si el usuario debe limitarse a los valores de la lista o si, aun disponiendo de ella, puede introducir un valor nuevo.

GetStandardValues entrega la lista de miembros del Combobox.

Proyecto C#

NOTA : El proyecto esta desarrollado en VS 2012 y se esta usando SQL CE 4.0 la base de datos ya viene incrustada en el proyecto, solo tienen que correr la aplicación.