sábado, 21 de febrero de 2015

Programación en 3 capas–Validación Reglas de Datos

Hola a todos:

Este articulo es la continuación de:

Programación en 3 capas

En donde se hablo de lo que es la arquitectura de desarrollo 3 capas, las ventajas de cada una de ellas y la forma de crear un proyecto con esta arquitectura usando Visual Studio.

En este articulo les compartiré la manera de como de una forma muy sencilla podemos nosotros validar nuestras reglas de datos y personalizar los mensajes de información cuando una de ellas “falle” a fin de informarle al usuarios que campo es el que no esta proporcionando correctamente, hablo de reglas como:

  • No nulos
  • No iguales
  • No vacíos
  • Iguales
  • Menores que
  • Menores o igual que
  • Mayores que
  • Mayores o igual que
  • Formato de Email
  • Contenido de cadenas, Expresiones regulares

En fin, reglas como las anteriores que a simple vista podrían verse sencillas, se complica en un proyecto de 3 Capas, puesto que este trabajo debe de realizarlo la capa de lógica y no la capa de presentación, imagínese tener que hacer una validación como las anteriores en un proyecto de una sola capa y tener que devolver un tipo de mensaje por cada regla fallida, sin lugar a dudas esto le llevaría bastantes líneas de código y un poco mas.

Antes de entrar de lleno en el tema de las validaciones recuerde que: “El objetivo no es otro mas que el de orientar a los Parvulos .Net sobre la arquitectura de software 3 capas y la validación de las reglas de Datos, todo lo escrito en este articulo no es ensayado y no es revisado por nadie mas, por lo cual podría contener errores gramaticales y sintácticos, el articulo y sus conceptos no pretenden ser la verdad absoluta del tema por lo tanto siéntase con la confianza de dejar sus comentarios y opiniones en la sección de comentarios al final del mismo y si lo considera prudente envíeme un correo electrónico por medio del formulario de contacto con sus ideas y opiniones sobre el tema, y por ultimo si el articulo le es de utilidad por favor considere dejar un comentario de agradecimiento, apoyar al mismo recomendando los artículos y unirse como miembro del blog. Si se siente agradecido puede ver los anuncios que están en este articulo haciendo Click en ellos.”

Requisitos: Visual Studio 2013, Framework 4.0, el proveedor de datos SqlCompact 4.0 instalado en su equipo y muchas ganas de aprender.

Como siempre recomiendo encarecidamente que antes de descargar los proyectos de ejemplo (que les pondré al final de articulo), traten de hacerlo ustedes mismos siguiendo paso a paso todo lo que se mencionara aquí, si tienen dudas en uno en especifico no duden en contactarme.

Validación de Reglas de negocio

Les mostrare como hacer uso de una librería que descubrí ya hace algunos años y que desde entonces no dejo de utilizar, hablo de.

FluentValidation

FluentValidation es una librería en donde por medio de Expresiones Lambda podemos fácilmente construir reglas de validación poderosísimas, con mucha facilidad y con una flexibilidad enorme, FluentValidation puede servirnos tanto para proyecto del tipo WindowsForms (como será nuestro caso) como para aplicaciones Web usando ASP.NET o ASP con MVC, por lo cual si usamos una arquitectura 3 capas en un proyecto WindowsForms y el día de mañana deseamos migrar nuestra capa de presentación a un proyecto web, no tendremos que preocuparnos por cambiar la manera de validar nuestras reglas de Datos debido a la compatibilidad entre estos, ¿no suena grandioso?, pues en la siguientes líneas les mostrare como usar esta librería.

Bien, para comenzar:

  • Descargue el proyecto de ejemplo anterior en el lenguaje que usted  mas domine
  • Localice la Capa de presentación llamada “Tienda-Presentacion”
  • Agregue un formulario nuevo y llámelo FrmEmpleado
  • Agregue algunos campos al formulario FrmEmpleado a fin de logar un diseño como este:

1

  • Nombre los controles acorde los campos que representan, recuerde que siempre es importante seguir una convención en cuanto a esto, anteponiendo al nombre una abreviatura del control, ejemplo, el Label que tiene establecido Numero en su propiedad Text, podría llamarse “lblNumero”, el TextBox que estará representando al Numero podría llamarse txtNumero, etc..
  • Ahora, hagamos un pequeño cambio en el proyecto anterior, suponemos que usted descargo la Base de datos SQlCompact llamado “DataBase1.Sdf”.
  • Click derecho sobre la capa de presentación –> Agregar –> Elemento Existente:

2

Diríjase a la carpeta donde se encuentra nuestro archivo de base de datos SqlCompact llamado “DataBase1.Sdf”, selecciónelo y presione “Agregar”.

3

Ubicados en nuestra capa de presentación, haga Click derecho sobre “DataBase1.Sdf” –> Seleccione Propiedades del menú emergente –> Ubique la propiedad “Copiar en el directorio de resultados” –> de las opciones desplegadas seleccione “No copiar”

4

Recuerde: Que el seleccionar esta propiedad nos permite trabajar con una copia de nuestra base de datos que el compilador automáticamente coloca dentro de la carpeta Bin/Debug en tiempo de ejecución, por lo cual conservaremos integra la Bd agregada en la capa de Presentación, ya que los datos que vayamos insertando se harán sobre esta copia.

Ya tenemos, correctamente configurada nuestra base de datos, ahora modifiquemos un poco nuestra cadena de conexión ubicado en el archivo de configuraciones “App.Config” a fin de que este proyecto sea capaz de leer el archivo de base de datos ubicado en bin/debug, nuestra cadena de conexión quedara de la siguiente forma:

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
<
configSections>
</
configSections>
<
connectionStrings>
<
add name="cnnString"
connectionString="Data Source=|DataDirectory|\Database1.sdf"
providerName="Microsoft.SqlServerCe.4.0" />
</
connectionStrings>
<
startup>
<
supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</
startup>
</
configuration>


  • Cree la entidad EEmpleado, la cual contendrá todas las propiedades necesarias para poder manejar los datos de un Empleado, para ello diríjase al proyecto de entidades llamado “Tienda-Entidades” –> Click derecho –> Agregar –> Nuevo Elemento –> Seleccione Clase –> Nombrela EEmpleado –> Click en el botón Agregar:

5


Abra la clase EEmpleado y dentro cree la siguiente estructura de código:

using System;

namespace Tienda_Entidades
{
public class EEmpleado
{
public int EmpleadoId { get; set; }
public string Nombre { get; set; }
public string ApellidoPaterno { get; set; }
public string ApellidoMaterno { get; set; }
public string Sexo { get; set; }
public string EstadoCivil { get; set; }
public DateTime FechaNacimiento { get; set; }
public string Telefono { get; set; }
public string TelefonoMovil { get; set; }
public DateTime FechaContratacion { get; set; }
}
}

Instalar FluentValidation en capa Lógica de Negocio


Para instalar FluentValidation en nuestra capa de Lógica de negocio tenemos dos opciones


1. Usar el paquete de Nuget para descargar en línea la librería e instalarla en nuestro proyecto, para esto:


Localice el proyecto de Lógica de Negocio –> Click derecho –> Administrar paquetes Nuget


6


Del panel izquierdo seleccione “En línea” –> en el panel izquierdo escriba FluentValidation –> Presione la tecla “Enter” –> Del panel  Central ubique FluentValidation –> Click sobre el botón Instalar


7


8


Después de instalado usted podrá ver la referencia de la librería dentro de las referencias del proyecto:


9


2. La segunda opción que tiene para instalar FluentValidation es mediante la creación de la referencia a la librería directamente desde las referencias del proyecto, para ello:



  • Descargue la librería desde esta dirección: FluentValidation
  • Ubique el proyecto Tienda-LogicaNegocio
  • Reference click derecho –> Agregar referencia

10



  • En el panel izquierdo elija “Examinar” –> presione el botón “Examinar” –> diríjase a la ubicación de descarga –> Seleccione la librería –> click en “Agregar” –> click en “Aceptar” –> Después usted podrá ver la referencia a la librería en la carpeta de referencias, tal cual como se muestra en la pantalla relacionado a Nuget.

Clases Validator


Una vez instalado FluentValidation en nuestro proyecto, cree una carpeta dentro de la capa de lógica y llámela “RulesValidation”, esta carpeta contendrá todas nuestras clases necesarias para realizar las validaciones de las distintas reglas de Datos, en esta ocasión solo crearemos una clase Validator ya que solo tenemos la entidad Empleados para fines de ejemplificación, pero en sus proyectos, usted tendrá que crear tantas clases Validator como entidades desee validar.


11


3. Posicionado en la carpeta “RulesValidation”, agregue una nueva clase y llámela “EmpleadoValidator”:


12


Nuestra clase EmpleadoValidator deberá de tener una estructura de código como la siguiente (todas las explicaciones de cada regla están en las siguientes líneas de código, si usted desea profundizar en el tema por favor visite la documentación de la librería en el link: Documentation o visite el foro de: Discussions):

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//
//Se requiere de las referencias a la libreria FluentValidation y al proyecto de Entidades
//
using FluentValidation;
using Tienda_Entidades;

namespace Tienda_LogicaNegocio.RulesValidation
{
//
//Hacemos que nuestra clase EmpleadoValidator sea de alcance publico y que herede de la clase AbstractValidator
//a la clase AbstractValidator le definimos la clase que validara
//
public class EmpleadoValidator : AbstractValidator<EEmpleado>
{
public EmpleadoValidator()
{
//CascadeMode, determina si la ejecucion de las reglas deberia de continuar al fallar una de ellas
//o detener la validacion del resto cuando alguna falle
//
//Aqui le indicamos que la validacion continue aun y cuando alguna de ellas falle
CascadeMode = CascadeMode.Continue;

//Como mencionamos FluentValidation se vale de Expresiones lambda para hacer las validaciones correspondientes
//
//EmpleadoId
//* No debe de permitir valores nulos
RuleFor(x => x.EmpleadoId)
.NotNull().WithMessage("El Número de Empleado no puede ser nulo");
//
//Nombre
//* No debe ser vacio
//* Debe ser una cadena con una longitud minima de 4 caracteres
//* Debe ser una cadena con una longitud maxima de 50 caracteres
RuleFor(x => x.Nombre)
.NotEmpty().WithMessage("El Nombre, debe ser diferente de vacio")
.Must(x => x.Length > 3).WithMessage("El Nombre, de tener mas de 3 caracteres")
.Must(x => x.Length < 51).WithMessage("El Nombre, debe tener menos de 51 caracteres");
//
//ApellidoPaterno
//* No debe ser vacio
//* Debe ser una cadena con una longitud minima de 4 caracteres
//* Debe ser una cadena con una longitud maxima de 50 caracteres
RuleFor(x => x.ApellidoPaterno)
.NotEmpty().WithMessage("Apellido Paterno debe ser diferente de vacio")
.Must(x => x.Length > 3).WithMessage("Apellido Paterno, de tener mas de 3 caracteres")
.Must(x => x.Length < 51).WithMessage("Apellido Paterno, debe tener menos de 51 caracteres");
//
//ApellidoMaterno
//* No debe ser vacio
//* Debe ser una cadena con una longitud minima de 4 caracteres
//* Debe ser una cadena con una longitud maxima de 50 caracteres
//
//Observe como para la propiedad ApellidoMaterno no establecemos valores para el Mensaje que FluentValidation
//debera de devolver en caso de que la regla de validación no se cumpla. FluentValidation cuenta con un
//diccionario de mensajes a devolver para las direrentes opciones de validacion con las que cuenta.
//
RuleFor(x => x.ApellidoMaterno)
.NotEmpty()
.Must(x => x.Length > 3)
.Must(x => x.Length < 51);
//
//Sexo
//*El valor no puede ser Nulo
//*El valor debe ser igual a Femenido o Masculino
//
//Observe el uso de operador OR (||) para comparar las cadenas
//
RuleFor(x => x.Sexo)
.NotNull().WithMessage("Por favor seleccione el Sexo del Empleado")
.Must(x => x == "Femenino" || x == "Masculino").WithMessage("Por favor seleccione el Sexo del Empleado");
//
//EstadoCivil
//* El valor no puede ser nulo
//* El valor no puede estar vacio
//* El valor debe ser diferente de "<<<Seleccione>>>"
RuleFor(x => x.EstadoCivil).NotNull().NotEmpty().NotEqual("<<<Seleccione>>>");
//
//FechaNacimiento
//* El valor debe ser menor a la fecha del SO
//
RuleFor(x => x.FechaNacimiento)
.LessThan(DateTime.Today.Date).WithMessage("La Fecha de Nacimiento no es valida");
//
//Telefono
//* Debe ser diferente de Nulo
//* Debe ser diferente a vacio
//* Debe ser de una longitud de 10 caracteres
//* Debe tener un formato diferente a...
//
//Observe como para la validacion de Nulo no se definio un mensaje de informacion
RuleFor(x => x.Telefono)
.NotNull()
.NotEmpty().WithMessage("Teléfono de contacto, debe ser diferente de vacio")
.Length(10).WithMessage("Teléfono de contacto, debe tener una longitud de '10' caracteres")
.Must(x => x != "1111111111" && x != "2222222222"
&& x != "3333333333" && x != "4444444444"
&& x != "5555555555" && x != "6666666666"
&& x != "7777777777" && x != "8888888888"
&& x != "9999999999").WithMessage("Teléfono de contacto, no tiene un formato valido");
//
//Telefono Movil
//* Mismas reglas que la propiedad Telefono
//Observe como para la validacion de Nulo, Vacio y Longitud no se definio un mensaje de informacion
RuleFor(x => x.TelefonoMovil)
.NotNull()
.NotEmpty()
.Length(10)
.Must(x => x != "1111111111" && x != "2222222222"
&& x != "3333333333" && x != "4444444444"
&& x != "5555555555" && x != "6666666666"
&& x != "7777777777" && x != "8888888888"
&& x != "9999999999").WithMessage("Teléfono móvil del cliente, no tiene un formato valido");
}
}
}


  • Agregue una nueva clase al proyecto y llámela “CustomException”, esta clase nos permitirá desde la capa de presentación diferenciar cuando las excepciones son generadas por alguna regla de Datos fallida y cuando son generadas por alguna excepción no controlada en la aplicación(al menos en esta ocasión), mas adelante estoy seguro que usted podrá explotar toda la funcionalidad de esta clase.
  • Esta es la estructura de la clase:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Threading.Tasks;

    namespace Tienda_LogicaNegocio
    {
    [
    Serializable]
    public class CustomException : Exception
    {
    public CustomException()
    :
    base() { }

    public CustomException(string message)
    :
    base(message) { }

    public CustomException(string format, params object[] args)
    :
    base(string.Format(format, args)) { }

    public CustomException(string message, Exception innerException)
    :
    base(message, innerException) { }

    public CustomException(string format, Exception innerException, params object[] args)
    :
    base(string.Format(format, args), innerException) { }

    protected CustomException(SerializationInfo info, StreamingContext context)
    :
    base(info, context) { }
    }
    }

  • Ya que tenemos nuestra clase Validator y nuestra CustomExpcetion, inserte una nueva clase de lógica dentro del proyecto “Tienda-LogicaNegocio” y llamela “EmpleadoBol”:

13


Esta clase será la que consuma nuestro EmpleadoValidator y será el encargado de devolver a presentación las excepciones generadas desde el Validator.


Esta es la estructura de código que deberá de contener la clase “EmpleadoBol”:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//
//Hacemos la referencia a FluentValidation.Results, la cual nos ayudara a conocer los resultados de nuestras validaciones
//
using FluentValidation.Results;
using Tienda_AccesoDatos;
using Tienda_Entidades;
using Tienda_LogicaNegocio.RulesValidation;

namespace Tienda_LogicaNegocio
{
public class EmpleadoBol
{
//Instanciamos nuestra clase ProductoDal para poder utilizar sus miembros
private readonly EmpleadoDal _empleadoDal = new EmpleadoDal();
//
//Creamos una instancia de la clase EmpleadoValidator
readonly EmpleadoValidator _empleadoValidator = new EmpleadoValidator();

public void Nuevo(EEmpleado empleado)
{
//Enviamos nuestra entidad a validar usando la funcion Validate de la clase EmpleadoValdiator
//recuerde que esta calse hereda de FluentValidator
ValidationResult result = _empleadoValidator.Validate(empleado);

//pregutnamos si nuestra validacion fue exitosa
if (result.IsValid)
{
//
//en caso de que todas las reglas fallaron, se procede a usar el metodo Guardar de la clase EmpleadoDAL
_empleadoDal.Guardar(empleado);
}
else
{
//si alguna regla fallo recuperamos los mensajes y los enviamos a nuestro CustomValidator para ser atrapados
//en presentacion...
throw new CustomException(Validator.GetErrorMessages(result.Errors));
}
}
}
}

Podrá usted observar que en esta clase hacemos uso de una función llamada GetErrorMessages perteneciente a otra clase llamada Validator, no muestro la estructura de esta clase en el articulo solo por cuestiones de relevancia, pero usted podrá hacer el respectivo análisis descargando el proyecto de ejemplo.


Capa de Presentación, enviar la entidad y atrapar las excepciones


Ya que tenemos nuestras capas superiores perfectamente configuradas, ahora le toca a la capa de Presentación entrar en juego, diríjase a ella, seleccione el formulario FrmEmpleado y presione F7 para dirigirse a la ventana de código, la estructura de código que deberá de crear debe ser similar a esta:

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 Tienda_LogicaNegocio;
using Tienda_Entidades;
namespace Tienda_Presentacion
{
public partial class FrmEmpleado : Form
{
private EEmpleado _empleado;
private EmpleadoBol _empleadoBol = new EmpleadoBol();
public FrmEmpleado()
{
InitializeComponent();
}

private void Nuevo()
{
try
{
_empleado
= new EEmpleado();

_empleado
.EmpleadoId = string.IsNullOrEmpty(txtNumero.Text.Trim())
? 0
: Convert.ToInt32(txtNumero.Text.Trim());
_empleado
.Nombre = txtNombre.Text.Trim();
_empleado
.ApellidoPaterno = txtApellidoPaterno.Text.Trim();
_empleado
.ApellidoMaterno = txtApellidoMaterno.Text.Trim();
_empleado
.Sexo = Convert.ToString(cmboxSexo.Text);
_empleado
.EstadoCivil = Convert.ToString(cmboxEstadoCivil.Text);
_empleado
.FechaNacimiento = dtpFechaNacimiento.Value.Date;
_empleado
.Telefono = mskTelefono.Text;
_empleado
.TelefonoMovil = mskTelefonoMovil.Text;
_empleado
.FechaContratacion = dtpFechaContratacion.Value.Date;

_empleadoBol
.Nuevo(_empleado);
}
catch (CustomException ex)
{
MessageBox.Show(this, ex.Message, "Parvulos .Net", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
catch (Exception ex)
{
MessageBox.Show(string.Format("Error: {0}", ex.Message), "Error inesperado", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}

private void FrmEmpleado_Load(object sender, EventArgs e)
{
cmboxEstadoCivil
.SelectedIndex = 0;
cmboxSexo
.SelectedIndex = 0;
}
private void btnAceptar_Click(object sender, EventArgs e)
{
Nuevo();
}

}
}

Observe como nuestra manera de trabajar en la capa de presentación no cambia en mucho, a excepción de la manera de atrapar nuestras excepciones, ahí es donde aplicamos un pequeño cambio para poder distinguir cuando es una excepción por alguna regla de datos y cuando es por algo no controlado.


Ya que tenemos todo perfectamente ligado, procedamos a correr nuestra aplicación para hacer nuestros primeros análisis.


1. Primero haremos click en el botón “Aceptar” sin introducir un solo registro:


14


15


* Observe como los primeros dos mensajes (encerrado en verde) corresponden a la validación de nuestra regla para la propiedad Nombre y que nos devuelve los mensajes que definimos.


* Observe como las validaciones para la propiedad ApellidoMaterno (encerrado en rojo), devuelven mensajes propios para las validaciones que definimos, sin que hayamos especificado mensajes personalizados


* Observe el mensaje para las propiedades Sexo y Estado Civil (encerrado en azul), ponga atención en como uno se diferencia del otro en función de si definimos o no un mensaje personalizado.


* Una situación similar ocurre con los teléfonos.


2. Ahora llene parcialmente los campos:


16


17


* Observe que en las reglas ya no muestra el mensaje para Nombre del empleado y Sexo.


* Observe como el mensaje para el Apellido Paterno cambio.


* Observe como a pesar de seleccionar una fecha para el campo Fecha nacimiento, este sigue generando excepciones, ¿Por que?, porque a pesar de que seleccionamos una fecha esta no es valida debido a que es superior a la fecha actual del sistema (21/02/2015) y una fecha de Nacimiento no puede ser mayor al actual (al menos no para un Empleado).


* Observe como a pesar de que introdujimos un valor para el campo Teléfono, seguimos obteniendo un mensaje debido a que no cumple con un formato valido.


3. Ahora llenemos todos nuestros campos según definimos nuestras reglas de validación:


18


19


* Observe como nuestro mensaje de validación, titulo del mensaje y el icono cambiaron, ¿por que?, porque esta excepción ya no fue generada por una regla de datos fallida, sino por que el método encargado de guardar la entidad aun no esta implementado, si ustedes navegan hacia la clase EmpleadosDal verán la siguiente estructura de código:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tienda_Entidades;

namespace Tienda_AccesoDatos
{
public class EmpleadoDal
{
public void Guardar(EEmpleado empleado)
{
throw new NotImplementedException();
}

public void Traer(int empleadoId)
{
throw new NotImplementedException();
}

public void Actualizar(EEmpleado empleado)
{
throw new NotImplementedException();
}
}
}

Observe como el método Guardar, solicitado por EmpleadoBol cuando se cumplen todas las reglas de validación, realmente no esta implementado y en su lugar se genera una excepción, esta parte no la implemente para que usted pudiera observar las diferencias entre una excepción generada por una regla de negocio y una excepción generada por código no controlado, pero, estoy seguro que como usted ya leyó el articulo previo a este no le será difícil implementar las líneas de código necesarias para hacer que este método funcione.


Aquí termina nuestro articulo sobre Arquitectura 3 Capas- Validación de reglas de datos, espero haya sido de su agrado y que la explicación haya sido lo bastante clara como para que en sus próximos desarrollos usted sea capaz de usar FluentValidation sin ningún problema, en caso de que tenga alguna duda por favor deje su pregunta en la sección de comentarios o escríbame por medio del formulario de contacto.


Escribir este articulo me llevo mas de 4 horas, dejar un comentario de agradecimiento le tomara 5 minutos, usted podría apoyar al foro viendo la publicidad que en el se muestra.


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

domingo, 28 de septiembre de 2014

ReportViewer y Rdlc, ejemplo Factura- Eviar Imagen


El envió de imágenes a un reporte Local o remoto siempre es muy necesario, en muchas ocasiones se requieren para hacer un reporte personalizado utilizando el logo de la empresa, firma digital, etc. o simplemente para embellecer nuestros reportes “recordando que una imagen vale mal que mil palabras”

En este articulo aprovecharemos un proyecto anterior que ya tenemos creado y creo que la mayoría de ustedes ya analizo  y puso en practica los conceptos ahí mencionados para mostrar como enviar una imagen a un Local Report, si aun no tiene el proyecto anterior o tiene dudas sobre como crear un reporte local por favor antes de continuar sírvase a revisar el articulo abajo mencionado:
ReportViewer y Rdlc, ejemplo Factura (Base de datos)
Extenderemos el proyecto insertando un objeto Imagen al Report1.rdlc y enviándole un arreglo de Bytes desde el formulario principal dicho arreglo contendrá los bytes de archivo tipo *.png

Antes de comenzar a leer este articulo recuerde que: “El objetivo no es otro mas que el de orientar a los Parvulos .Net sobre como Utilizar el control Imagen de un Local Report, todo lo escrito en este articulo no es ensayado y no es revisado por nadie mas, por lo cual podría contener errores gramaticales y sintácticos, el articulo y sus conceptos no pretenden ser la verdad absoluta del tema, siéntase en confianza de dejar sus comentarios y opiniones en la sección de comentarios al final del mismo y si lo considera prudente envíeme un correo electrónico por medio del formulario de contacto y por ultimo si el articulo le es de utilidad por favor considere dejar un comentario de agradecimiento.”

Requisitos: Visual Studio 2012, Framework 4.0

1. Para empezar, descargue el proyecto ofrecido en el articulo anterior, ábralo utilizando Vs2012, espere a que termine la conversión del código.

2. Abra el formulario principal llamado Form1
image

3. Cree el siguiente diseño utilizando un PictureBox y un Botón (recuerde que el objetivo del articulo es mostrar como se envía un objeto imagen a un Local Report así que ignore mi escaso sentido del diseño), nombre a los controles como pctboxLogo y btnSeleccionar respectivamente.
image

4. En las propiedades del objeto PictureBox, seleccione StretchImage para que la imagen que seleccione se autoajuste al tamaño de nuestro control.
image

5. Haga doble clic sobre el botón que acabamos de crear a fin de generar la estructura de código del evento Click, dentro coloque las siguientes líneas de código:

private void btnSeleccionar_Click(object sender, EventArgs e)
{
    //
    //Utilizaremos el la clase OpenFileDialog para seleccionar una imagen
    OpenFileDialog fileDialog = new OpenFileDialog();
    //
    //Definimos los filtros necesarios para evitar ver archivos que no corresponden a archivo de imagen
    fileDialog.Filter = "Archivo JPG (*.jpg)|*.jpg| Archivo PNG (*.png)|*.png| Archivo BMP (*.bmp)|*bmp";

    if (fileDialog.ShowDialog() == DialogResult.OK)
    {
        //
        //Cargamos nuestro control PictureBox con el archivo de imagen seleccionado
        pctboxLogo.Image = Image.FromFile(fileDialog.FileName);
    }

Probemos estas líneas de código:
image

Puesto que pudimos cargar una imagen a nuestro PictureBox el código anterior funciona. Continuemos…

Para poder enviar nuestro arreglo de Bytes al Local Report necesitamos crear una nueva propiedad que será la encargada de transportar los Bytes, como el Logo forma parte del Header de la factura, usaremos la clase EFactura, para esto.

6. Desde el “Explorador de Soluciones” haga doble clic sobre la clase EFactura y cree una nueva propiedad llamada Logo la cual será de tipo Bytes[] con acceso publico:

using System;using System.Collections.Generic;
namespace ReportViewerInvoiceReport_CSharp
{
    public class EFactura
    {
        public int Numero { get; set; }
        public string Nombre { get; set; }
        public string Rfc { get; set; }
        public string Direccion { get; set; }
        public decimal Subtotal { get; set; }
        public decimal Iva { get; set; }
        public decimal Total { get; set; }
        public DateTime FechaFacturacion { get; set; }
        //
        //Propiedad con acceso publico del tipo Byte, será el encargado de transportar los bytes de nuestro imagen
        public Byte[] Logo { get; set; }

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

7. Ahora vayamos a configurar nuestro Local Report para recibir esta imagen como parte de los datos que debe de mostrar, para ello, desde el Explorador de Soluciones abra el reporte llamado Report1.rdlc haciendo doble clic sobre el:
image

Tendremos que modificar un poco nuestro  diseño a fin de darle espacio en el Header al control Imagen, que será el encargado de mostrar la imagen que fungirá como logo de la empresa.

8. Modifique el diseño arrastrando los controles hacia abajo hasta que considere prudente.
image

Antes de continuar con nuestro diseño tenemos que actualizar nuestro origen de datos a fin de tener acceso a la propiedad Logo de nuestra clase EFactura, pero antes de eso observe que la propiedad agregada pasos anteriores aun no esta visible y por lo tanto no esta disponible para ser utilizado en nuestro reporte:
image

image

y por mas que hagamos Clic derecho Actualizar no veremos jamás esta propiedad hasta que no hagamos una nueva compilación de nuestro proyecto, así que:

10. Diríjase a la barra de Menús –> Compilar –> Compilar Solución
image

11. Observe como después de la compilación en el cuadro de Origen de Datos podemos visualizar una nueva propiedad llamada Logo:
image

12. Diríjase al cuadro “Datos del informe”- > clic derecho sobre el origen “Encabezado” –> Actualizar y observe que nuestra propiedad se hace presente en este origen de datos.
image

Ahora que tenemos nuestra nueva propiedad como parte del origen de datos de nuestro informe podemos utilizarlo para asignárselo al objeto Imagen.

13. Desde el cuadro de herramientas ubique el control Imagen y arrástrelo al Header de nuestro local report.
image

14. Automáticamente veremos el cuadro de dialogo “Propiedades de la imagen”, ubique la opción “Seleccionar el origen de la imagen” y seleccione “Base de datos”, ubique la opción “Usar este campo:” y seleccione “=First(Fields!Logo.Value, "Encabezado")”, ubique la opción “Usar este tipo MIME:” y seleccione “image/png” (esta ultima opción lo dejo a su consideración no necesariamente tiene que ser este tipo de imagen)
image

15. Haga clic en “Aceptar” y observe como nuestro nuevo control fue agregado al Header de nuestro Local Report, juegue un poco con  el tamaño.
image

Para continuar vayamos a nuestro Form1 a terminar de configurar el envió del arreglo de bytes

16. En la parte superior de nuestra estructura de código, cree el siguiente método que nos ayudara a la transformación de nuestra imagen en un arreglo de bytes.

private byte[] GetBytes(Image imageIn)
{
    //
    //Usamos la clase MemoryStream para contener los bytes que compone la imagen
    MemoryStream ms = new MemoryStream();
    imageIn.Save(ms, ImageFormat.Png);
    //
    //Retornamos el arreglo de bytes
    return ms.ToArray();
}

17. Ubique el método InvoiceGenerate() y agregue las siguiente líneas de código:
//
// Usamos la Función GetBytes() para convertir la imagen cargada al PicureBox en un arreglo de Bytes para establecérselo a nuestra propiedad Logo_factura.Logo = GetBytes(pctboxLogo.Image);

Después de haber realizado todo lo anterior solo nos resta probar nuestra aplicación.

Ejecute la aplicación presionado la tecla “F5” o desde el menú presione el botón “Iniciar”
image

En el campo “Numero” ingrese cualquier valor del 1 al 5 y presione “Enter”
image

Haga Clic sobre el botón “…” para seleccionar una imagen (yo escogeré una imagen del tipo png)
image

Observe como nuestro PictureBox muestra la imagen seleccionada:
image

Presione el botón “Imprimir” y observe como nuestra imagen es mostrada en nuestro Local Report
image
image

Repita esta operación con tantas imágenes guste…

Por ultimo recuerde que el origen de la imagen podrías ser un arreglo de bytes almacenado en un campo de una base datos, el resultado siempre será el mismo ya que al Local Report no le interesa ni necesita saber el origen de la imagen siempre y cuando esta venga como un arreglo de Bytes

Escribir este articulo me llevo mas de 3 horas, dejar un comentario de agradecimiento le tomara 5 minutos.

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

Ejemplo C#
Ejemplo Vb.Net


Nota: Al momento de migrar la aplicación de Vs2010 a Vs2012 recibí este mensaje de advertencia “

Advertencia    1    El proyecto hace referencia a una versión anterior de SQL Server Compact que no tiene compatibilidad con el diseñador en la versión actual de Visual Studio. Para continuar sin compatibilidad con el diseñador, descargue SQL Server Compact 3.5 del centro de descarga de Microsoft (http://go.microsoft.com/fwlink/?LinkId=229598). Para actualizar los activos a SQL Server Compact 4.0 y habilitar la compatibilidad con el diseñador, abra el proyecto y use el cuadro de diálogo Agregar conexión para crear una conexión a la base de datos. ReportViewerInvoiceReport-CSharp”

No tuve la necesidad de realizar absolutamente nada para poder trabajar con el proyecto de ejemplo pero si usted lo desea podría actualizar la base de datos a la versión 4.0, el script para crear las estructuras de las tablas la podrá descargar en el link proporcionado en el proyecto anterior.