domingo, 13 de febrero de 2011

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

 

Introducción


El objetivo de este articulo será reflejar los siguiente puntos:

- Grabar una imagen en un campo de una base de datos

- Recuperar la imagen visualizándola en un celda del DataGridView y en un PictureBox

- Comunicar un formulario pasándole información en el constructor para la edición de una entidad

- Detectar el cierre del formulario para actualizar la información de los empleados

 

Dominio de la aplicación


Bien empezaremos definiendo al estructura de datos de la aplicación.

imagen1

 

El dominio consiste en Empleados, con información básica del mismo, mas un campo de imagen el cual es opcional por lo cual esta marcado para permitir nulos.

El atributo Estado Civil tomara los valores de 1 a 4, los cuales fueron definido en un enumerado.

Adicionalmente el empleado podrá tener Estudios realizados, los cuales se asignan marcando de una lista.

El diseño de la interfaz es simple, un formulario principal que lista los usuarios.

imagen2

Y otro que edita el usuario cuando se realiza un doble click en la fila de la lista.

imagen3

La arquitectura de la aplicación es en dos capas, la presentación se comunica con DataAccess pasándole la información que necesita persistir o recuperar, el medio de comunicación son entidades definidas por medio de clases que modelan el dominio de la aplicación.

imagen4

Si bien el esquema de estas entidades son muy parecido al de la base de datos ,se debe apreciar que la entidad “Empleado” posee una relación con la de estudios, lo cual posibilita la navegación de la información asociada.

 

Cargar lista de empleados (con imágenes)


En esta sección analizaremos como cargar las imágenes en el DataGridView, tomando esta información desde la base de datos.

El primer paso será recuperar los registros de la tabla, incluyendo la imagen.

[C#]

public static List<EmpleadoEntity> ObtenerTodos()
{
    List<EmpleadoEntity> lista = new List<EmpleadoEntity>();

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

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

        SqlCommand cmd = new SqlCommand(query, conn);

        SqlDataReader reader = cmd.ExecuteReader();

        while (reader.Read())
        {
            lista.Add(ConvertirEmpleado(reader, false));
        }

    }

    return lista;
}

private static EmpleadoEntity ConvertirEmpleado(IDataReader reader, bool cargarRelaciones)
{
    EmpleadoEntity empleado = new EmpleadoEntity();

    empleado.IdEmpleado = Convert.ToInt32(reader["IdEmpleado"]);
    empleado.Nombre = Convert.ToString(reader["Nombre"]);
    empleado.Apellido = Convert.ToString(reader["Apellido"]);
    empleado.FechaNacimiento = Convert.ToDateTime(reader["FechaNacimiento"]);
    empleado.EstadoCivil = Convert.ToInt16(reader["EstadoCivil"]);

    if (reader["Imagen"] != DBNull.Value)
        empleado.Imagen = (byte[])reader["Imagen"];

    if (cargarRelaciones)
    {
        empleado.Estudios = EstudiosDAL.ObtenerAsignadoEmpleado(empleado.IdEmpleado);
    }

    return empleado;
}		

[VB.NET]

Public Shared Function ObtenerTodos() As List(Of EmpleadoEntity)

    Dim lista As New List(Of EmpleadoEntity)()

    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
        conn.Open()

        Dim query As String = "SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen" & _
                              "FROM Empleados"

        Dim cmd As New SqlCommand(query, conn)

        Dim reader As SqlDataReader = cmd.ExecuteReader()

        While reader.Read()
            lista.Add(ConvertirEmpleado(reader, False))
        End While

    End Using

    Return lista

End Function


Private Shared Function ConvertirEmpleado(ByVal reader As IDataReader, ByVal cargarRelaciones As Boolean) As EmpleadoEntity

    Dim empleado As New EmpleadoEntity()

    empleado.IdEmpleado = Convert.ToInt32(reader("IdEmpleado"))
    empleado.Nombre = Convert.ToString(reader("Nombre"))
    empleado.Apellido = Convert.ToString(reader("Apellido"))
    empleado.FechaNacimiento = Convert.ToDateTime(reader("FechaNacimiento"))
    empleado.EstadoCivil = Convert.ToInt16(reader("EstadoCivil"))

    If reader("Imagen") IsNot DBNull.Value Then
        empleado.Imagen = DirectCast(reader("Imagen"), Byte())
    End If

    If cargarRelaciones Then
        empleado.Estudios = EstudiosDAL.ObtenerAsignadoEmpleado(empleado.IdEmpleado)
    End If

    Return empleado

End Function	

Es preciso notar como se asigna la imagen a la entidad

[C#]

if (reader["Imagen"] != DBNull.Value)
    empleado.Imagen = (byte[])reader["Imagen"];

[VB.NET]

If reader("Imagen") IsNot DBNull.Value Then
    empleado.Imagen = DirectCast(reader("Imagen"), Byte())
End If

lo cual resulta muy simple.

En el formulario “ListaEmpleados” se usa esta información para cargar el DataGridView

[C#]

private void CargarListaEmpleados()
{
    dgvEmpleados.AutoGenerateColumns = false;
    dgvEmpleados.DataSource = EmpleadosDAL.ObtenerTodos();

    foreach (DataGridViewRow row in dgvEmpleados.Rows)
    {
        //se asigna el alto de la fila para poder ver la imagen correctamente
        row.Height = 120; 

        //se recupera la entidad que es asignada como dato
        EmpleadoEntity empleado = row.DataBoundItem as EmpleadoEntity;

        if (empleado.Imagen == null)
            row.Cells["Imagen"].Value = ImageHelper.ObtenerImagenNoDisponible();
        else
            row.Cells["Imagen"].Value = ImageHelper.ByteArrayToImage(empleado.Imagen); 
    }

}

[VB.NET]

Private Sub CargarListaEmpleados()
	dgvEmpleados.AutoGenerateColumns = False
	dgvEmpleados.DataSource = EmpleadosDAL.ObtenerTodos()

	For Each row As DataGridViewRow In dgvEmpleados.Rows
		'se asigna el alto de la fila para poder ver la imagen correctamente
		row.Height = 120

		'se recupera la entidad que es asignada como dato
		Dim empleado As EmpleadoEntity = TryCast(row.DataBoundItem, EmpleadoEntity)

		If empleado.Imagen Is Nothing Then
			row.Cells("Imagen").Value = ImageHelper.ObtenerImagenNoDisponible()
		Else
			row.Cells("Imagen").Value = ImageHelper.ByteArrayToImage(empleado.Imagen)
		End If
	Next

End Sub

seguramente una pregunta que se hagan es porque en este método se esta recorriendo las filas del DataGridView después de asignar los datos al mismo ?

bien esto tiene una explicación, resulta que la celda de imagen del DataGridView requiere un tipo Image para poder trabajar, pero la entidad que proporcionamos devuelve un array de byte, lo cual no es compatible, entonces es que se aprovecha la capacidad del DataGridView para retornar la entidad que bindea a cada fila para obtener la entidad “Empleado” y convertir el array de byte al tipo Image.

También debo aclara que esto se intento hacer en un evento, como ser el CellFormatting, pero resulto un desastre porque este evento repintaba constantemente la información generando un parpadeo molesto en el control, se podría haber realizado esto mismo en el evento DataBindingComplete, pero para este caso no valía la pena separa la funcionalidad, por eso se dejo allí mismo.

En ese método se hace uso del la funcionalidad de “helper” que no es mas que un grupo de funciones que facilitan ciertas tareas, en este caso convierten el array de byte en Image.

 

Comunicar Formulario y detectar cierre del form hijo


Al realizar un doble click en la celda del DataGridView este entra en modo edición, esto implica simplemente toma la información y pacérsela al formulario

Desde “ListaEmpleados” se trabaja con el evento tomando solo el id del empleado, y en la instancia del formulario se lo pasa en el constructor. También en la misma operación se adjunta el evento FormClosing para poder detectar el cierre el form y proceder a la actualización del la lista de empleados.

[C#]

private void dgvEmpleados_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
{
    int IdEmpleado = Convert.ToInt32(dgvEmpleados.Rows[e.RowIndex].Cells["IdEmpleado"].Value);

    //
    // al pasarle un id de empleado este lo cargara para su edicion
    //
    EditarEmpleado frmEditar = new EditarEmpleado(IdEmpleado);
    frmEditar.FormClosing += new FormClosingEventHandler(frmEditar_FormClosing);

    frmEditar.Show();
}

void frmEditar_FormClosing(object sender, FormClosingEventArgs e)
{
	//
	// al cerrarse el form de edicion se ingresa a este evento 
	// para actualizar la informacion del listado
	//

	EditarEmpleado frmEdit = sender as EditarEmpleado;

	if(frmEdit.DialogResult == DialogResult.OK)
		CargarListaEmpleados();

}

[VB.NET]

Private Sub dgvEmpleados_CellContentDoubleClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs)

    Dim IdEmpleado As Integer = Convert.ToInt32(dgvEmpleados.Rows(e.RowIndex).Cells("IdEmpleado").Value)

    '
    ' al pasarle un id de empleado este lo cargara para su edicion
    '
    Dim frmEditar As New EditarEmpleado(IdEmpleado)
    AddHandler frmEditar.FormClosing, New FormClosingEventHandler(AddressOf frmEditar_FormClosing)

    frmEditar.Show()

End Sub

Private Sub frmEditar_FormClosing(sender As Object, e As FormClosingEventArgs)
	'
	' al cerrarse el form de edicion se ingresa a este evento 
	' para actualizar la informacion del listado
	'

	Dim frmEdit As EditarEmpleado = TryCast(sender, EditarEmpleado)

	If frmEdit.DialogResult = DialogResult.OK Then
		CargarListaEmpleados()
	End If

End Sub

En el formulario que recibe esta información “EditarEmpleado” solo utiliza el id del empleado, porque con este ira a la db y recuperara la entidad para cargar el formulario, no hace falta enviar la entidad completa, solo la mínima información como para que este form pueda recuperar el resto.

[C#]

  public partial class EditarEmpleado : Form
   {
       private int? _idEmpleado = null;

       public EditarEmpleado()
       {
           InitializeComponent();
       }

       public EditarEmpleado(int idEmpleado)
           : this()
       {
           _idEmpleado = idEmpleado;
       }
	
	.
	.
	.
}	

[VB.NET]

 

Public Partial Class EditarEmpleado
    Inherits Form

    Private _idEmpleado As Nullable(Of Integer) = Nothing

	Public Sub New()
		InitializeComponent()
	End Sub

	Public Sub New(idEmpleado As Integer)
		Me.New()
		_idEmpleado = idEmpleado
	End Sub
	
	.
	.
	.
End Class

Cuando se termina la edición se retorna como un resultado del formulario que indicara si se grabo o se cancelo el formulario, esto se logra por medio del DialogResult.

[C#]

		
private void btnGuardar_Click(object sender, EventArgs e)
{
	.
	.
	.

	//
	// se graba 
	//
	EmpleadosDAL.Save(empleado);

	this.DialogResult = DialogResult.OK;
	this.Close();
}

private void btnCancelar_Click(object sender, EventArgs e)
{
	this.DialogResult = DialogResult.Cancel;
	this.Close();
}		

 

[VB.NET]

Private Sub btnGuardar_Click(sender As Object, e As EventArgs)

	.
	.
	.

	'
	' se graba 
	'
	EmpleadosDAL.Save(empleado)

	Me.DialogResult = DialogResult.OK
       Me.Close()

End Sub

   Private Sub btnCancelar_Click(ByVal sender As Object, ByVal e As EventArgs)

       Me.DialogResult = DialogResult.Cancel
       Me.Close()

   End Sub

Editar Empleado (mostrando la imagen en un Picturebox)


Esta tarea es bastante simple, solo implica recuperar una entidad en concreto haciendo uso de la cada de datos y cargar la entidad en los controles para presentar esta al usuario.

[C#]

public static EmpleadoEntity ObtenerById(int idEmpleado)
{
    EmpleadoEntity empleado = null;

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

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

        SqlCommand cmd = new SqlCommand(query, conn);
        cmd.Parameters.AddWithValue("@idEmpleado", idEmpleado);

        SqlDataReader reader = cmd.ExecuteReader();

        if (reader.Read())
        {
            empleado = ConvertirEmpleado(reader, true);
        }

    }

    return empleado;
}

[VB.NET]

Public Shared Function ObtenerById(ByVal idEmpleado As Integer) As EmpleadoEntity

    Dim empleado As EmpleadoEntity = Nothing

    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
        conn.Open()

        Dim query As String = "SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento, EstadoCivil, Imagen " & _
                              "FROM Empleados WHERE IdEmpleado = @idEmpleado"

        Dim cmd As New SqlCommand(query, conn)
        cmd.Parameters.AddWithValue("@idEmpleado", idEmpleado)

        Dim reader As SqlDataReader = cmd.ExecuteReader()

        If reader.Read() Then
            empleado = ConvertirEmpleado(reader, True)
        End If

    End Using

    Return empleado

End Function

Una vez que se tiene la entidad solo se carga esta en los contoles.

[C#]

private void EditarEmpleado_Load(object sender, EventArgs e)
{
    CargarEstadoCivil();
    CargarEstudio();

    //por defecto se carga una imagen de no disponible
    picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible();

    //
    // se carga la informacion del empleado que se quiere editar
    //
    if (_idEmpleado.HasValue)
    {
        EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(_idEmpleado.Value);

        _idEmpleado = empleado.IdEmpleado;
        txtNombre.Text = empleado.Nombre;
        txtApellido.Text = empleado.Apellido;
        dtpFechaNacimiento.Value = empleado.FechaNacimiento;

        cbEstadoCivil.SelectedValue = Convert.ToInt32(empleado.EstadoCivil);

        if (empleado.Imagen == null)
            picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible();
        else
            picImagenEmpleado.Image = ImageHelper.ByteArrayToImage(empleado.Imagen);

        //
        // Se obtienen los estudios del empleado
        //
        AsignarEstudios(empleado);

    }

}

private void CargarEstadoCivil()
{
    cbEstadoCivil.DisplayMember = "Descripcion";
    cbEstadoCivil.ValueMember = "IdEstadoCivil";
    cbEstadoCivil.DataSource = EstadoCivilDAL.ObtenerTodos();
}

private void CargarEstudio()
{
    dgvEstudios.AutoGenerateColumns = false;
    dgvEstudios.DataSource = EstudiosDAL.ObtenerTodos();
}

private void AsignarEstudios(EmpleadoEntity empleado)
{
    
    List<DataGridViewRow> rows = (from row in dgvEstudios.Rows.Cast<DataGridViewRow>()
                                    let idEstudio = Convert.ToInt32(row.Cells["IdEstudio"].Value)
                                  join dd in empleado.Estudios on idEstudio equals dd.IdEstudio
                                  select row).ToList();


    rows.ForEach(o => o.Cells["Seleccion"].Value = true);
    
}

[VB.NET]

   Private Sub EditarEmpleado_Load(ByVal sender As Object, ByVal e As EventArgs)

       CargarEstadoCivil()
       CargarEstudio()

       'por defecto se carga una imagen de no disponible
       picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible()

       '
       ' se carga la informacion del empleado que se quiere editar
       '
       If _idEmpleado.HasValue Then
           Dim empleado As EmpleadoEntity = EmpleadosDAL.ObtenerById(_idEmpleado.Value)

           _idEmpleado = empleado.IdEmpleado
           txtNombre.Text = empleado.Nombre
           txtApellido.Text = empleado.Apellido
           dtpFechaNacimiento.Value = empleado.FechaNacimiento

           cbEstadoCivil.SelectedValue = Convert.ToInt32(empleado.EstadoCivil)

           If empleado.Imagen Is Nothing Then
               picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible()
           Else
               picImagenEmpleado.Image = ImageHelper.ByteArrayToImage(empleado.Imagen)
           End If

           '
           ' Se obtienen los estudios del empleado
           '

           AsignarEstudios(empleado)
       End If


   End Sub

   Private Sub CargarEstadoCivil()

       cbEstadoCivil.DisplayMember = "Descripcion"
       cbEstadoCivil.ValueMember = "IdEstadoCivil"
       cbEstadoCivil.DataSource = EstadoCivilDAL.ObtenerTodos()

   End Sub

Private Sub CargarEstudio()
	dgvEstudios.AutoGenerateColumns = False
	dgvEstudios.DataSource = EstudiosDAL.ObtenerTodos()
End Sub

Private Sub AsignarEstudios(empleado As EmpleadoEntity)

       Dim rows As List(Of DataGridViewRow) = (From row In dgvEstudios.Rows.Cast(Of DataGridViewRow)() _
                                                Let idEstudio = Convert.ToInt32(row.Cells("IdEstudio").Value) _
                                                Join dd In empleado.Estudios On idEstudio Equals dd.IdEstudio _
                                                Select row).ToList()


       'rows.ForEach(Function(o) o.Cells("Seleccion").Value = True)
       rows.ForEach(Function(o) InlineAssignHelper(o.Cells("Seleccion").Value, True))

End Sub

La asignación de la imagen es muy simple y similar a como se trabajo en el DataGridview, solo se convierte el array de byte recuperado de la tabla en la base de datos y se carga directo en el PictureBox.

Algo interesante a remarcar es la forma en como se carga la lista de Estudios, en esta se usan dos consultas separadas, por un lado la primera que carga todos los estudios existentes, si se ingresara en modo de alta de empleado solo esta lista seria cargada, pero si se esta editando se recupera la lista de estudios asignados a ese empleado en particular y por medio de una consulta linq (método AsignarEstudios) se realiza la unión entres ambas listas aquellas filas del DataGridView que coincidan serán marcadas porque son las que el empleado tiene registradas en la tabla.

 

Búsqueda de una imagen (cargar archivo a un PictureBox)


Un punto que debería ser menor pero que he visto con mucha dificultad en preguntas en los foros, es como cargar seleccionar un archivo y cargarlo en un PictureBox?

El código es muy simple pero suele generar problemas.

[C#]

private void btnBuscarImagen_Click(object sender, EventArgs e)
{
    OpenFileDialog fileDialog = new OpenFileDialog();
    fileDialog.Filter = "Archivo JPG|*.jpg";

    if (fileDialog.ShowDialog() == DialogResult.OK)
    {
        picImagenEmpleado.Image = Image.FromFile(fileDialog.FileName);
    }
}

[VB.NET]

Private Sub btnBuscarImagen_Click(ByVal sender As Object, ByVal e As EventArgs)

    Dim fileDialog As New OpenFileDialog()
    fileDialog.Filter = "Archivo JPG|*.jpg"

    If fileDialog.ShowDialog() = DialogResult.OK Then
        picImagenEmpleado.Image = Image.FromFile(fileDialog.FileName)
    End If

End Sub

Persistir la edición


La operación de persistir los datos o guardarlos requiere dos etapas, una implica tomar la información de los controles y generar los objetos de dominio que la capa de datos requiere para llevar a cabo la actualización en la base de datos, la segunda será simplemente procesar la entidad generando los INSERT y UPDATE necesario.

[C#]

private void btnGuardar_Click(object sender, EventArgs e)
{
    //
    // Se crea la entidad
    //
    EmpleadoEntity empleado = new EmpleadoEntity() 
    { 
        IdEmpleado = _idEmpleado.GetValueOrDefault(),
        Nombre = txtNombre.Text,
        Apellido= txtApellido.Text,
        FechaNacimiento = dtpFechaNacimiento.Value,
        EstadoCivil = Convert.ToInt16(cbEstadoCivil.SelectedValue),
        Imagen = ImageHelper.ImageToByteArray(picImagenEmpleado.Image)
    };

    //
    // Se asignan los estudios seleccionados, se unicializa la lista 
    // para cargar la selecciond el usuario
    //
    empleado.Estudios = new List<EstudioEntity>();

    IEnumerable<DataGridViewRow> rowsSelected = dgvEstudios.Rows.Cast<DataGridViewRow>()
                                                                .Where(o => Convert.ToBoolean(o.Cells["Seleccion"].Value));

    foreach (DataGridViewRow row in rowsSelected)
    {
        EstudioEntity estudio = new EstudioEntity()
        {
            IdEstudio = Convert.ToInt32(row.Cells["IdEstudio"].Value)
        };
        
        empleado.Estudios.Add(estudio);
    }

    //
    // se graba 
    //
    EmpleadosDAL.Save(empleado);

    this.DialogResult = DialogResult.OK;
    this.Close();
}

[VB.NET]

Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
	'
	' Se crea la entidad
	'
       Dim empleado As New EmpleadoEntity() With { _
                                                  .IdEmpleado = _idEmpleado.GetValueOrDefault(), _
                                                  .Nombre = txtNombre.Text, _
                                                  .Apellido = txtApellido.Text, _
                                                  .FechaNacimiento = dtpFechaNacimiento.Value, _
                                                  .EstadoCivil = Convert.ToInt16(cbEstadoCivil.SelectedValue), _
                                                  .Imagen = ImageHelper.ImageToByteArray(picImagenEmpleado.Image) _
                                                 }

	'
	' Se asignan los estudios seleccionados, se unicializa la lista 
	' para cargar la selecciond el usuario
	'
	empleado.Estudios = New List(Of EstudioEntity)()

       Dim rowsSelected As IEnumerable(Of DataGridViewRow) = dgvEstudios.Rows.Cast(Of DataGridViewRow)().Where(Function(o) Convert.ToBoolean(o.Cells("Seleccion").Value))

       For Each row As DataGridViewRow In rowsSelected

           Dim estudio As New EstudioEntity() With { _
                                                    .IdEstudio = Convert.ToInt32(row.Cells("IdEstudio").Value) _
                                                   }

           empleado.Estudios.Add(estudio)

       Next

	'
	' se graba 
	'
	EmpleadosDAL.Save(empleado)

	Me.DialogResult = DialogResult.OK
       Me.Close()

End Sub

Es preciso remarcar como en la entidad “Empleado” es cargada la lista de Estudios que este posee y solo una entidad es enviada desde la Presentación a la capa de datos. Durante esta operación también se toma la imagen del PictureBox y se convierte en un array de byte.

La actualización realiza varias operaciones las cuales son muy extensas en código, por lo tanto no será puesta directa en el articulo, pero si podrán verse en el código de ejemplo que puede descargarse.

 

Código de Ejemplo


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

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

 

[C#] 
[VB.NET] 

158 comentarios:

  1. hola leandro soy nuevo en tu blog, me llamo luis soy del hermano país de Perú, tenia un par de consultitas, la primera es concerniente a los escenarios desconectados y conectados, veo que tu utilizas escenarios conectados en tus ejemplos, segun la msdn recomiendan usar modo conectado, me podrias explicar un poco mas al respecto pero basado en tu experiencia..., la segunda duda es que veo que vos utilizas metodos estaticos, o shared, la verdad yo no estaba acostumbrado a usar metodos estaticos, me podrias explicar un poco alas ventajas y desventas de usar metodos estaticos y shared contra la forma tradicional en cuanto a rendimiento performance, etc.. muchas grcias espero puedas aclarar mis dudas y mucho exitos ...

    ResponderEliminar
  2. perdon quise decir que la msdn recomienda usar escenarios desconectados, me confudí....

    ResponderEliminar
  3. hola luis fernando

    En realidad estos ejemplos son todos escenarios desconectados, si analizas bien veras que se usa el reader solo lo suficiente para cargar al entidad, pero solo eso, despues se continua con la entidad en memoria ya cargada, el reader se desconecto una vez terminada esta operacion.

    Por lo tanto este tambien es un escenario desconectado, lo que pasa es que seguro asocies al reader con conectado y al dataset con no conectado, pero en realidad esto se refiere a que el reader permenece vivo durante toda la transaccion, en cambio aqui no solo el tiempo justo para cargar una entidad.

    El tema de los metodo estaticos se debe a que tanto al capa de negocio, como al persistencia, implementan funcionalidad al estilo de proceso, no aplican conceptos de herencia o sobrecarga de operadores ni nada de lo orientado a objetos, solo procesan las entidades. Recuerda que donde se usa static (shared) son los metodo de las capas y solo alli.

    La capa de negocio seria se denomina tambien capa de Servicio, y esta implementa el patron Facade, por lo tanto al ser la entrada al sistema el usarlo static (shared) reduce el tener que crear la instancia por cada clase usada, ademas seria muy raro que apliques logica de POO sobre estos metodos.

    saludos

    ResponderEliminar
  4. hola muchas gracias leandro, me aclaraste mi duda, teremendamente, ahor te voy a ser otra pregunta, espero no incomodar, siempre en aplciaciones para windows y en aplicaciones web en mi experiencia, grababamos las imagenes, archivos o imagenes , solamente su ruta, mas no lo grababamos como tipo de datos image, usabamos varchar(255), ahora cuales son las ventajas de grabar el binario o la ruta, en temas de rendimiento, performance, bye y ojala y exitos...

    ResponderEliminar
  5. Hola leandro, gracias por responderme, muchas gracias, te quiero hacer una nueva pregunta, siempre veo en tus ejemplos ue usas sentencias SQL embebidas y parametrizadas, no crees ue es mejor usar Stored Procedure, o lo haces asi por son post de ejemplos y para un proyecto real definitavemente se tendria ue usar SP, cual es tu opinion concerniente a los Stored Procedure, VENTAJAS, desvntajas, tu los sueles usar en tus proeyctos reales, muchas gracias y exitos..

    ResponderEliminar
  6. Hola Leandro. Una vez más gracias por la información publicada. Me gustaría que me dieras una sugerencia, estoy conectada a un servidor por ssh y se demora un poco subiendo cada imagen. Te echo el cuento, el sistema hace 4 procesos,escanea (1 o varias imagenes), realiza unos calculo, imprime y guarda, la cosa es que si hago el proceso secuencial el usuario espera mucho por cada imagen. Ahorita, estoy escaneando, luego los cálculos, guardo una info, luego imprimo y simultáneamente guardo la imagen asociado a los datos que guarde previamente, el problema es que cuando son muchas imágenes me quedan registros sin las mismas, a veces completa la impresión y a veces no. Alguna idea? Como hago para trabajar con varias conexiones a la vez? Gracias!

    ResponderEliminar
  7. hola Pierina Joplin

    Si es un tema de conectividad lo primero que se podria evaluar es de mejorarla, o sea si esto es una tarea critica para el trabajo operativo se pdoria pedir que la conexion seguro aumente el ancho de tranferencia, porque sino entendi mal esta conexion seguro tiene poca tasa de tranferencia.

    Otra tecnica podria que ser realicer tareas en batch de forma desatendida, o seas separa las tareas, que necesitas hacer en el momento, y deja otras para que se registren pero se completen mas tarde, por ejemplo si el tema de la tranferencia es critico, podrias dejar encoladas los registros en una db, y que un proceso posterior, quizas nocturno, realice las transferencias por el canal seguro, pero el registro lo haces local en una db, y el proceso nocturno podrias ser un Servicio de Windows que se ejecute solo a cierta hora

    saludos

    ResponderEliminar
  8. hola lyc1903

    siempre en aplciaciones para windows y en aplicaciones web en mi experiencia, grababamos las imagenes, archivos o imagenes , solamente su ruta, mas no lo grababamos como tipo de datos image..

    en realidad la decision depende mas que nada de que tan critica es la imagen, si se trata de la foto de un empleado, por ahi una tecnica u otra podria ser indistinta.

    Pero que sucede si la imagen es un contrato legal, o el plano de alguna pieza de produccion, ahi la cosa cambia porque la imagen es un dato critico para la entidad, alli es cuando dejar la imagen suelta en un directorio puedo no ser una opcion y se registra esta directo en un campo de la db.

    En Sql Server 2008 se introduce una opcion que podria ser util, el FILESTREAM

    saludos

    ResponderEliminar
  9. HOla una pregunta, estoy validando un datagridview mi problema es el siguiente, cuando doy clic en agregar (productosBindingSource.AddNew()
    me agrega la fila, pero digamos que en el caso que seleccione otra fila que tengo solo como lectura, se me cierra la que se agrego!!! Gracias Saludos...

    ResponderEliminar
  10. hola Edgard Geovanny

    Esta algo generico el planteo de la pregunta como para dar una respuesta de que puede estar pasando.

    Entiendo que si has puesto AddNew, has activado la accion para dar de alta un nuevo registro, el salir de este sin haber aplciado el Update() del registro para persistirlo en la tabla imagino hace que esta accion se cancele.

    Igual no estoy del todo seguro, puede que esta sea la accion predeterminada al dar de alta un registro, o sea si quitas el foco del registro la accion se cancela.

    Por ahi deberias hacer el planteo en el foro de MSDN

    Foro VB.NET

    Alli podrias plantear la pregunta con mas detalle, quizas la siga respondiendo yo, o sino alguien mas podria dar una mano.

    saludos

    ResponderEliminar
  11. Hola, esto funciona cuando almacena en un campo image; pero cuando almacenas el path de donde esta la imagen cual seria el procedimiento para cargar la imagen en el CR si realizas el select y lo almacenas en un archivo *.html y de ahi le llamas a CR me explico? quiza sea claro.

    ResponderEliminar
  12. hola JOSÉ ANTONIO

    Lo que no me quedo claro es el tema de html, no sera quizas aspx a lo que hacias referencia?

    Mas alla que sea un proyecto web o winforms lo que vas a necesitar hacer es agregar a ese datatable un campo del tipo byte array y cargar la imagen alli usando de base el nombre de la imagen

    Seguramente debas, despues de hacer la consulta a la tabla, recorrer los registros del datatable, del dataset tipado, y buscar cada imagen para colocarla en ese campo byte[]
    para esto usarias el metodo:

    File.ReadAllByte


    Por cada row del datatable armas la ruta al archivo, recuperas el byte array del mismo y los cargas, para al final tener las imagenes y enviarlas al reporte.

    saludos

    ResponderEliminar
  13. Hola leandro, muchas gracias por el codigo que pones a disposicion, te comento que me surge un Qeubrantamiento de Cabeza, ahora te contare mi problema, no tengo mucha experiencia en base de datos ni en Modelamiento, asi es que espero no te aburran mis pregunta novatas.

    mi duda es con respecto a la relacion de las tablas EMPLEADO Y EmpleadosEstudios, TENGO ENTENDIDO QUE LA TABLA: "EmpleadosEstudios" ES UNA TABLA DETALLE, HASTA AHI BIEN, PERO AHORA COMO REGISTRAR DESDE MEDIANTE UN INSERT INTO UN REGISTRO DE EMPLEADO CON SU PRIMARIA, SECUNDARIA, Y UNIVERSIDAD, ESD DECIR ESTE EMPLEADO TIENE ESOS 3 NIVELES DE ESTUDIO A LA VEZ, ESA ES MI DUDA COMO REGISTRAR LA TABLA DETALLE, PODRIAS AUYDARME DANDOME EL QUERY DE COMO INSERTAR UN EMPLEADO CON ESOS ESTUDIOS??, POR FAVOR, YA LLEVO UN PAR DE DIAS CON ESTE PROBLEMA...

    GRACIAS POR TU TIEMPO...!!

    ResponderEliminar
  14. hola Johnny

    Primeramente un punto para corregir, la tabla "EmpleadosEstudios", no es una tabla de detalle, es una tabla que permite definir una relacion mucho a mucho

    Esto tinee que ver con el Modelo entidad-relación

    Bien aclarado ese punto, para insertar en la tabla es simple solo creas una query del tipo

    INSERT INTO EmpleadosEstudios (IdEmpleado, IdEstudio) VALUES (@isempleado, @idestudio)

    con esa simple consulta ahora lo que haces es pasar los parametros
    El empleado sera siempre fijo, este no varia porque es el que editas y los estudios los insertas en un loop uno por uno, o sea si tienes 3 estudio para ese empleado ejecutaras este mismo INSERT 3 veces, uno por cada id de estudio que tengas

    Igualmente una aclaracion, esto es si el empleado se registrs la primera vez porque no tendra ningun estudio asignado, pero que sucede si es una modificacion y de 3 ahora solo le marcas 1 estudio
    Bien en ese caso lo que simpre ahacer priemro es un DELETE

    DELECT From EmpleadosEstudios WHERE IdEmpleado = @idEmpleado

    Este delete lo haces siempre y luego vuelcas la info del los estudio, de esta forma evitas hacer un marge completo de los datos existentwes con la nuevas modificaciones
    O sea harias siempre asignaciones con el insert

    bueno, espero hay quedado claro

    saludos

    ResponderEliminar
  15. Hola,se que esto puede sonar algo tonto :S , pero aparte de esta manera:

    Dim rowsSelected As IEnumerable(Of DataGridViewRow) = dgvEstudios.Rows.Cast(Of DataGridViewRow)().Where(Function(o) Convert.ToBoolean(o.Cells("Seleccion").Value))

    No hay alguna otra manera de recuperar los estudios seleccionados en el grid?? Lo pregunto porque aquí veo algunas cosas que me resultan completamente nuevas :( y no se si también me podrías indicar que conceptos has aplicado??. Saludos y Gracias ;)

    ResponderEliminar
  16. hola Alexandra

    Como haber siempre hay otras formas de hacer algo

    pero si en este caso se dipone de .net 3.5 porque no aprovechar todas su potencia usando una expresion lambda para filtrar la seleccion

    Lambda Expressions (Visual Basic)


    La alternativa a esa linea de codigo serian muchas linea para implementar un foreach por cada row evaluando cuales estan marcadas y pasandolas a una lista auxilia,
    pero es una linea como impliquemente aqui contra por lo menos 4 o mas del for.

    saludos

    ResponderEliminar
  17. Hola una consulta, a que se debe que cuando intento registrar un nuevo empleado o editar uno existente obtengo este error :"MSDTC NO esta disponible en el servidor"??

    Gracias de antemano :D

    ResponderEliminar
  18. hola Edalo

    MSDTC NO esta disponible en el servidor

    Ese error tiene que bver con el uso de transacciones, hay un servicio que requiere estar habilitado para que funcione, prueba con el comentario del link siguiendo los pasos a ver que sucede


    saludos

    ResponderEliminar
  19. Estimado Leandro como siempre muy buenos tus post, queria consultarte algo que para mi es nuevo, almomento de declarar la variable del codigo del empleado lo haces asi.
    int? _idEmpleado
    Para que es el signo de interrogacion?

    Luego usas el
    _idEmpleado.HasValue y el _idEmpleado.GetValueOrDefault().

    no se si puedes por favor explicar que hace esto???
    Saludos

    ResponderEliminar
  20. hola Fernando

    el ? despues de un tipo de dato lo define como nulable

    o sea puede asignar un null a esa variable, cuando no sabes que dato tendra

    Tipos que aceptan valores NULL (Guía de programación de C#)

    saludos

    ResponderEliminar
  21. Hola Leandro, estoy comenzando a programar con C# y me está costando un poco el tema de las listas. Para lo cual bajé tu código donde declaras la clase EmpleadoEntity, pero me da el siguiente error al compilar:

    "DataAccess.Entidades.EmpleadoEntity.IdEmpleado.get' debe declarar un cuerpo porque no está marcada como abstract o extern"

    ¿esto puede ser porque estoy usando Visual Studio 2005?

    Desde ya agradezco tu predisposición y tu tiempo.

    PD1: No encontré que postearas sobre listas, pero si tienes algo (a parte de MSDN) te agradezco.

    PD2: Estoy renegando con listas porque tengo una pantalla con muchas combinaciones posibles de filtro y una lista es la única forma que se me ocurre para pasar los parámetros y sus valores opcionalmente. Ya que los arrays son de dimensión fija.

    ResponderEliminar
  22. hola Andrés

    el tema con el error es que el ejemplo que has descargado esta desarrollado con VS2008 y .net 3.5
    por lo tanto la definicion de las propiedades que ves alli tienes la variable auto implementada

    si usas VS2005, o sea .net 2.0 debes declarar al variable privada que usar la propiedad

    private string nombre;

    public string Nombre
    {
    get { return nombre; }
    set { nombre= value; }
    }

    como veras en tu caso asi es como debes declarar todas las propiedades, porque la forma que yo lo uso es algo particular de .net 3.5


    con respecto a las listas, no has evaluado usar listas genericas

    Clase genérica List


    saludos

    ResponderEliminar
  23. Saludos desde Venezuela! no ha pensado en hacer un VIDEO TUTORIAL de esta entrada?

    ResponderEliminar
  24. hola Rafa

    la verdad no me lo habia planteado, igual este tema en particular apunta a mostrar un ejemplo implementado, no es que explica alguna caracteristica de programacion concreta, es muy general el tema para un video

    saludos

    ResponderEliminar
  25. Hola nuevamente Leandro, cómo estás?
    Te comento que estoy trabajando con listas en C#, pero me surge una duda cuando tengo que grabar en BD esta información.
    Por ahora lo hago de la siguiente forma, pero que hay una forma más eficiente con un RecordSet.


    foreach (Parametro prime in Lista.Parametros)
    {
    SqlCommand Comando = new SqlCommand(prime.campo, cnn);
    Comando.CommandType = CommandType.StoredProcedure;

    if (prime.campo == "Insertar")
    Comando.Parameters.AddWithValue("@Descripcion", prime.valor);

    Comando.ExecuteNonQuery();
    }

    Me parece que no está bueno crear un comando por cada elemento de la lista. ¿a vos qué te parece?

    Gracias de antemano!

    ResponderEliminar
  26. Ya que estamos, voy con otra duda que tengo con un DataGridView. Porque necesito poder seleccionar las filas y columnas completas.

    Con la propiedad "SelectionMode = RowHeaderSelect" puedo seleccionar solo las filas en ejecución y con "RowHeaderSelect" puedo seleccionar solo las columnas, pero no tengo un valor para ambos.

    También probé con código como el siguiente para las filas y funciona..
    grid.CurrentRow.Selected = true;

    Pero para las columnas no encuentro la forma. Probé con..
    gridPrecios.Columns[gridPrecios.CurrentCell.ColumnIndex].Selected = true;

    Pero no le encuentro la vuelta al asunto todavia.

    Aclaro que no pretendo seleccionar las filas y columnas completas a la misma vez (lo que sería toda la grilla). Necesito poder seleccionar toda una fila o toda una columna en tiempo de ejecución. Porque también estoy usando la grilla para editar los valores de las celdas por otro lado.

    Espero que se entienda la pregunta.

    Gracias de nuevo!

    ResponderEliminar
  27. hola Andres

    podria ahcer algo como esto

    SqlCommand Comando = new SqlCommand(prime.campo, cnn);
    Comando.CommandType = CommandType.StoredProcedure;

    foreach (Parametro prime in Lista.Parametros)
    {
    Comando.Parameters.Clear();

    if (prime.campo == "Insertar")
    Comando.Parameters.AddWithValue("@Descripcion", prime.valor);

    Comando.ExecuteNonQuery();
    }

    ResponderEliminar
  28. hola Andres

    bien la verdad con respecto al SelectionMode del DataGridView, no creo que puedas implementar esta funcionalidad

    entiendo lo que quieres hacer, pero lavd con este control nucna he visto que se pueda.

    igualmente me parece algo raro este tipo de seleccion porque lo normal es resaltarle al usuario aquella columna o fila que le interece como dato

    despues dedee codigo ya no afecta porque puede tomar el CurrentRow, o CurrentCell y con este ultimo por ejemplo sabes la fila con el RowIndex, y la columna con el ColumnIndex

    con lo cual con la info puede operar sin problemas, mas alla del resaltado visual

    saludos

    ResponderEliminar
  29. hola leandro muuy buen blog
    tengo una duda se puede aplicar todo esto para una base de datos en access 2003??
    la duda es por que veo que utilizas transactionScope y esta funcion no se aplica para access.. entonces la duda es:
    cual metodo(si lo hay) es parecido al transaction scope que se pueda aplicar en access??

    ResponderEliminar
  30. Hola Ruben

    Con respecto a la edicion del empleado se podria implememntar sin problemas en Access, solo que ingresar la imagen dentro de la db no seria muy recomendable, porque access puede degradar la performance.

    Las transacciones es obvio que TransactionScope no aplica, vas a necesitar usar el OleDbTransaction

    entonces iniciar y cerrar la trasnaccion, la macana con esto es que ya deja de ser transparente cuando programas en capas, porque debes pasar la conexion en cada invocacion si la logica pega en varias entidades.

    saludos

    ResponderEliminar
  31. hola de nuevo Leandreo muy util tu ejemplo pero tengo una duda
    si yo heredo la plantilla de un formulario (es la que utilizo)
    cuando hagoi clic en mi grilla este abre supuesta mente el formulario pero este formulario aparece en blanco, es decir no carga ninguno de los controles que le coloque(los textbox) por que sucede eso?

    ResponderEliminar
  32. Hola Leandro soy nuevo por aquí exelente tu blog, estoy desarrollando una aplicación parecida, mi consulta es que al traer los datos en una grilla de una tabla en particular quiero mostrar la descripción de los id asignados, es decir por ejemplo tengo una tabla hospitales hago un reader de la misma que tiene un id_provincia y id_region que hacen referencia a sus respectivas tablas y quiero que muestre la descripción de la provincia y región.
    Si bien al crear la consulta en la capa "AccesoDatos" utilizo un inner join para combinar las tablas pero al llamar al método y mostrarlo en la grilla no me muestra los datos de esos campos.
    Desde ya gracias...

    ResponderEliminar
  33. hola Ruben

    puede suceder por varias cosas, pero una que se me ocurre es que valides el codigo del Designer.cs asociado al formulario, alli deberia estar el codigo que diseña el form con la ubicacion de cada control que lo conforma

    tambien podrias probar de crear un for nuevo y validar si sucede lo mismo

    valida ademas que la aplicacion compila sin errores


    saludos

    ResponderEliminar
  34. hola Augusto

    la capa de accso a dato con que tipo de entidades trabaja ?

    porque si lo haces con dataset tipados puede que la query devuelva los 4 campos, o sea los 2 ids y las 2 descripciones
    pero si solo sabe mapear dos porque son solo las dos columans del id que tiene definidas las otras las descarta

    o sea deberia actualizar el modelo o crear una entidad nueva que tenga estos 4 campos paera que la query con el join devuelva los datos pero la entidad tiene wue saber mapearse

    si usas clases creadas por ti deberias en el while del reader asignar las propiedades al objeto, asi como ahra los haces con las dos propiedades ids, debes agregar las de las descripciones

    saludos

    ResponderEliminar
  35. Hola Leandro

    E modificado la entidad con la que trabajo en la capa datos agregándole las descripciones y ahora me funciona como esperaba muchas gracias por la ayuda.

    ResponderEliminar
  36. Que tal Leandro, estoy utilizando vb2005, como grabo la imagen en la bd?, no existe la propiedad imagehelpe.imageToByteArray(imagen), al tratar de introducir el parametro me sale el error "Error al convertir el valor de bitmap a byte()"; te cuento que utilizo la siguiente sintaxis: cmd.Parameters.add(new sqlparameters("@foto").sqlType.VarBinary)
    cmd.parameters("@foto").value = me.pixturebox1.image; si me podrias dar una mano.

    ResponderEliminar
  37. hola jorge

    El tema es que no puede hacer esto
    cmd.parameters("@foto").value = me.pixturebox1.image

    porque no es valido, elpetro espera un array de byte, y tu no se lo estas dando por eso falla

    si analiza el codigo del ejemplo del articulo, veras una clase de nombre ImageHelper
    esta es la que contiene las funciones de conversion de Image a un array de byte que requieres para asignar el parametro

    saludos

    ResponderEliminar
  38. Correcto Leandro lo revise y solucione el tema, gracias. Te sigo!!

    ResponderEliminar
  39. hola gracias por tu respuesta Leandro pero tengo una consulta mas sobre esto

    he seguido el ejemplo del link y todo funciona bien excepto que tengo un problema he aplicado el mismo codigo en otra grilla (claro que para esta aplico un objeto diferente, una clase diferente) y no me llena la grilla cuando corro el programa, ademas como me explicaste he aplicado la propiedad DataPropertyName para cada columa (este valo es el mismo que aplico en las propiedades del objeto)

    lo que me extraña que tenga varias grillas y algunas las llena y otras no, el problema no son los querys ya que los he probado y todos me devuelven los valores que quiero ademas no creo que sea la clase o el llamada a esta porque esta es una rutina y como te menciono en algunas grillas devuelve el valor y en otras no?

    ademas he comprobado los DataPropertyName de las grillas y si coinciden con los propiedades del objeto que quiero recuperar que crees que puede ser el error?

    ResponderEliminar
  40. hola Ruben

    la verdad bastante raro el problema que describes, porque que en algunas grid funcione y en otras no, no tendria mucha logica, o sea funciona o no funciona si es que en todas aplcias la misma tecnica para cargar la imagen

    has validado que la propiedad AutoGenerateColumns lo pongas en false

    prueba de asignar a las tablas con problemas los datos d las grid que cargan correctamente, para validar si es algo de datos el tema

    saludos

    ResponderEliminar
  41. amigo nesecito su ayuda para mostrar una serie de imagenes que tengo en la base de datos pero en vez de un datagriview es un listview quiero mostrar las imagenes en formato de vista previa

    ResponderEliminar
  42. hola Ali

    el tema es que si es un listview deberias agregar estas imagenes en un ImageList para luego vincular con el listview

    http://social.msdn.microsoft.com/Forums/es/vbes/thread/aadfe755-680f-4870-ad91-d60ddda2e56b

    puedes agregar imagenes en timepo de ejecucion recuperando estas desde la db

    saludos

    ResponderEliminar
  43. Hola Leandro tengo un problemita al querer visualizar la imagen por default en un picturebox cuando quiero hacer una nueva entrada de un registro me da este error "Value of 'null' is not valid for 'stream'." previamente llamo en en load del form al método para que cargue la imagen por default al control creo que no encuentra la ruta de acceso si no me equivoco no se si sera por algún error de referencia o algo que me este faltando desde ya gracias.

    ResponderEliminar
  44. hola Augusto

    me suena mas a un problema de la logica de codigo que aplicas

    sera que la imagen que recuperas esta en null ?

    pueden ser miles los problema que geenren este error, la unica forma de solucionarlo es poniendo un breakpoint y validando que valores toma la asignacion que haces el picturebox

    saludos

    ResponderEliminar
  45. Hola Leandro te agradezco por tu códigos me han servido mucho exitos y bendiciones en tu vida

    ResponderEliminar
  46. una pregunta como puedo convertir esto
    rowDatosCliente.DNI = pictureBox1.Image

    ya que al intentar cargar de un picturebox.Image meda un error de tipo Byte[]
    he intentado con system.byte[picturebox.image] y con convert.______ y no e podido. para mi seria un honor que me puedas ayudar. gracias

    ResponderEliminar
  47. hola luis

    pero no veo mucha compatibilidad entre esos campos, un DNI que sea una imagen es algo raro, el dni no es un campo numerico ?
    como vas a poner una imagen alli

    ademas si revisas el ejemplo veras que hay un clase helper que permite convertir el Image en byte[], deberias usarla

    saludos

    ResponderEliminar
  48. es que es rowDatosCliente.foto = pictureBox1.Image
    es para cargar la foto en se encuentra cargada en el picturebox

    ResponderEliminar
  49. es que este metodo me funciona mejor
    dtCompra.DatosClienteRow rowDatosCliente = facturacion.DatosCliente.NewDatosClienteRow();
    rowDatosCliente.Nombre=Convert.ToString*txtNombre.Text);
    rowDatosCliente.cantidad =Convert.toint32(txtcantidad.Text);
    rowDatosCliente.precio=Convert.toint32(txtprecio.Text);
    rowDatosCliente.total=Convert.toint32(txttotal.Text);
    rowDatosCliente.codigo_barra=pictureBox1.Image;
    la imagen es un codigo de barra que se genera automaticamente.
    el error: es(
    No se puede convertir implicitamente el tipo 'System.Drawing.Image'en'byte[]')

    ResponderEliminar
  50. hola luis

    bien, pero el Image de un Picturebox no es compatible con un campo de la db, deberias convertir este a un byte array

    si analizas el articulo veras que hay una clase helper para esto, lo has evaluado?

    saludos

    ResponderEliminar
  51. Saludos leandro
    es que estoy cargando directo del formulario al crystal report. pero para guardarlas la cargue en un memorystream llamado ms esto sera lo que boy a convertir. puedo enviarle el proyecto para que lo vea.

    ResponderEliminar
  52. Saludos leandro
    ya resolvi el problema te lo muestro
    por si alguien tiene el mismo problema aki esta la solucion

    System.IO.MemoryStream ms = new System.IO.MemoryStream();
    pictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
    PurificadoraDataSet facturacion = new PurificadoraDataSet();

    PurificadoraDataSet.VentasRow rowventas = facturacion.Ventas.NewVentasRow();
    rowventas.cliente = Convert.ToString(txtcliente.Text);
    rowventas.cantidad = Convert.ToInt32(txtcantidad.Text);
    rowventas.precio = Convert.ToInt32(txtprecio.Text);
    rowventas.total = Convert.ToInt32(txttotal.Text);
    rowventas.ncf = Convert.ToString(txtncf.Text);
    rowventas.fecha = Convert.ToString(txtfecha.Text);
    rowventas.idfactura = Convert.ToInt32(txtidfactura.Text);
    --- rowventas.codigo_barra = ms.ToArray();-----

    mil gracias por tus aportes.

    ResponderEliminar
  53. Hola Leandro.

    Estoy desarrollando una aplicación en la que quiero mostrar varias imagenes pequeñas de diferentes tamaños para elegir una. Alturas entre 40 y 100px, anchuras entre 100 y 500px. La cantidad de imagenes a mostrar será entre 5 y 20 imagenes.

    Actualmente no usa base de datos pero en un futuro si tendrá esta opción.

    ¿Que control me recomiendas para mostrar las imagenes?

    Había pensado usar PictureBox generados dinámicamente, pero es posible que un listview o un DataGridView u otro sea mas recomendable.

    Saludos, enhorabuena por tu gran trabajo y gracias por la información.

    ResponderEliminar
  54. hola David

    o sea apuntas a algo como esto


    Image Thumbnail Viewer with .NET 2.0


    el tema que control estandar desde .net para este tipo de trabajo con imagenes no hay, salvo el ImageList pero no es para temas visuales sino para referenciar imagenes por medio de uan key en una coleccion

    saludos

    ResponderEliminar
  55. hola leandro, te molesto,,, por mas que intento, en la parte de codigo donde pones:

    grilla.AutoGenerateColumns=false;

    no consigo que entre al bucle debajo para recorrer la grilla sacarle el objeto, verificar su campo imagen y convertirla de arreglo de bytes a imagen para luego mostrarla en dicha grilla. Seteaste algo en la grilla en diseño o hay algun code que no estoy viendo bien?

    David

    ResponderEliminar
  56. hola David

    pero asignas el DataSource ? y puedes verificar que se generen registros ?

    o sea descarta el realizar el foreach por cada row y solo deja que los datos se muestren en el grid, puede ver filas? sino las visualizas alli esta el problema no hay info cargada por lo tanto tampoco se podra recorrer nada
    valida como recuperas los datos desde la db

    saludos

    ResponderEliminar
  57. Todo bien Leandro, mi error en:
    grilla.AutoGenerateColumns=false;

    era que cuando armo los encabezados de la grilla manualmente, no estaba seteando la propiedad:
    DataPropertyName=CampoClase

    Ahora funciona bien, pero, el unico error raro, salto con algo relacionado con el evento DataError de la grilla y se me borran todas las columnas de la misma. Estoy tratando de volver a generar el error asi doy detalles pero parece que desaparecio. Ja

    Saludos

    ResponderEliminar
  58. Leandro gracis por tu ayuda con esto, me podrias ayudar estoy haciendo un reporte en donde deben de salir 22 fotos que son los alumnos por cada aula, tendria que hacerlo colocando solo el path y que llame a la foto o como?

    desde ya gracias

    ResponderEliminar
  59. hola Orlando

    por reporte, te refieres a que usas crystal ? o sea seria algo como esto

    Crystal Reports – Cargar imagen usando una capa de reporte

    quizas en tu caso debas primero recorrer el datatable y recuperar als ruta de las imageens para poder asignar a un campo extra que definas en el datatable tipado el byte[] de la imagen que vas a visualizar

    saludos

    ResponderEliminar
  60. Hola Leandro.Soy Sandra

    Esta publicacion me ha aclarado muchas dudas sobre un proyecto que estoy realizando, sin embargo tengo una problema con el cual me gustaria que me ayudaras.

    Tengo que hacer un formulario de captura para productos, clientes, entre otros. Pero el problema está en el sistema tiene que crear automaticamente un codigo ya sea de producto, de cliente, etc, igualmente tengo que crear consultas parametrizadas de la forma: desde hasta, tanto con fechas como por direccion o nombre de articulo o por precio, etc.
    Por ejemplo: desde la a hasta la d

    Sou novata en esto y ahora estoy comenzando a aprender por lo que te agradeceria en el alma que me ayudaras con esto.

    ResponderEliminar
  61. hola Sandra

    con respecto al codigo del producto, imagino tienes un campo key que defines como identity en la tabla, no ? porque no puede ser ese el codigo
    o apuntas a un codigo diferentes algo alfanumerico? si es asi que este camo no se la key de la tabla que sea un campo adicional

    para las busquedas podrias aplicar este concepto

    Filtros Condicionales (1/2)

    como veras se filtra con una variedad de opciones

    saludos

    ResponderEliminar
  62. Gracias por el ejemplo de las búsquedas, es justamente lo que quería, pero sin embargo me sigue la duda respecto al código de registro, porque el problema es que definiendo el campo tipo identity como apuntas, el id que se genera es de tipo 1, 2, 3, etc. según el orden de los registros y yo los necesito que sean por ejemplo: 000001, 0002 o AS0005, AS002.

    Además lo que pasa es que aún no conozco la forma de que este código se cree de forma automático en sql y no que el usuario tenga que insertarlo en el momento en que se agrega el registro.

    Espero que entiendas mi pregunta porque creo que no me sé explicar bien.

    Muchas gracias, espero que me puedas ayudar.

    ResponderEliminar
  63. hola Sandra

    si la idea es usar 00001 eso es solo formato, podrias dejar que se cree secuancial con el identity y luego usas

    string id = string.Format("{0:0000}", 2);

    con eso obtienes un 0002, pero lo resuleves cuando lo muestras nada mas, en la db solo ser un 2

    ahora si es S002 la cosa es otra, recomendaria que alli tendras una clave compues de dos campo unao para la letra " y otro para el numero, no uses un solo campo, podrias suar el Max() para tomar el ultimo id he incrementar

    SELECT Max(id) FROM Tabla WHERE codigo = 'S'

    por supuesto la forma en que cambias de codigo eso el negocio imagino lo define

    saludos

    ResponderEliminar
  64. Buenas leandro muy buen blog primeramente te felicito porque me ha ayudado mucho.

    Bueno mi problema es el siguiente: tengo un form con un data grid view y un picture box. quiero que al seleccionar una fila dentro del datagridview me muestre la foto en el picture box. mi programa es de un almacen y quiero colocar fotos, me gustaria saber la manera mas sencilla de hacerlo. trabajo en vb.net y access 2007.
    espero su respuesta

    ResponderEliminar
  65. hola Leandro muy bueno todas tus publicaciones ,queria pedirte un fabor si podrias ayudarme en una coneccion :
    tengo un procedimiento almacenado REGISTRAR:
    ALTER PROCEDURE [dbo].[registrar]
    @tipocuenta int,
    @nom_usu varchar(50),
    @pass varchar(50),
    @pregunta varchar(50),
    @respuesta varchar (50),
    @email varchar(50)
    AS
    BEGIN

    if exists(select * from cuenta where cuenta.nombre_usuario=@nom_usu)
    begin
    raiserror('Nombre de usuario ya se encuentra registrado',11,1)
    end

    else
    begin
    insert into cuenta (nombre_usuario,contraseña,id_tipo,pregunta,respuesta,email)values(@nom_usu,@pass,@tipocuenta,@pregunta,@respuesta,@email)
    end
    END
    cual seria el codigo para que me muestre el raiserror en VB y C# en un label con el nombre "lblerror" o si podrias hacer un proyecto con eso,le agradeceria mucho su ayuda

    ResponderEliminar
  66. hola Leonel

    pero las fotos dondes estas ubicadas, dentro de la db, o en una carpeta ?

    porque si es en una carpeta podrias tomar el nombre del archivo y usar

    Picturebox1.Image = Image.FromFile(ruta)

    para mostrar la imagen
    saludos

    ResponderEliminar
  67. hola JHON

    podrias usar el try..catch y capturar el SqlException

    http://social.msdn.microsoft.com/Forums/es/vcses/thread/99982a71-627c-4fe3-870d-84a4c61739b3

    http://social.msdn.microsoft.com/Forums/es/vbes/thread/e36ae5e9-7b43-41d8-aeed-8dc877365cfe

    saludos

    ResponderEliminar
  68. Hola de Nuevo Leandro, con respeto a lo de las imágenes yo también tengo una pregunta.

    Como podría hacer para guardar una imagen en una carpeta, o sea, en el momento que inserto X registro a la base de datos buscar una imagen en el computador, asignarle un nombre relacionado con el registro que estoy creando y guardarla en otra carpeta especifica, esto seria para no sobrecargar la BD con imágenes.

    Eso es lo primero
    lo segundo es que estoy realizando un formulario de consulta con el siguiente código:

    private void comando()
    {

    conexion = new SqlConnection(cc);
    conexion.Open();
    cmd = new SqlCommand(consulta, conexion);
    SqlDataReader reader = cmd.ExecuteReader();

    if (reader.Read())
    {
    MessageBox.Show("Registro encontrado");
    reader.Close();
    SqlDataAdapter adap = new SqlDataAdapter(consulta, conexion);

    DataTable dtProductos = new DataTable();
    adap.Fill(dtProductos);


    this.dgvProductos.DataMember = "dtProductos";
    this.dgvProductos.DataSource = dtProductos;
    button1.Enabled=false;
    textBox1.ReadOnly = true;
    btnNuevaConsulta.Enabled = true;
    }
    else
    {
    MessageBox.Show("REGISTRO NO ENCONTRADO");
    reader.Close();
    }



    conexion.Close();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    //conexion = new SqlConnection(cc);
    try
    {
    if (cbCriterio.Text == "Codigo Porducto")
    {
    if (textBox1.Text.Length == 0)
    {
    MessageBox.Show("El textBox esta vacio");
    }
    else {
    consulta=(" SELECT * FROM productos WHERE IdProducto = '" + textBox1.Text + "'");
    comando();
    }

    Cuando lo ejecuto y realizo una primera consulta todo funciona perfecto, pero luego cuando quiero realizar otra sin tener que ejecutar el formulario me da el siguiente erro: "child list for field dtproductos cannot be created".

    Esto ha sido un verdadero problema y por mas que lo intento no encuentro en donde esta error.

    Te agradecería mucho como siempre que me ayudaras.

    Gracias Por Anticipado

    ResponderEliminar
  69. gracias Leandro por la atencion inmediata ,funcionó:
    ALTER PROCEDURE [dbo].[registrar]
    @tipocuenta int,
    @nom_usu varchar(50),
    @pass varchar(50),
    @pregunta varchar(50),
    @respuesta varchar (50),
    @email varchar(50)
    AS
    BEGIN
    IF exists(select * from cuenta where cuenta.nombre_usuario=@nom_usu)
    BEGIN
    RAISERROR('Ya existe una cuenta de usuario asociado al ID ingresado',11,1)
    END

    ELSE
    BEGIN
    insert into cuenta (nombre_usuario,contraseña,id_tipo,pregunta,respuesta,email)values(@nom_usu,@pass,@tipocuenta,@pregunta,@respuesta,@email)
    END
    END
    try
    {
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = cn;
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "registrar";
    cmd.Parameters.AddWithValue("@tipocuenta", 2004);
    cmd.Parameters.AddWithValue("@nom_usu",txtnomreg.Text);
    cmd.Parameters.AddWithValue("@pass", txtpass.Text);
    cmd.Parameters.AddWithValue("@pregunta",Cbopreguntareg.SelectedItem);
    cmd.Parameters.AddWithValue("@respuesta", txtrespuesta.Text);
    cmd.Parameters.AddWithValue("@email", txtemailreg.Text);

    cn.Open();
    cmd.ExecuteNonQuery();
    cn.Close();

    }
    catch (Exception ex)
    {
    lblveripass.Text = ex.Message ;
    }

    ResponderEliminar
  70. hola Sandra

    si la idea es guardar solo una cadena que hag referencia a la imagen recuerda solo hacerlo con el nombre, o una ruta relativa, no lo hagas con una ruta fisica completa, o sea mueve la imagen a una carpeta conocida, podrias suar el
    File.Copy()

    la idea es que la ruta real para acceder a la imagen la armes dinamicamente, podrias suar el Application.StartUpPath para saber la ruta de la aplicacion y en una carpeta de imagenes poner todo alli, esto si la aplicacion se ejecuta local, o sino configurar una ruta en el app.config

    despues guarda el nombre de la imagen es como cualquier otro parametro del tipo string, no hay diferencia

    saludos

    ResponderEliminar
  71. Intento como me explicas, pero no me funciona. Tienes algun ejemplo similar?

    Y sigo con el proble del error en el codigo que expuse arriba: "child list for field dtproductos cannot be created".

    Será que le falta algo al codigo???

    ResponderEliminar
  72. Hola Master Leandro,

    Últimamente he estado viendo tus artículos y me ha sorprendido el gran aporte que haces. Han servido de mucha ayuda.

    Te un inconveniente que me gustaría consultar.
    Es que necesito seleccionar de una BD un conjunto de artículos para editarlos, el problema está en que no sé cómo sacar uno de los campos de la tabla tipo imagen a un picture box.
    Como le asigno el valor de ese campo imagen de la tabla a un picture box para editarlo.

    Muchas Gracias

    ResponderEliminar
  73. hola Sandra

    este error que mencionas se produce cuando eejcutas desde el VS ? o es cuando haces el deploy

    has analizado el StackTrace del Exception para poder ubicar donde se produce


    saludos

    ResponderEliminar
  74. hola Jose

    pero es un desarrollo web el que estas realizando ? o es desktop

    porque sino debrias analizar

    [WinForms] Edición Empleados

    alli recuperao la imagen y la muestro en un picturebox

    saludos

    ResponderEliminar
  75. Saludos Leandro

    Gracias, con este ejemplo lo pude hacer, gracias.

    ResponderEliminar
  76. Hola denuevo Leandro


    el error se produce cuando ejecuto desde el VS, pero lo analice con el StackTrace como me dijiste y ya detecte donde esta el error.

    Gracias por la ayuda.

    ResponderEliminar
  77. Hola leandro muy bueno tu post, lo estoy implementando en un formulario de productos, pero resulta que tengo inconvenientes con aquellos productos que no tienen imagenes. Ejemplo cuando busco determinado producto y de los resultados hay dos que no tiene fotos el primer registro lo decuelve con foto no disponible y el segundo lo trae sin imagen.

    ResponderEliminar
  78. Hola Leandro, Me gustan mucho tus publicaciones.

    Necesito que me orentes en un inconveniente que se me ha presentado.

    Esto creando un formulario de captura de productos y la mayoria se insertan bien, pero los de tipo decimal o double no lo hacen de la manera correcta pues parece que se redondean automaticamente.

    Si cuando lo ejecuto introduzco por ejemplo un 5.89 en la base de datos se guarda un 6 y no lo guarda en forma decimal

    y si coloco un 1.45 en la bd aparece guardado un 1.

    No se por que pasa esto porque en el codigo de la captura lo especifico de esta forma:


    cmd.Parameters.Add("@precioCompra", System.Data.SqlDbType.Decimal);
    cmd.Parameters.Add("@utilidad ", System.Data.SqlDbType.Decimal);
    cmd.Parameters.Add("@inpuestoVenta", System.Data.SqlDbType.Decimal);



    y luego


    cmd.Parameters["@precioCompra"].Value = Convert.ToDecimal(txtPrecioCompra.Text);
    cmd.Parameters["@utilidad "].Value = Convert.ToDecimal(txtUtilidad.Text);
    cmd.Parameters["@inpuestoVenta"].Value = Convert.ToDecimal(txtImpuestoVenta.Text);

    Me prodrias deir que tengo que hacer para estos datos se capturen asi en forma decimal y noentero como lo estan haciendo?




    Gracias.



    ResponderEliminar
  79. hola andresfelipe

    si pones un breakpoint justo en el lugar donde evalua si el campo es dbnull o no
    puedes validar que este sea nulo? porque podria ser que no sea nulo, pero la imagen este corrupta o grabada en el registro de forma incorrecta, por eso al recuperarla la realice de form incorrecta

    podrias editar y probar asignar una nueva imagen, o dejar ese campo en nulo

    saludos

    ResponderEliminar
  80. hola Mario

    has validado la precision del campo en la tabla ? porque parece ser que se esta redondenando el valor

    si por ejemplo haces

    decimal precio =Convert.ToDecimal(txtPrecioCompra.Text);

    y pones un breakpoint alli para evaluar el valor de la conversion, puedea validar que alli no se redondeo y los decimales se conservan ?

    porque puede tambien ser un tema de cultura, valida que sea en-US para que toma el punto como decimal

    saludos

    ResponderEliminar
  81. Leandro, tengo dos registros en la base de datos el primero no tiene imagen, el segundo tiene imagen, depurando aparece tal cual esta en la bd.
    cundo carga los datos en el datagridview y selecciono el que no tiene imagen y adjunto la imagen correspondiente, edito y cuando luego consulto de nuevo no aparece la imagen que seleccione si no que aparece la imagen de no disponible. pero esto pasa con algunas imagenes por que con otras da perfectamente. por ultimo abusando de tu gentileza, por que la razon de no guardar la imagen del no disponible como recurso del ensablado y no como una foto de bd. Gracias

    ResponderEliminar
  82. Leandro como estas quisera saber como arrastar una imagen un documento word uno excel o un ppt a una grilla y gaudarlo en un base de datos en vb.net
    gracias

    ResponderEliminar
  83. Hola Leandro me llamo Julio, quisiera que me des un ejemplo de como cargar una imagen, un word , un excel, un archivo de outlook o un ppt a un gGrid en vb.net con base de datos myql osea que se guarde

    ResponderEliminar
  84. Hola Leandro

    Tenias Razon el problema era la precision del campo en la tabla.

    Muchas, Muchas Muchas Gracias!!!!!!

    ResponderEliminar
  85. hola andres

    la verdad esta bastante raro el problema, pareciera ser un problema en la logica de la aplicacion

    si en lugar de visualizar la imagen en el picturebox la grabas como archivo usando
    File.WriteAllBytes()

    puede ver que se recupera correctamente de la db?

    mas que nada para validar que en el registro se haya insertado la imagen que sabes seleccionaste para esa entidad

    saludos

    ResponderEliminar
  86. hola Julio

    lo de arrastrar la verdad no sabria decirte

    pero si defines alguna forma de selecciona usando el OpenfileDialog, podrias tomar se archivo he insertarlo en la db

    http://social.msdn.microsoft.com/Forums/es/vsgenerales/thread/410c7ce0-058e-4dfc-8a08-a4f3cb1726f0

    la tecnica para mysql es igual a cualquier otra db, si consigues el array de byte de la imagen puede asignarlo al parametro

    saludos

    ResponderEliminar
  87. Hola tutini este el blog esta muy bueno pero podrias hacer este mismo ejemplo pero usando Linq? es que soy nuevo y quiero aprender

    ResponderEliminar
  88. hola Michael

    cuando dices linq te refieres a Entity Framework ?

    saludos

    ResponderEliminar
  89. Me refiero al LINQ o Language INtegrated Query es una de las novedades más importantes de
    Microsoft .NET Framework 3.5 y que afecta por lo tanto a Visual Studio 2008 o versiones mayores.

    SALUDOS

    ResponderEliminar
  90. hola Michael

    el tema es que con linq basico no se puede implementar lo que aqui estoy explicando

    si se usa linq to sql, o entity Framework en ese caso si

    pero linq de por si no persiste contra una db

    saludos

    ResponderEliminar
  91. creo que debebi especificarme bien jejejeje era con linq to sql me gustaria ver un ejemplo de lo que hiciste usando linq to sql si puedes hacerlo estaria agradecido

    Saludos =)

    ResponderEliminar
  92. hola

    la verdad use casi nada linq sql ya que este es reemplazado por Entity Framework

    estaba pensado armar algunos ejemplos con EF, no habia pensado ponerle tratamiento de imagenes, pero podria hacerlo

    no has evaluado pasar a EF, es mas Code First esta genial

    saludos

    ResponderEliminar
  93. Si puedes armar un ejemplo con EF y con linq (aun que este obsoleto) seria genial me gustaria ver algo EF para ver como es

    Saludo =)

    ResponderEliminar
  94. hola

    entonces voy a ver si me hago un rato el finde para terminar el articulo asi lo publico

    saludos

    ResponderEliminar
  95. Hola Leandro, saludos
    Me ha surgido un problema que no he podido solucionar por más que lo intento.
    Lo que pasa es que cuando intento llamar un formulario desde otro cualquira que sea el formulario que intento abrir se cierra antes de haber iniciado completamente, por ejemplo :
    if (Nivel=="Administrador")
    {Form2 f2=new Form2();
    f2.Show(); }

    de esta manera llamo x formulario según el tipo de usuario que inicia el sistema, el problema es que automáticamente abre el nuevo formulario que estoy llamando, en un abrir y cerrar de ojos se cierra, cualquiera que sea el formulario que intento abrir desde el login y no sé porque pasa esto porque he revisado las propiedades del nuevo formulario y no encuentro el problema. Además también he revisado el código en el login y no ordeno a cerrar desde que abran en ningún sitio.

    Por qué podría estar pasando esto, me ayudas??

    Gracias

    ResponderEliminar
  96. hola Mario

    estas definiendo algun codigo en el form_Load ? o en el constructor del form

    que pasa si pones un breakpoint en el Load del form, ingresa la ejecucion alli ?

    esto solo sucede con ese form, o es con todos lo mismo

    saludos

    ResponderEliminar
  97. Hola Leandro,

    Esto me pasa en todos los formularios que llamo desde el form del login, he provado y parece que el problema esta en las sentencias close() porque cuando se las quito los form si cargan correctamente.

    ResponderEliminar
  98. hola Sandra

    pero implementas el login en el Main() ? porque si lo haces no necesitas de ningun close

    como explico aqui
    Login – Usando Password con Hash

    como veras el login es previo a inicirse cualquier form

    saludos

    ResponderEliminar
  99. Una vez mas, eres un contribuidor de los buenos Leandro , muchas gracias.

    ResponderEliminar
  100. Hola saludos estoy usando la base de datos nortwind y trato de crear un nuevo registro en la tabla Categories mediante un winform pero el problema esta en que esta base de datos tiene una cabecera de 78 bits que hay q considerar a la hora de hacer el registro puedo extraer los datos para mostrarlos alguna idea de como hacer el registro?

    este seria el codigo para recuperar los valos de la tabla categories

    byte[] foto = (byte[]) this.dataGridView1.Rows[e.RowIndex].Cells["Imagen"].Value;
    MemoryStream ms = new MemoryStream(foto, 78, foto.Length-78);
    picFoto.Image = Image.FromStream(ms);

    ResponderEliminar
  101. hola Alonso

    imagino esto que planteas se relaciona con la pregunta en el foro

    http://social.msdn.microsoft.com/Forums/es-ES/vcses/thread/92b6fbaa-9c44-4766-8417-7f06b8399bdc


    saludos

    ResponderEliminar
  102. Hola Leandro, hablando en 3 capas... porque se hace el diagrama de clases de un proyecto en base a las entidades?
    capa de datos, negocio, presentacion y entidades.. saludos

    ResponderEliminar
  103. hola Si Señor

    partamos que el diagrama de clases modela las clases de la aplicacion, por lo que cada capa se la podria ver como una clase, despues de todo eso es justamente lo que se usa en cada proyecto

    a su vez las entidades de negocio tambien son clases por lo que podria usarse el mismo diagrama

    en realidad lo correcto seria que las capas en si se definan en un diagram de despliegue, pero esto solo es util si se va a utilizar capas remotas o si van a estar fisicamente en distintos equipos,
    por lo general las capas con mas bien una separacion logica para dividir responsabilidads y mejorar el mantenimiento futuro

    saludos

    ResponderEliminar
  104. Leandro muy buenos dias muy interesante el codigo que nos muestras con esta solución. Fuera de este contexto tengo la siguiente pregunta como puedo enviar el foco hacia una ventana y qu esta ventana siempre la coloque hasta arriba sobre todas las ventanas

    ResponderEliminar
  105. Hola Leandro, entonces supongamos que tenemos las clases ProductoDAL, ProductoBLL, ProductoENT .. en el diagrama de clases se pondría un "Producto" y esa clase va a contener las propiedades de la entidad y los métodos de la clase DAL y la BLL o nada que ver?
    Es un ejemplo.. saludos

    ResponderEliminar
  106. hola Teckno

    podrias usar el metodo ShowDialog() para lanzar la ventana de forma modal y que quede por encima del resto, impidiendo acceder a las de abajo hasta que no la cierres

    otra seria usar la propiedad
    Form.TopMost Property

    y una tercera opcion seria usando las api de windows con el SetWindowPos

    saludos

    ResponderEliminar
  107. hola Si Señor

    en realidad cada una de esas clases actura de forma independente, implementando su responsabilidad
    quizas deberias analizar:

    [N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 3

    alli se explica mejor el tema de capas, si quieres antes analiza las primeras partes asi puede ver toda la evolucion

    saludos

    ResponderEliminar
  108. Hola leandro, buen tutorial, una pregunta, en lugar de usar codigo linq en asignarestudios(), se podria trabajarlo de otra manera creando algun store procedure o similares, sucede que no uso mucho linq, agradecere tu respuesta

    ResponderEliminar
  109. hola mel

    el tema es que alli estas uniendo un control grid contra los datos procedientes de una db, no se puede hacer desde una simple query porque no tendrias acceso al control para saber que item marcar

    se podria hacer dos foreach, se toma un item del datagridview y se recorre el datarable para ver si alguno coincide y si es asi se marca, luego se pasa el siguiente
    linq internamente hace algo como es, mucho mas optimo por supuesto, pero bueno sino te animas a linq seria la alternativa,
    aunque aconsejaria te animes, cuando entiendas linq puedo asegurarte que no lo dejas nunca mas

    101 LINQ Samples

    LINQ to Objects

    saludos

    ResponderEliminar
  110. Hola Leandro quiero llevar este ejemplo a web, pero en el caso del datagridview, seria similar al gridview?, lo digo por el codigo mas que nada puesto que mas he usado ha nivel windows y nesecito uno en web, no se si podrias darme una ayudita en esta parte, por favor, saludos

    ResponderEliminar
  111. Hola Sandra,

    para el problema de la carga del DataGridView: prueba a no informar la propiedad DataMember a ver si se resuelve tu problema.
    Si sólo le pasas una tabla en el DataSource no es necesario que indiques ningún valor a la propiedad DataMember.

    (Framework .NET 4.0)

    Saludos!
    Jesús.

    ResponderEliminar
  112. muy bueno el tutorial, en algun momento pasaras a hacer tutoriales con bases de datos de mysql?

    ResponderEliminar
  113. hola 4º secretariado C

    el tema es que conociendo ado.net la base de datos que se utilice no es importante ya que ado.net es igual para todas las db

    quizas cambien algunos detalles, como ser la definicion de los parametros en sql server usas "@" y en mysql usas "?"

    pero mas alla de eso el resto es practicamente igual

    estaba pensando armar articulos sobre ado.net que muestren como trabjar con Sql Server, Mysql y Oracle, tengo que ver como estructuro el tema

    saludos

    ResponderEliminar
  114. Gracias por este valioso aporte, me ha servido en gran manera la forma en como utilizas la clase ImageHelper.cs. Te agradesco y te mando saludo desde Nicaragua

    ResponderEliminar
  115. Hola Leandro gracias por el aporte pero tengo una gran duda, por ejemplo a mi me solicitan constantemente que muestre otros campos de los foreign keys, te hago un ejemplo:

    class Cliente{
    public string Nombre;
    public string Apellido;
    public int IdTipoCliente;
    }

    class TipoCliente{
    public int IdTipoCliente;
    public string TipoCliente;
    }

    Y desean ver una lista de clientes que muestre los campos:
    Nombre, Apellido, TipoCliente(el texto segun el id que es IdTipoCliente).

    ¿Esto en ASP.NET como podría listarlo en el gridview?
    Gracias.

    ResponderEliminar
  116. hola Bryan

    hay dos formas de implementar lo que planteas

    - aplanando la entidad y creando una clase que sea

    class Cliente{
    public string Nombre;
    public string Apellido;
    public int IdTipoCliente;
    public int TipoClienteDesc {get;set;}
    }

    entonces en la misma entidad traes el id y la descripcion de la entidad relacionada
    por supuesto a nivel de db esto lo resuleves con un INNER JOIN

    - la otra es relacionar las entidades, permitiendo una relacion de asociacion entre estas

    class Cliente{
    public string Nombre;
    public string Apellido;
    public int IdTipoCliente;
    public TipoCliente TipoCliente {get;set;}
    }

    como veras la clase Cliente tinee una propiedad del tipo TipoCliente, para cargar esta entidad podrias hacer

    n-Layer - SchoolManager - Herencia y navegación de entidades relacionadas (2/2)

    Nota: lo que si recomendaria es que uses propiedades y no variables publicas

    saludos

    ResponderEliminar
  117. Gracias Leandro, lo estaré viendo, una consulta sé que estos tips lo aprendiste con la experiencia pero crees que haya algún libro, manual o web, donde pueda leer sobre estas teorías que me faltan aprender, me gusta saber el porque y para que se debe usar así.
    Gracias nuevamente, un saludo cordial desde Perú.

    ResponderEliminar
  118. Leandro una consulta.. cuando agrego algo al ejemplo asi sea un label.. me escribe en el codigo handles a los botones y eso hace un error estoy en vb2008 con sql server express 08
    si le saco lo de handless se desabilitan los botones??? tendras alguna idea???

    ResponderEliminar
  119. hola Luis

    puede ser que sin querer estes realizando doble click en el control que agregas y esto hace que se cree el evento por defecto que tiene ese control

    o sea si al colocar un control cualquiera le haces doble click iras al codigo y se creara el evento por defecto

    aunque es raro que un label cree una asociacion con el evento de un boton porque no tienen nada que ver uno con otro

    si revisas los eventos, selecciona el control y presiona F4 para ver sus propiedades, despues ve al icono con forma de rayo amarillo y asi listar los eventos, puedes ver que se asocio de forma incorrecta el evento

    saludos

    ResponderEliminar
  120. Gracias, leandro ante todo quiero felicitarte por toda la info que tienes de .net eres de gran ayuda para los novatos (me incluyo) , volviendo al tema solo si hago doble click en los botones se desenlaza ya me acople al ejemplo de maravilla

    ResponderEliminar
  121. hola Luis

    entonces pudiste definir el evento?

    si desde el diseñador es con doble click en el control que genera el codigo del evento por defecto

    saludos

    ResponderEliminar
  122. Hola leandro una consulta, en este ejemplo estas usando solo 2 capas cierto?? la de datos y la de interfaz? o la helper viene siendo la 3ra (logica)?

    Me queda clara la de datos e interfaz pero helper no.

    Otra cosa leyendo dicen que las aplicaciones en capas van perdiendo rendimiento, es verdad?? te ha pasado??

    Gracias

    ResponderEliminar
  123. Exelecete tus aporte, toy buscando de como utilizar un scanner con VB.NET o C# tendras un ejemplo o guia. no encuentro como utilizar

    ResponderEliminar
  124. hola CRISTIAN

    exacto en este caso solo son dos capas
    el helper no es la capa de negocio

    [N-Tier] – Desarrollo en capas - Ejemplo Facturación – Parte 3

    en este otro articulo si veras la implementacion de 3 capas

    hasta donde se no deberian perder nada si es que armas bien la arquitectura, si programas mas tengas 1,2 o 3 capas el rendimiento lo vas a perder, pero no es por la arquitectura sino porque esta mal diseñado

    saludos

    ResponderEliminar
  125. hola Jordan

    podrias suar las librerias de WIA o TWAIN

    http://social.msdn.microsoft.com/Forums/es-ES/4218281e-4444-4443-9902-0f8643cf2660/obtener-imagen-desde-scanner-directamente-a-imagen-o-pdf-sin-utilizar-el-asistente?forum=vcses

    en la pregunta del foro respondo sobre este mismo tema

    saludos

    ResponderEliminar
  126. hola leandro yo estoy implementando esos mismo procedimientos pero para un sistema de autos, aqui te pongo el codigo para que me digas por favor por que me da error en esas 3 lineas

    private static Autoacces AgregarNuevo(Autoacces auto)
    {
    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SistemadeAutos"].ToString()))
    {
    conn.Open();

    string query = @"INSERT INTO catalogo (Marca, Año, Transmision, Recomendaciones, Imagen)
    VALUES (@Marca, @Año, @Transmision, @Recomendaciones, @Imagen);
    SELECT SCOPE_IDENTITY()";

    SqlCommand cmd = new SqlCommand(query, conn);

    cmd.Parameters.AddWithValue("@Marca", auto.Marca);
    cmd.Parameters.AddWithValue("@Año", auto.Año);
    cmd.Parameters.AddWithValue("@Transimision", auto.Transmision);
    cmd.Parameters.AddWithValue("@Recomendaciones", auto.Recomendaciones);

    SqlParameter imageParam = cmd.Parameters.Add("@Imagen", System.Data.SqlDbType.Image);
    imageParam.Value = auto.Imagen;

    //
    // se recupera el id generado por la tabla
    //
    //en esta fila me da error// auto.Marca = Convert.ToInt32(cmd.ExecuteScalar());
    }
    private static Autoacces Actualizar(Autoacces auto)
    {
    //aqui me da error//using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SistemadeAutos"].ToString()))
    {
    conn.Open();
    public static Autoacces Save(Autoacces auto)
    {
    using (TransactionScope scope = new TransactionScope())
    {
    //
    // Graba datos empleado
    //
    //aqui me da error// if (Existe(auto.Marca))
    Actualizar(auto);
    else
    AgregarNuevo(auto);

    ResponderEliminar
  127. como veras solo puede los 3 lugares donde me da el error ya que el codigo es muy grande

    ResponderEliminar
  128. hola Joel

    pero no mencionaste cual es el mensaje de error que obtienes

    veo el codigo y parece estar todo correcto

    saludos

    ResponderEliminar
  129. Hola que tal Leandro te saludo desde Ecuador , sabes la mayoría de tus blog me han sido de gran utilidad, y lo q estas publicando es lo q justamente necesito, como guardar imágenes, pero en este caso al guardar utilizo un stored procedure que desde la base de datos sql esta creado… el problema está en como adapto tu código de almacenar imagen a mi línea de sentencia, ya que tú lo envías como tipo de dato image , aunque si deseo guardar la imagen con ese tipo, pero no sé cómo adaptarle en este caso. Ojala me puedas ayudar o dar una sugerencia, a continuación te pongo como está el código.
    Desde ya te doy gracias por el tiempo que dedicas a ayudarnos ..
    Module Module1
    '// Declaracion de variable globales para la conexion
    Public conexion As SqlClient.SqlConnection
    Public com As SqlClient.SqlCommand, dr As SqlClient.SqlDataReader
    Public da As SqlClient.SqlDataAdapter, cb As SqlClient.SqlCommandBuilder
    End Module
    ---------------------------------------

    ced = txced.Text
    nom = UCase(txtnombre.Text)
    ape = UCase(txtapellido.Text)
    dir = UCase(txtdireccion.Text)
    fec_nac = DateTimePicker1.Value.ToString("yyyy-MM-dd")
    sql = "exec guardar_paciente '" + ced + "','" + nom + "','" + ape + "','" + dir + "','" + fec_nac + "','" + foto + "'"
    conectar()
    com = New SqlClient.SqlCommand(sql, conexion)
    res = com.ExecuteNonQuery
    conexion.Close()

    ResponderEliminar
  130. hola mary

    la resolucion utilizando procedure es la misma que aqui planteo, solo que no debes usar el exec para invocar a un procesure sino que seria

    using (SqlConnection conn = new SqlConnection("connectionstring"))
    {

    SqlCommand cmd = new SqlCommand("NombreProcedure", conn);
    cmd.CommandType = SqlCommandType.StoredProcedure;

    cmd.Parameters.AddWithValue("@param1", valor);

    cmd.ExecuteNonQuery();
    }

    la idea es que si defines el commandtype no necesitas del exec, por lo tanto asignas el parametro a la coleccion Parameters del Sqlcommand


    saludos

    ResponderEliminar
  131. Que tal Leandro, Un buen dia por fa talvez me puedas ayudar con lo siguiente: Estoy generando la impresion de unas facturas pero al momento de realizar esto se saturan los pools del IIS 7 y no permite que ningun usuario pueda realizar ninguna accion con la aplicacion para solventar esto tengo que ejecutar un recycle a los pools o en su defecto un IISRESET. La consulta es talvez tu conoces como liberar los pools desde el programa una vez terminado de imprimir las facturas????? O que puedo hacer para que no se presente este problema. Adicional te comento que se encuentra configurado para que se ejecute automaticamente el recycle cada dia, no puedo configurar para que se ejecute cada 10 o 15 min ya que esto conllevaria a que cada vez que realice les bote de las sesiones a los usuarios. De antemano Muchas gracias por tu ayuda... Saludos

    ResponderEliminar
  132. hola Mario

    entiendo que es un desarrollo web, pero me pregunto porque imprimes directo desde el servidor?
    porque no generas el reporte, lo envias al cliente para que lo descargue y que este lo imprima desde su pc local, no veo como buena idea que un server web deba tener conectada una impresora

    sino podrias tambien realizar un proceso separado al iis para estas impresiones, quizas un servicio de windows (el cual podria estar en el mismo servidor) y que el sitio web lance la accion de impresion para que ese otro proceso se encargue sin molestar al server web
    es mas ese servicio hasta podria estar en una pc separada

    saludos

    ResponderEliminar
  133. Gracias Leandro, fue de gran utilidad tu respuesta ,pude solucionar el problema... gracias

    ResponderEliminar
  134. Hola Leandro muy buena explicación, te hago una consulta se podría asociar para un cliente varias imágenes? debería crearse 2 tablas?
    el tema es que yo quiero consultar para un cliente las imagenes que tiene almacendas. Gracias

    ResponderEliminar
  135. hola TECSECA

    en este caso la tabla de empleados se diseño con un campo de imagen por lo que solo podras contener una

    ahora bien si se quiere definir dos se podria simplemente agregar un campo adicional

    aunque si se prevee que puede tener N imagenes se podria crear una tabla que tenga los campos

    EmpleadoImagenes(tabla)
    ImageId PK
    EmpleadoId FK
    Imagen

    esta table se unira uno a muchos con la de empleados

    saludos

    ResponderEliminar
  136. Gracias por tu respuesta. Ahora bien, de acuerdo a mi consulta entiendo que tu respuesta seria para para adaptar a tu ejemplo. La nueva tabla a crear sera lo mas eficiente al parecer, pero la verdad no se me ocurre el codigo para adaptarlo a tu ejemplo. Me podrias orientar por favor. Muchas Gracias

    ResponderEliminar
  137. hola TECSECA

    pero es codigo implicaria recorrer la lista de imagenes que tienes que persistir y realizar tantos INSERT como imagenes tendra

    seria poner en un loop el insert que harias con una sola imagen

    insertar una imagen como explico en este articulo imagino quedo claro, no ? bueno insertar varias en esta table seria ejecutar el INSERT de una imagen varias veces

    saludos

    ResponderEliminar
  138. Hola, soy nuevo en programación (vb). Me gustaría saber como puedo la imagen de un picturebox, previamente cargada de webcam, guardarla en base de datos access y después, cuando la necesite verla en el picturebox. Gracias de antemano

    ResponderEliminar
  139. Hola quisiera que me ayudes en un sistema pequeño que estoy haciendo quiero guardar imagenes en una base de datos de access con vb. net 2010 y que esas imagenes se carguen en un datagridview. Gracias de antemano

    ResponderEliminar
    Respuestas
    1. hola
      Pero no hay mucha diferencia en el codigo que implementarias entre sql server o access (en ambos usarias ado.net), quizas lo que cambie es el tipo de dato que defines en la tabla
      En ambos casos defienes parametro y envias un array de byte del archivo
      saludos

      Eliminar
  140. Hola leandro en mi caso yo uso 3 capas, y lo que quiero hacer es guardar una foto del empleado en mi base de datos de sql lo cual mi campo es de tipo IMAGE. y quiero hacerlo a travez de procedimientos almacenados juntamente con un picturebox y un openfiledialog en mi form de C# podrias ayudarme con un codigo mas sencillo por favor, esto es para una tarea de la Universidad. mi codigo en mi capa de conexion es el siguiente:

    public class SQLHelper
    {
    public DataTable ExecuteDataQuery(String NombreSP, params object[] ParamArray)
    {
    String strcone = "Integrated Security=true; Initial Catalog=BHCARDENAS;server=.";
    SqlConnection cn = new SqlConnection(strcone);
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = cn;
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = NombreSP;
    cn.Open();
    SqlCommandBuilder.DeriveParameters(cmd);
    int i = 0;
    foreach (SqlParameter p in cmd.Parameters)
    {
    if (p.ParameterName != "@RETURN_VALUE")
    {
    p.Value = ParamArray[i];
    i++;
    }
    }
    cn.Close();
    SqlDataAdapter da;
    da = new SqlDataAdapter(cmd);

    DataTable dt = new DataTable();
    da.Fill(dt);
    return dt;
    }

    //GRABA
    public String ExecuteUpdateQuery(String NombreSP, params object[] ParamArray)
    {
    String result = "";
    try
    {
    String strcone = "Integrated Security=true; Initial Catalog=BHCARDENAS;server=.";
    SqlConnection cn = new SqlConnection(strcone);
    SqlCommand cmd = new SqlCommand();
    cmd.Connection = cn;
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = NombreSP;
    cn.Open();
    SqlCommandBuilder.DeriveParameters(cmd);
    int i = 0;
    foreach (SqlParameter p in cmd.Parameters)
    {
    if (p.ParameterName != "@RETURN_VALUE")
    {
    p.Value = ParamArray[i];
    i++;
    }
    }
    cmd.ExecuteNonQuery();
    cn.Close();
    result = "Operacion Exitosa";

    }
    catch (Exception ex)
    {
    result = "Error en SP" + ex.Message;
    }
    return result;

    }


    espero que puedas ayudarme, gracias saludos.

    ResponderEliminar
    Respuestas
    1. hola
      te estas equivocando cuando defines ExecuteDataQuery() no deberias indicar los parametro como array de object, sino que deberian ser un array de SqlParameter

      public DataTable ExecuteDataQuery(String NombreSP, params SqlParameter[] sqlParams){ ...

      de esta forma cuando indicas los datos desde fuera puedes crear el parametro del tipo imagen y asignar el array de byte de la imagen que quieres insertar

      Es mas para hacerlo mas simple usaria

      public DataTable ExecuteDataQuery(String NombreSP, List sqlParams){ ...

      una lista generica

      Una aclaracion definir este helper no quiere decir que uses 3 capas, el helper no representa la capa de persistencia, para eso deberias implementar el patron Repository

      saludos

      Eliminar
  141. Que tal Leandro, estoy diseñanado un sistema en vb.net, solo que no se muy bien lo de las clases y metódos utilizados al igual que no se no se como se hacen los procemientos almacenados, yo solo inserto directo mi codigo en vb. Por esta razón queria pedirte tu ayuda para solucionar el error en en que me encuentro al no poder insertar una imagen.
    Este es el codigo que utilizo en esta parte declaro mi variable,

    Dim vimgn As New IO.MemoryStream()
    If ptbimagen.Image Is Nothing Then
    ptbimagen.Image.Save(vimgn, ptbimagen.Image.RawFormat)
    Else
    ptbimagen.Image = My.Resources.fondo
    ptbimagen.Image.Save(vimgn, ptbimagen.Image.RawFormat)
    End If

    Aca inserto entre otros mis registros y el de imagen

    With cmd
    .Connection = conexion
    .CommandText = " INSERT INTO Compras (idemisor, No_Compra, Pdf, Xml_, Impreso, JpgPdf) " &
    " VALUES (" & idemi & ", '" & noComp & "', '" & vpdf & "', '" & vxml & "', " &
    " '" & vimpre & "', '" & ptbimagen.ImageLocation & "')"
    i = cmd.ExecuteNonQuery
    End With

    Realizo una trazo y esto es lo que me devuelve

    INSERT INTO Compras (idemisor, No_Compra, Pdf, Xml_, Impreso, JpgPdf) VALUES (2, 'A', 'SI', 'SI', 'SI', '')

    Y aca es donde no me trae datos de la imagen.
    al parecer creo que es en esta parte '" & ptbimagen.ImageLocation & "', no se que metódo o clase utilizar para que me devulva algo.
    Espero puedas ayudarme.
    Saludos

    ResponderEliminar
  142. Hola, queria saber que diferencia habria entre que el EmpleadoEntity en vez de tener un EstadoCivil de tipo entero, tuviese un EstadoCivil de tipo EstadoCivilEntity. y como seria si quisiese mostrarlo en el datagridview de lista de empleados

    Por otro lado ¿que deberia hacer si quisiese mostrar en el datagridvew el id de empleado en el sguente formato: EMP-0001? Estuve intentando esto: row.Cells["id"].Value = "EMP-" + item.id.ToString("0000"); pero me dice que solo puedo asignar un valor de tipo entero

    ResponderEliminar
    Respuestas
    1. hola
      La diferencia entre un estado civil como entidad es de dodne obtienes la lista para cargar el combo que listas al usuario, despues el resto es igual

      Para mostrarlo en el grid vas a tener que definir el INNER JOIN en el SELECT y devolver el nombre o descripcion de ese estado como respuesta

      si usas clases podrias definir este strinf directo alli

      public string IdDesc { get { return string.Format("EMP-{0:0000}", this.Id); } }

      esta propiedad de solo lectura la defines dentro de la clase y la usas para bindear con el campo del grid

      saludos

      Eliminar
  143. Y como haria si quisiese mostrar los estudios en un campo del datagrdview principal (el de la lista de empleados) de esta forma: "primaria, secundaria, superior", es decir, en un solo campo separados por comas

    ResponderEliminar
    Respuestas
    1. En este caso vas a tener que armar una query sql que permita agrupar por estos campos y unirlos en uno solo

      GROUP BY clause to get comma-separated values

      saludos

      Eliminar
    2. Pero si la query llega con todos los estudios de los empleados en una celda, como pasaria de la query a objetos: EmpleadoEntity y EstudioEntity para luego pasar la lista de EmpleadoEntity al datagridview mostrando los nombres de los estudios alli?

      Eliminar
    3. hola
      En el DataGridView no vas a poder mostrar objetos complejos, es por eso que una lista no aplica en el grid, podrias si en la clase EmpleadoEntity crear una propiedad readonly que usea la propiedad de lista de estudios y genere un string con los estudios separados por coma, usando esta nueva propiedad de solo lectura en el campo del control grid
      saludos

      Eliminar
  144. Como deberia ser el stored procedure en la base de datos para asignar los estudios a los empleados? ¿sera posible enviar por parametro al stored procedure de "crear empleado", un array de los id de los estudios para luego asignarle a ese empleado todos sus estudios en un solo procedimiento o se tiene que hacer de obligado una llamada por cada estudio a un procedimiento que asigne estudios uno por uno?

    ResponderEliminar
    Respuestas
    1. hola
      Podrias enviar a un procedure la lista de ids de estudios que quieres relacionar con el empleado, para eso usarias el
      Table-Valued Parameters in SQL Server 2008 (ADO.NET)
      con un table-valued podrias envias datos en formato array, tambien podrias enviar un xml con la lista
      saludos

      Eliminar
  145. Hola leandro, como podria actualizar los datos si los dos formularios lo tengo en uno solo?
    he intentado hacerlo de la manera en que describes en tu ejemplo pero no he podido hacerlo.

    ResponderEliminar
  146. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  147. Es decir todo lo quiero realizar en un solo formulario, yodo va bien hasta el momento de hacerle doble click para pasar a editarlo.
    cuando hago esa operación me abre nuevamente el formulario y eso es lo que no deseo, en vez que me abra otro formulario que lo actualice ahí mismo.

    ResponderEliminar