viernes, 22 de enero de 2010

[WinForms] – Pasaje de información formulario hijo

 

Introducción


Es muy común cuando se desarrolla con formularios tener la necesidad de comunicarlos de pasar información entre ellos, pudiendo trabajar con la selección de ciertos datos, o especificar algún valor que se asignara en algún control, etc

Este articulo intentara explicar cuales son las mejores formas de realizar estas operaciones.

En artículos previos se ha comentado como pasar información de un formulario hijo al padre, o aquel que realizo la apertura.

  • Comunicar formularios MDI
  • Comunicar formularios de forma desacoplada
  • DataGridView – Parte 3 – Pasaje de información entre grillas en distintos formularios

    En esta oportunidad se atacara la comunicación del padre al hijo, o sea se pasara información al momento de realizar la apertura del formulario.

    Existen dos técnicas que pueden aplicarse en este caso:

    • haciendo uso del constructor del formulario
    • por medio de propiedades

     

    1 – Uso del Constructor del formulario


    Una de las opciones pasa pasar información entre formularios es por medio del constructor.

    Debe tenerse en cuenta que en .net los formularios son en definitiva clases, por lo tanto puede aplicarse los mismo conceptos orientados a objetos.

    EJEMPLO 1

    Empezaremos por un ejemplo simple, en donde solo se pasara un valor del formulario denominado “Principal”, a otro formulario denominado “Detalle” que tomara este valor y lo mostrara en un TextBox.

    Formulario Detalle

    [C#]

    public partial class frmDetalle : Form
    {
        private int? idbusqueda = null;
    
        public frmDetalle()
        {
            InitializeComponent();
        }
    
        public frmDetalle(int idbusqueda) : this()
        {
            this.idbusqueda = idbusqueda;
        }
    
        private void frmDetalle_Load(object sender, EventArgs e)
        {
            if(idbusqueda.HasValue)
                txtIdBusqueda.Text = Convert.ToString(idbusqueda);
        }
    }

    [VB.NET]

    Public Partial Class frmDetalle
        Inherits Form
    
    	Private _idbusqueda As System.Nullable(Of Integer) = Nothing
    
    	Public Sub New()
    		InitializeComponent()
    	End Sub
    
    	Public Sub New(idbusqueda As Integer)
    		Me.New()
    		Me._idbusqueda = idbusqueda
    	End Sub
    
    	Private Sub frmDetalle_Load(sender As Object, e As EventArgs)
    		If _idbusqueda.HasValue Then
    			txtIdBusqueda.Text = Convert.ToString(_idbusqueda)
    		End If
        End Sub
    
    End Class

    En este código se puede apreciar como se ha creado un nuevo constructor en la clase del formulario agregando al mismo un parámetro en donde se indica el id que debe trabajarse.

    Un punto importante a destacar es que al pasar la información en el constructor no se realice la asignación directamente en un control del formulario, sino que recomendable hacer uso variable privadas para contener valor que luego en el evento Load o en algún otro evento se haga efectiva la asignación si es necesaria.

    Esto es así porque en la etapa de construcción de la clase del formulario los controles aun no están inicializados, por lo tanto una asignación en ese punto puede traer problemas.

    En el caso de este ejemplo se veras que la variable privada ha sido declarada para soportar valores nulos, se realizo de esta forma ya que se ha dejado el constructor del formulario sin parámetros, en caso de que no quiera especificarse valor de búsqueda al momento de instanciar el formulario.

    En caso de darse la situación en que sea necesario usar el formulario sin pasar un valor determinado, esta situación es verificada en el evento Load y así determinar la asignación un valor a la variable o no.

    Formulario Principal

    [C#]

    public partial class frmPrincipal : Form
    {
        public frmPrincipal()
        {
            InitializeComponent();
        }
    
        private void btnBuscar_Click(object sender, EventArgs e)
        {
            int idbusqueda = Convert.ToInt32(txtBusqueda.Text);
    
            frmDetalle form = new frmDetalle(idbusqueda);
            form.Show();
    
        }
    }

    [VB.NET]

    Public Partial Class frmPrincipal
        Inherits Form
    
    	Public Sub New()
    		InitializeComponent()
    	End Sub
    
        Private Sub btnBuscar_Click(ByVal sender As Object, ByVal e As EventArgs)
    
            Dim idbusqueda As Integer = Convert.ToInt32(txtBusqueda.Text)
    
            Dim form As New frmDetalle(idbusqueda)
            form.Show()
    
        End Sub
    End Class

    El formulario denominado Padre o Principal encargado de hacer la llamada y de pasa el valor al formulario Hijo es bastante simple, solamente cuenta con un botón el cual al ejecutarse crea una instancia del formulario a invocar pasándole allí mismo el valor que el usuario ingreso en el TextBox, y por ultimo invoca al método Show() para que se desplegué el formulario.

    [C#]
    [VB.NET]

    EJEMPLO 2

    En este punto aplicaremos el mismo concepto que el ejemplo anterior solo que lo haremos algo mas complejo.

    No solo vamos a enviar como valor al segundo formulario un simple dato, en este caso será toda una colección de ítems seleccionados en un DataGridView,

    Formulario Detalle

    [C#]

    public partial class frmDetalle : Form
    {
        private int _maxPrecio;
        private List<DataRow> _listDataRow = null;
        private List<Productos> _listProducto = null;
    
    
        private frmDetalle()
        {
            InitializeComponent();
        }
    
        public frmDetalle(List<DataRow> items)
            : this(items, 3200)
        {
        }
    
        public frmDetalle(List<Productos> items)
            : this(items, 3200)
        {
        }
    
        public frmDetalle(List<DataRow> items, int maxPrecio)
            : this()
        {
            this._listDataRow = items;
            this._maxPrecio = maxPrecio;
        }
    
        public frmDetalle(List<Productos> items, int maxPrecio)
            : this()
        {
            this._listProducto = items;
            this._maxPrecio = maxPrecio;
        }
    
    
        private void frmDetalle_Load(object sender, EventArgs e)
        {
            //
            // Cargo los items provenientes del constructor con DataRows
            //
            if (_listDataRow != null)
            {
                foreach (DataRow row in _listDataRow)
                {
                    dtgDetalles.Rows.Add(row.ItemArray);
                }
    
            }
    
            //
            // Cargo los items provenientes del constructor 
            // con entidades de la clase Producto
            //
            if (_listProducto != null)
            {
                dtgDetalles.AutoGenerateColumns = false;
                dtgDetalles.DataSource = _listProducto;
            }
        }
    
        private void dtgDetalles_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
    
            if (dtgDetalles.Columns[e.ColumnIndex].Name == "Precio")
            {
                if (Convert.ToInt32(e.Value) > _maxPrecio)
                    dtgDetalles.Rows[e.RowIndex].DefaultCellStyle.ForeColor = Color.Red;
            }
    
        }
    
    
        public class Productos
        {
            public int IdProducto { get; set; }
            public string Descripcion { get; set; }
            public int Precio { get; set; }
        }
    
    }

    [VB.NET]

    Public Partial Class frmDetalle
        Inherits Form
    
    	Private _maxPrecio As Integer
    	Private _listDataRow As List(Of DataRow) = Nothing
    	Private _listProducto As List(Of Productos) = Nothing
    
    
    	Private Sub New()
    		InitializeComponent()
    	End Sub
    
    	Public Sub New(items As List(Of DataRow))
    		Me.New(items, 3200)
    	End Sub
    
    	Public Sub New(items As List(Of Productos))
    		Me.New(items, 3200)
    	End Sub
    
    	Public Sub New(items As List(Of DataRow), maxPrecio As Integer)
    		Me.New()
    		Me._listDataRow = items
    		Me._maxPrecio = maxPrecio
    	End Sub
    
    	Public Sub New(items As List(Of Productos), maxPrecio As Integer)
    		Me.New()
    		Me._listProducto = items
    		Me._maxPrecio = maxPrecio
    	End Sub
    
    
    	Private Sub frmDetalle_Load(sender As Object, e As EventArgs)
    		'
    		' Cargo los items provenientes del constructor con DataRows
    		'
    		If _listDataRow IsNot Nothing Then
    			For Each row As DataRow In _listDataRow
    				dtgDetalles.Rows.Add(row.ItemArray)
    
    			Next
    		End If
    
    		'
    		' Cargo los items provenientes del constructor 
    		' con entidades de la clase Producto
    		'
    		If _listProducto IsNot Nothing Then
    			dtgDetalles.AutoGenerateColumns = False
    			dtgDetalles.DataSource = _listProducto
    		End If
    	End Sub
    
    	Private Sub dtgDetalles_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs)
    
    		If dtgDetalles.Columns(e.ColumnIndex).Name = "Precio" Then
    			If Convert.ToInt32(e.Value) > _maxPrecio Then
    				dtgDetalles.Rows(e.RowIndex).DefaultCellStyle.ForeColor = Color.Red
    			End If
    		End If
    
    	End Sub
    
    
        Public Class Productos
            Private _IdProducto As Integer
    
            Public Property IdProducto() As Integer
                Get
                    Return _IdProducto
                End Get
                Set(ByVal value As Integer)
                    _IdProducto = value
                End Set
            End Property
    
            Private _Descripcion As String
    
            Public Property Descripcion() As String
                Get
                    Return _Descripcion
                End Get
                Set(ByVal value As String)
                    _Descripcion = value
                End Set
            End Property
    
            Private _Precio As Integer
    
            Public Property Precio() As Integer
                Get
                    Return _Precio
                End Get
                Set(ByVal value As Integer)
                    _Precio = value
                End Set
            End Property
    
        End Class
    
    End Class

    Este código tiene bastante para comentar, en primer lugar es interesante analizar como se han definido varios constructores para el formulario, los cuales abarcan distintos tipos de colecciones, que podrá ser enviada al mismo para ser desplegada en el formulario de detalle.

    Entre las opciones disponibles esta una lista de DataRow, o la carga de una clase custom que representa a los productos. Además en los constructores esta la posibilidad de definir un valor de precio máximo, el cual cumplirá la función de definir que registros de la grilla aparecerán con letras rojas según se haya excedido ese monto, sino se especifica valor (al optar el uso del constructor sin este parámetro), se define un valor por defecto de 3200.

    Algo importante a marcar es la definición del constructor sin parámetros como privado, esto permite que sea invocado desde dentro de la clase, pero no desde fuera, es por eso que los constructores con parámetros lo terminan utilizan, cuando haces :this(), para inicializar los controles del formulario, pero desde fuera quien cree la instancia deberá obligativamente definir los valores de los ítems a cargar en la grilla.

    Dentro del evento Load del formulario se valida que lista de datos se ha cargado y se procede con la carga del mismo, como se vera cada lista tiene su particularidad en el método con que carga la información en el DataGridView.

    Además se hizo uso del evento CellFormatting, y como juega el valor de precio máximo para definir que filas de la grilla deben ir en color, en este caso definido con rojo.

    Formulario Principal

    [C#]

    private void btnMostrarSeleccionDataRow_Click(object sender, EventArgs e)
    {
        List<DataRow> items = new List<DataRow>();
    
        foreach (DataGridViewRow row in dataGridView1.SelectedRows)
        {
            DataRowView dataItem = row.DataBoundItem as DataRowView;
            items.Add(dataItem.Row);
        }
    
        frmDetalle form = new frmDetalle(items);
        form.Show();
    }
    
    
    private void btnMostrarSeleccionEntidades_Click(object sender, EventArgs e)
    {
        List<frmDetalle.Productos> items = new List<frmDetalle.Productos>();
    
        foreach (DataGridViewRow row in dataGridView1.SelectedRows)
        {
            frmDetalle.Productos producto = new frmDetalle.Productos()
            {
                IdProducto = Convert.ToInt32(row.Cells["IdProducto"].Value),
                Descripcion = Convert.ToString(row.Cells["Descripcion"].Value),
                Precio = Convert.ToInt32(row.Cells["Precio"].Value),
            };
    
            items.Add(producto);
        }
    
        frmDetalle form = new frmDetalle(items);
        form.Show();
    }

    [VB.NET]

    Private Sub btnMostrarSeleccionDataRow_Click(ByVal sender As Object, ByVal e As EventArgs)
    
        Dim items As New List(Of DataRow)()
    
        For Each row As DataGridViewRow In dataGridView1.SelectedRows
            Dim dataItem As DataRowView = TryCast(row.DataBoundItem, DataRowView)
            items.Add(dataItem.Row)
        Next
    
        Dim form As New frmDetalle(items)
        form.Show()
    
    End Sub
    
    
    Private Sub btnMostrarSeleccionEntidades_Click(ByVal sender As Object, ByVal e As EventArgs)
    
        Dim items As New List(Of frmDetalle.Productos)()
    
        For Each row As DataGridViewRow In dataGridView1.SelectedRows
    
            Dim producto As New frmDetalle.Productos()
    
            producto.IdProducto = CInt(row.Cells("IdProducto").Value)
            producto.Descripcion = CStr(row.Cells("Descripcion").Value)
            producto.Precio = CInt(row.Cells("Precio").Value)
    
            items.Add(producto)
    
        Next
    
        Dim form As New frmDetalle(items)
        form.Show()
    
    End Sub

    Del formulario Padre o Principal que realiza la llamada, es importante notar los dos métodos utilizados para pasar la mismos registros seleccionados, pero utilizando distintos constructores del formulario Hijo o Detalle.

    En el primero se toma la información usada como origen de datos al DataGridView, transformado este en un DataRow, en el segundo método se construye una entidad independiente en base a la información e la misma.

    [C#]
    [VB.NET]

    2 – Uso de Propiedades


    Esta es otra de las técnicas que pueden utilizarse para pasar información entre formularios, por ahí no es tan restrictivo al momento de inicializar el formulario como el uso de constructores, ya que en este método el constructor será único y sin parámetros, las propiedades definirán los atributos que podrán asignarse.

    Para este caso se implementaran los mismo ejemplos anteriores pero con el uso de propiedades esto permitirá la comparación y así analizar que técnica resulta mas conveniente, como en todo desarrollo no hay una regla que diga que esta bien y que mal, será cuestión de aplicar los criterios que uno crea mas razonables y seleccionar que método aplicar.

    EJEMPLO 1

    Formulario Detalle

    [C#]

    public partial class frmDetalle : Form
    {
        private int? _idbusqueda = null;
    
        public int IdBusqueda
        {
            set
            {
                _idbusqueda = value;
            }
        }
    
        public frmDetalle()
        {
            InitializeComponent();
        }
    
    
    
        private void frmDetalle_Load(object sender, EventArgs e)
        {
            if (_idbusqueda.HasValue)
                txtIdBusqueda.Text = Convert.ToString(_idbusqueda);
        }
    }

    [VB.NET]

    Public Partial Class frmDetalle
        Inherits Form
    
    	Private _idbusqueda As System.Nullable(Of Integer) = Nothing
    
    	Public WriteOnly Property IdBusqueda() As Integer
    		Set
    			_idbusqueda = value
    		End Set
    	End Property
    
    	Public Sub New()
    		InitializeComponent()
    	End Sub
    
    
    
    	Private Sub frmDetalle_Load(sender As Object, e As EventArgs)
    		If _idbusqueda.HasValue Then
    			txtIdBusqueda.Text = Convert.ToString(_idbusqueda)
    		End If
        End Sub
    
    End Class

    En este caso se ha creado una propiedad de nombre IdBusqueda, definida para que solo pueda ser establecido su valor, pero no su lectura, esto es así porque la idea es luego usar la variable privada para la lógica interna.

    Si se compara con el método anterior del constructor el código no ha cambiado mucho, solo se ha dejado un constructor sin parámetros, y se agrego la propiedad.

    Formulario Principal

    [C#]

    public partial class frmPrincipal : Form
    {
        public frmPrincipal()
        {
            InitializeComponent();
        }
    
        private void btnBuscar_Click(object sender, EventArgs e)
        {
            int idbusqueda = Convert.ToInt32(txtBusqueda.Text);
    
            frmDetalle form = new frmDetalle();
            form.IdBusqueda = idbusqueda;
            
            form.Show();
    
        }
    }

    [VB.NET]

    Public Partial Class frmPrincipal
        Inherits Form
    
    	Public Sub New()
    		InitializeComponent()
    	End Sub
    
        Private Sub btnBuscar_Click(ByVal sender As Object, ByVal e As EventArgs)
    
            Dim idbusqueda As Integer = Convert.ToInt32(txtBusqueda.Text)
    
            Dim form As New frmDetalle()
            form.IdBusqueda = idbusqueda
    
            form.Show()
    
        End Sub
    End Class

    Al crear la instancia ahora se puede hacer libremente sin necesidad de establecer allí mismo un valor como parámetro, pues esto ha sido reemplazado por la línea contigua en donde se utiliza la propiedad creada.

    [C#]
    [VB.NET]

    EJEMPLO 2

    Formulario Detalle

    [C#]

    public partial class frmDetalle : Form
    {
        private int _maxPrecio;
        private List<DataRow> _listDataRow = null;
        private List<Productos> _listProducto = null;
    
        public List<DataRow> ListaDataRow
        {
            set
            {
                _listDataRow = value;
            }
        }
    
        public List<Productos> ListaProducto
        {
            set
            {
                _listProducto = value;
            }
        }
    
        public int PrecioMaximo
        {
            set
            {
                _maxPrecio = value;
            }
        }
    
        public frmDetalle()
        {
            InitializeComponent();
    
            this._maxPrecio = 3200;
        }
    
    
    
        private void frmDetalle_Load(object sender, EventArgs e)
        {
            //
            // Cargo los items provenientes del constructor con DataRows
            //
            if (_listDataRow != null)
            {
                foreach (DataRow row in _listDataRow)
                {
                    dtgDetalles.Rows.Add(row.ItemArray);
                }
    
            }
    
            //
            // Cargo los items provenientes del constructor 
            // con entidades de la clase Producto
            //
            if (_listProducto != null)
            {
                dtgDetalles.AutoGenerateColumns = false;
                dtgDetalles.DataSource = _listProducto;
            }
        }
    
        private void dtgDetalles_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
    
            if (dtgDetalles.Columns[e.ColumnIndex].Name == "Precio")
            {
                if (Convert.ToInt32(e.Value) > _maxPrecio)
                    dtgDetalles.Rows[e.RowIndex].DefaultCellStyle.ForeColor = Color.Red;
            }
    
        }
    
    
        public class Productos
        {
            public int IdProducto { get; set; }
            public string Descripcion { get; set; }
            public int Precio { get; set; }
        }
    
    }

    [VB.NET]

    Public Partial Class frmDetalle
        Inherits Form
    
    	Private _maxPrecio As Integer
    	Private _listDataRow As List(Of DataRow) = Nothing
    	Private _listProducto As List(Of Productos) = Nothing
    
    	Public WriteOnly Property ListaDataRow() As List(Of DataRow)
    		Set
    			_listDataRow = value
    		End Set
    	End Property
    
    	Public WriteOnly Property ListaProducto() As List(Of Productos)
    		Set
    			_listProducto = value
    		End Set
    	End Property
    
    	Public WriteOnly Property PrecioMaximo() As Integer
    		Set
    			_maxPrecio = value
    		End Set
    	End Property
    
    	Public Sub New()
    		InitializeComponent()
    
    		Me._maxPrecio = 3200
    	End Sub
    
    
    
    	Private Sub frmDetalle_Load(sender As Object, e As EventArgs)
    		'
    		' Cargo los items provenientes del constructor con DataRows
    		'
    		If _listDataRow IsNot Nothing Then
    			For Each row As DataRow In _listDataRow
    				dtgDetalles.Rows.Add(row.ItemArray)
    
    			Next
    		End If
    
    		'
    		' Cargo los items provenientes del constructor 
    		' con entidades de la clase Producto
    		'
    		If _listProducto IsNot Nothing Then
    			dtgDetalles.AutoGenerateColumns = False
    			dtgDetalles.DataSource = _listProducto
    		End If
    	End Sub
    
    	Private Sub dtgDetalles_CellFormatting(sender As Object, e As DataGridViewCellFormattingEventArgs)
    
    		If dtgDetalles.Columns(e.ColumnIndex).Name = "Precio" Then
    			If Convert.ToInt32(e.Value) > _maxPrecio Then
    				dtgDetalles.Rows(e.RowIndex).DefaultCellStyle.ForeColor = Color.Red
    			End If
    		End If
    
    	End Sub
    
    
        Public Class Productos
            Private _IdProducto As Integer
    
            Public Property IdProducto() As Integer
                Get
                    Return _IdProducto
                End Get
                Set(ByVal value As Integer)
                    _IdProducto = value
                End Set
            End Property
    
            Private _Descripcion As String
    
            Public Property Descripcion() As String
                Get
                    Return _Descripcion
                End Get
                Set(ByVal value As String)
                    _Descripcion = value
                End Set
            End Property
    
            Private _Precio As Integer
    
            Public Property Precio() As Integer
                Get
                    Return _Precio
                End Get
                Set(ByVal value As Integer)
                    _Precio = value
                End Set
            End Property
    
        End Class
    
    End Class

    La sección donde se aplica la lógica del formulario si se compara con los ejemplo que usaban un constructor para pasar los valores, no ha cambiado en nada, la única parte que si ha recibido los cambios es en la definición del método en que se asignan los valores a las variables privadas del formulario.

    En este caso el constructor sin parámetro vuelve a ser publico, y dentro de este se define valor por defecto para el precio máximo, usado para asignar los colores a las filas de la grilla.

    Formulario Principal

    [C#]

    private void btnMostrarSeleccionDataRow_Click(object sender, EventArgs e)
    {
        List<DataRow> items = new List<DataRow>();
    
        foreach (DataGridViewRow row in dataGridView1.SelectedRows)
        {
            DataRowView dataItem = row.DataBoundItem as DataRowView;
            items.Add(dataItem.Row);
        }
    
        frmDetalle form = new frmDetalle();
        form.ListaDataRow = items;
    
        form.Show();
    }
    
    
    private void btnMostrarSeleccionEntidades_Click(object sender, EventArgs e)
    {
        List<frmDetalle.Productos> items = new List<frmDetalle.Productos>();
    
        foreach (DataGridViewRow row in dataGridView1.SelectedRows)
        {
            frmDetalle.Productos producto = new frmDetalle.Productos()
            {
                IdProducto = Convert.ToInt32(row.Cells["IdProducto"].Value),
                Descripcion = Convert.ToString(row.Cells["Descripcion"].Value),
                Precio = Convert.ToInt32(row.Cells["Precio"].Value),
            };
    
            items.Add(producto);
        }
    
        frmDetalle form = new frmDetalle();
        form.ListaProducto = items;
    
        form.Show();
    }

    [VB.NET]

    Private Sub btnMostrarSeleccionDataRow_Click(sender As Object, e As EventArgs)
    	Dim items As New List(Of DataRow)()
    
    	For Each row As DataGridViewRow In dataGridView1.SelectedRows
    		Dim dataItem As DataRowView = TryCast(row.DataBoundItem, DataRowView)
    		items.Add(dataItem.Row)
    	Next
    
    	Dim form As New frmDetalle()
    	form.ListaDataRow = items
    
    	form.Show()
    End Sub
    
    
    Private Sub btnMostrarSeleccionEntidades_Click(sender As Object, e As EventArgs)
    	Dim items As New List(Of frmDetalle.Productos)()
    
           For Each row As DataGridViewRow In dataGridView1.SelectedRows
    
               Dim producto As New frmDetalle.Productos()
    
               producto.IdProducto = CInt(row.Cells("IdProducto").Value)
               producto.Descripcion = CStr(row.Cells("Descripcion").Value)
               producto.Precio = CInt(row.Cells("Precio").Value)
    
               items.Add(producto)
    
           Next
    
    	Dim form As New frmDetalle()
    	form.ListaProducto = items
    
    	form.Show()
    End Sub
    [C#]
    [VB.NET]
    Conclusión

    Si bien en este articulo se han explicado dos técnicas para pasar información de un formulario Padre a su Hijo, esto no quiere decir que deba usarse una u otra, por el contrario estas podrían ser complementadas entre si.

    En el constructor se podría definir información que es requería para el funcionamiento del formulario que se este invocando, mientras que en las propiedades podría especificarse información opcional.

  • jueves, 21 de enero de 2010

    C# - [ADO.NET] – Parte 6 - Campos Auto numéricos (Identity)

     

    Introducción


    Se basara la explicación de este articulo de uno anterior en donde se aprendió de una forma simple y por medio de un ejemplo completo a trabajar los datos con ado.net

    [ADO.NET] – Parte 5 - Ejemplos Simples – Operaciones CRUD

    Por lo cual tomando ese ejemplo de base se pasara a explicar como trabajar con campos del tipo Autonuméricos o Identity.

    Como es sabido en estos tipos de campos es la base de datos la que genera el Id del registro que se inserta, por lo tanto las modificaciones solo afectaran a las operaciones INSERT de la aplicación de ejemplo.

    En la base de datos se ha realizado el siguiente cambio en la tabla:

     

     

    1- Obtener Identity en la misma consulta


    En este ejemplo se hará uso de una consulta combinada en donde en la misma ejecución se insertara el registro y a la vez se recuperara el identificador autonumérico generado.

    [C#]

    private void btnGuardar_Click(object sender, EventArgs e)
    {
        //
        // Validaciones
        //
        int Id = 0;
        if (!string.IsNullOrEmpty(txtId.Text))
        {
            if (!int.TryParse(txtId.Text, out Id))
            {
                MessageBox.Show("El Id debe ser un valor numerico");
                return;
            }
        }
    
        if (Exists(Id))
        {
            bool result = Update(Id, txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value);
    
            if (result)
                MessageBox.Show("El registro se ha actualizado correctamente.");
        }
        else
        {
            int id = Insert(txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value);
    
            if (id > 0)
            {
                MessageBox.Show(string.Format("El registro se ha insertado correctamente. Id : {0}", id));
                txtId.Text = Convert.ToString(id);
            }
        }
    }
    
    private int Insert(string nombre, string direccion, DateTime fechaNacimiento)
    {
        if (!ValidateForm())
            return 0;
    
        string sql = @"INSERT INTO Contactos (NombreCompleto
                              ,Direccion
                              ,FechaNacimiento)
                          VALUES (@Nombre, 
                                @Direccion, 
                                @FechaNacimiento)
                        SELECT SCOPE_IDENTITY()";
    
    
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            SqlCommand command = new SqlCommand(sql, conn);
            command.Parameters.AddWithValue("Nombre", nombre);
            command.Parameters.AddWithValue("Direccion", direccion);
            command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento);
    
            conn.Open();
    
            return Convert.ToInt32(command.ExecuteScalar());
    
        }
    }

    [VB.NET]

    Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
    	'
    	' Validaciones
    	'
           Dim _Id As Integer = 0
    	If Not String.IsNullOrEmpty(txtId.Text) Then
               If Not Integer.TryParse(txtId.Text, _Id) Then
                   MessageBox.Show("El Id debe ser un valor numerico")
                   Return
               End If
    	End If
    
           If Exists(_Id) Then
               Dim result As Boolean = Update(_Id, txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value)
    
               If result Then
                   MessageBox.Show("El registro se ha actualizado correctamente.")
               End If
           Else
               Dim idObtenido As Integer = Insert(txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value)
    
               If idObtenido > 0 Then
                   MessageBox.Show(String.Format("El registro se ha insertado correctamente. Id : {0}", idObtenido))
                   txtId.Text = Convert.ToString(idObtenido)
               End If
           End If
    End Sub
    
    Private Function Insert(nombre As String, direccion As String, fechaNacimiento As DateTime) As Integer
    	If Not ValidateForm() Then
    		Return 0
    	End If
    
           Dim sql As String = "INSERT INTO Contactos (NombreCompleto" & _
                                   ",Direccion" & _
                                   ",FechaNacimiento)" & _
                                   "VALUES (@Nombre, " & _
                                   "@Direccion, " & _
                                   "@FechaNacimiento)" & _
                               "SELECT SCOPE_IDENTITY()"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Nombre", nombre)
    		command.Parameters.AddWithValue("Direccion", direccion)
    		command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento)
    
    		conn.Open()
    
    
    		Return Convert.ToInt32(command.ExecuteScalar())
    	End Using
    End Function	

    Como se observa en el código el principal cambio se lo lleva al consulta SQL, en donde se combinan dos consultas en una.

    Por medio de método ExecuteScalar() del SqlCommand se realizaran las dos operaciones, ejecutar la consulta de INSERT y al mismo tiempo devolver el valor único del SELECT.

    Por ultimo se lo pasa el id obtenido al evento en al presentación para que muestre el id generado en pantalla.

     

    2 – Obtener Id en consultas separadas


    Un problema con la técnica anterior es que si por alguna razón el INSERT no realiza la operación de forma correcta, puede haber problemas con el id que se obtiene.

    Esto puede ser extraño pero hay formas de asegurar que solo se obtenga con el id si es que la operación anterior se ha realizado de forma correcta.

    Mas que nada esta técnica es útil cuando se quiere asegurar o validar que el INSERT afecto a los registros de forma adecuada.

    [C#]

    private int InsertComandosSeparadas(string nombre, string direccion, DateTime fechaNacimiento)
    {
        if (!ValidateForm())
            return 0;
    
        string sql = @"INSERT INTO Contactos (NombreCompleto
                              ,Direccion
                              ,FechaNacimiento)
                          VALUES (@Nombre, 
                                @Direccion, 
                                @FechaNacimiento)";
    
        
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            SqlCommand command = new SqlCommand(sql, conn);
            command.Parameters.AddWithValue("Nombre", nombre);
            command.Parameters.AddWithValue("Direccion", direccion);
            command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento);
    
            conn.Open();
    
    
            int rowsAffected = command.ExecuteNonQuery();
    
            if (rowsAffected > 0)
            {
                string sqlIdentity = "SELECT @@IDENTITY";
                SqlCommand cmdIdentity = new SqlCommand(sqlIdentity, conn);
    
                return Convert.ToInt32(cmdIdentity.ExecuteScalar());
            }
            else
                return -1;
    
    
        }
    }

    [VB.NET]

    Private Function InsertComandosSeparadas(nombre As String, direccion As String, fechaNacimiento As DateTime) As Integer
    	If Not ValidateForm() Then
    		Return 0
    	End If
    
           Dim sql As String = "INSERT INTO Contactos (NombreCompleto" & _
                                       ",Direccion" & _
                                       ",FechaNacimiento)" & _
                                   "VALUES (@Nombre, " & _
                                       "@Direccion, " & _
                                       "@FechaNacimiento)"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Nombre", nombre)
    		command.Parameters.AddWithValue("Direccion", direccion)
    		command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento)
    
    		conn.Open()
    
    
    		Dim rowsAffected As Integer = command.ExecuteNonQuery()
    
    		If rowsAffected > 0 Then
    			Dim sqlIdentity As String = "SELECT @@IDENTITY"
    			Dim cmdIdentity As New SqlCommand(sqlIdentity, conn)
    
    			Return Convert.ToInt32(cmdIdentity.ExecuteScalar())
    		Else
    			Return -1
    
    
    		End If
    	End Using
    End Function

     

    En le código puede observarse que la primer parte se conserva idéntica a la origina, en donde solo una instrucción de INSERT es declarada, y ejecutada por medio del ExecuteNonQuery(), este retornara los registros afectados y según el valor de retorno se procederá a recuperar el id autonumérico generado.

    Hay un cambio que hay que hacer notar y es que en el SELECT se ha cambiado la técnica usada para recuperar el id, en este caso se hace uso del @@IDENTITY

    Seguramente la pregunta que se harán es porque de este cambio, bien resulta que la diferencia entre estas instrucciones esta en el ámbito en que se ejecutan, SCOPE_IDENTITY trabaja en el ámbito actual, o sea el ámbito de la instrucción, si en este ejemplo usáramos esta instrucción obtendríamos como resultado un NULL, pues este debe ser ejecutado en la misma operación.

    En cambio @@IDENTY no es afectado por aun ámbito especifico, puede ser consultado luego de haber realizado la operación de INSERT en una operación distinta. Algo que haya que marcar es que @@IDENTITY puede tener problemas ya que al no trabajar en un ámbito determinado puede ser afectada por otras operaciones, si es que se realizan acciones con varias tablas.

    @@IDENTITY

    SCOPE_IDENTITY

     

    3 – Obtener Id desde Stored Procedure


    En este caso se analizara como obtener el id generado por la tabla, en donde la operacion de actualización esta dentro de un Stored Procedure, para esto se requerirán algunos cambios.

     

    [C#]

    private int InsertStoredprocedure(string nombre, string direccion, DateTime fechaNacimiento)
    {
        if (!ValidateForm())
            return 0;
    
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
            conn.Open();
    
            SqlCommand command = new SqlCommand("sp_ContactoInsert", conn);
            command.CommandType = CommandType.StoredProcedure;
    
            SqlParameter paramId = new SqlParameter("Id", SqlDbType.Int);
            paramId.Direction = ParameterDirection.Output;
            command.Parameters.Add(paramId);
    
            command.Parameters.AddWithValue("Nombre", nombre);
            command.Parameters.AddWithValue("Direccion", direccion);
            command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento);
    
            int rowsAffected = command.ExecuteNonQuery();
    
            if (rowsAffected > 0)
            {
                return Convert.ToInt32(command.Parameters["Id"].Value);
            }
            else
                return -1;
    
    
        }
    }

    [VB.NET]

    Private Function InsertStoredProcedure(nombre As String, direccion As String, fechaNacimiento As DateTime) As Integer
    	If Not ValidateForm() Then
    		Return 0
    	End If
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    		conn.Open()
    
    		Dim command As New SqlCommand("sp_ContactoInsert", conn)
    		command.CommandType = CommandType.StoredProcedure
    
    		Dim paramId As New SqlParameter("Id", SqlDbType.Int)
    		paramId.Direction = ParameterDirection.Output
    		command.Parameters.Add(paramId)
    
    		command.Parameters.AddWithValue("Nombre", nombre)
    		command.Parameters.AddWithValue("Direccion", direccion)
    		command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento)
    
    		Dim rowsAffected As Integer = command.ExecuteNonQuery()
    
    		If rowsAffected > 0 Then
    			Return Convert.ToInt32(command.Parameters("Id").Value)
    		Else
    			Return -1
               End If
    
           End Using
    
    End Function

    [Stored Procedure]

    ALTER PROCEDURE dbo.sp_ContactoInsert
    	
    	@Id int OUTPUT,
    	@Nombre varchar(50),
    	@Direccion varchar(50),
    	@FechaNacimiento DateTime
    	
    AS
    	INSERT INTO Contactos (NombreCompleto
                                      ,Direccion
                                      ,FechaNacimiento)
                                  VALUES (@Nombre, 
                                        @Direccion, 
                                        @FechaNacimiento);
    
                                                                            
        SELECT @Id = SCOPE_IDENTITY()     
                                 
    	RETURN

     

    En el código se han quitados todas las consulta reemplazándolo por el nombre del stored procedure.

    En este caso se hace uso de un parámetro que actuara como output para el valor generado dentro del Stored procedure, el cual será recuperado luego de la ejecución de la instrucción.

    [C#]
    [VB.NET]

    miércoles, 20 de enero de 2010

    [DataGridView] - Uso del DataGridViewComboBoxColumn

     

    Introducción


    Este articulo tendrá por objetivo mostrar como trabajar con las columna del tipo ComboBox que se encuentran dentro de una celda del datagridview.

     

    1- Definición de las columnas en tiempo de diseño


    Un paso importante es la definición de las columnas para ello en este caso explicare como hacerlo en tiempo de diseño y poder así controlar que datos visualizar.

    La opción para realizar esta operación se encuentra haciendo click con el botón derecho del mouse en el control DataGridView del formulario, visualizando una lista de ítems como se muestran en la imagen

    titulo1-imagen1

    Aquí puede seleccionarse dos opciones:

    - Agregar nuevas columnas a la grilla por medio de la opción “Add Column …” visualizándose el siguiente cuadro:

    titulo1-imagen2

    Como se observa en la imagen, puede definirse información rápida del tipo de columna que se quiere representar en la grilla.

    - Editar columnas existentes, mediante la opción “Edit Columns…” visualizando un cuadro como el siguiente

    titulo1-imagen3

    Lo interesante de esto es que uno podrá controlar que visualizar, en que orden, formato, tipo de columna y además todo desde un entorno visual

    La idea de estos diálogos es definir rápidamente las columnas mediante la opción de “Add Column…” para luego pasar a la edición mediante “Edit Columns…” y especificar propiedades que son importantes para que todo funcione.

    Una de las propiedades importantes es DataPropertyName, esta es fundamental para indicar que campo del origen de datos será asignado a esa columna. Al momento de cargar la grilla el valor de esa propiedad será tomado del origen de datos y asea un DataTable, o List<>, o cualquier otro que sea asignado y se mapeará con la columna usando esta propiedad.

    Sino se asigna información a la propiedad DataPropertyName ese campo no cargara datos alguno, lo cual puede ser interesante para campos calculados como veremos en ejemplo mas adelante.

    Es importante además que la propiedad AutoGenerateColumns sea establecida en false cuando se definan las columnas en tiempo de diseño, ya que en caso de no hacerlo se añadirá a la grilla las columnas generadas en runtime, lo cual no es deseable en este caso.

     

    2 – Asignación de los datos a la columna DataGridViewComboBoxColumn


    Empezaremos por cargar la información en un ejemplo simple, en este solo se tendrá un único campo del tipo combobox en el columna del DataGridView.

    En este caso se trata de una grilla de producto con sus precios unitarios, además cada producto pertenece a una marca especifica, que podrá seleccionarse entre las disponibles por el sistema.

    Los pasos a seguir serán:

    - se recuperara la columna que fue definida del tipo combobox a la cual se le asignaran los datos a desplegar, en este caso serán las Marcas disponibles.

    - y por ultimo se cargara la grilla con los datos de los Productos.

    El formulario que visualizaría será el siguiente:

    titulo2-imagen1

    La carga de los datos se ha realizado en el evento Load del formulario

    private void Form1_Load(object sender, EventArgs e)
    {
        //
        // Asigno los datos del combo de marcas
        //
        DataGridViewComboBoxColumn comboboxColumn = dataGridView1.Columns["Marca"] as DataGridViewComboBoxColumn;
    
        comboboxColumn.DataSource = ProductosDAL.GetAllMarca();
        comboboxColumn.DisplayMember = "Descripcion";
        comboboxColumn.ValueMember = "IdMarca";
    
        //
        // bindeo los datos de los productos a la grilla
        //
        dataGridView1.AutoGenerateColumns = false;
        dataGridView1.DataSource = ProductosDAL.GetAllProductos();
    
    }

    Es interesante notar que la primer parte se recupera la columna del DataGridView que define el combobox, la cual se programa como si se tratara de un combobox normal de .net, utilizando las propiedades DataSource, a la cual se le asignado el origen de datos con la lista de Marcas, el DisplayMember y ValueMember, para asignar que campo de la lista de Marcas será visible al usuario y cual será el id de cada ítem listado.

    El segunda parte carga los datos del DataGridView, tomando todos los producto a mostrar en la grilla.

    Además se especifica la propiedad AutoGenerateColumns definiendo que solo las columnas creadas en tiempo de diseño serán las visibles.

    A continuación se visualizara los método utilizados para cargar los datos en el DataGridView

    public static List<MarcaEntity> GetAllMarca()
    {
        string sql = @"SELECT IdMarca
                              ,Descripcion
                          FROM Marcas";
    
        List<MarcaEntity> list = new List<MarcaEntity>();
    
        using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            OleDbCommand command = new OleDbCommand(sql, conn);
    
            conn.Open();
    
            OleDbDataReader reader = command.ExecuteReader();
    
            while (reader.Read())
            {
                list.Add(LoadMarca(reader));
            }
    
            return list;
        }
    }
    
    private static MarcaEntity LoadMarca(IDataReader reader)
    {
        MarcaEntity marca = new MarcaEntity();
    
        marca.IdMarca = Convert.ToInt32(reader["IdMarca"]);
        marca.Descripcion = Convert.ToString(reader["Descripcion"]);
    
        return marca;
    }

     

    public static List<ProductoEntity> GetAllProductos()
    {
        string sql = @"SELECT [IdProducto]
                              ,[IdMarca]
                              ,[Descripcion]
                              ,[PrecioUnitario]
                          FROM Productos";
    
        List<ProductoEntity> list = new List<ProductoEntity>();
    
        using (OleDbConnection conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            OleDbCommand command = new OleDbCommand(sql, conn);
    
            conn.Open();
    
            OleDbDataReader reader = command.ExecuteReader();
    
            while (reader.Read())
            {
                list.Add(LoadProducto(reader));
            }
    
            return list;
        }
    
    
    }
    
    private static ProductoEntity LoadProducto(IDataReader reader)
    {
        ProductoEntity producto = new ProductoEntity();
    
        producto.IdProducto = Convert.ToInt32(reader["IdProducto"]);
    
        producto.IdMarca = Convert.ToInt32(reader["IdMarca"]);
        producto.Descripcion = Convert.ToString(reader["Descripcion"]);
    
        producto.PrecioUnitario = Convert.ToDecimal(reader["PrecioUnitario"]);
    
        return producto;
    }
         
    [C#]
    [VB.NET]

     

    3 – Realizar una operación al cambiar la selección del combo


    En este sección se analizara como poder trabajar con un combobox que ha sido agregado a la grilla.

    En este ejemplo se agrego un atributo nuevo al producto, referido a un descuento, según el el valor seleccionado del combo se realizara una operación con el mismo y el valor calculado será presentado en otra celda de la misma fila en donde se visualiza la información del producto.

    El formulario ahora tomara la siguiente forma

    titulo3-imagen1

    Además si se analiza las propiedades de la nueva columna se podrá apreciar que la propiedad DataPropertyName se le ha especificado el nombre del campo de la tabla de productos, y es el mismo nombre de la propiedad en la clase ProductoEntity.

    titulo3-imagen2

    A diferencia el ejemplo anterior en este solo se agregaron las línea para cargar los ítems de descuento

    private void Form1_Load(object sender, EventArgs e)
    {
        //
        // Asigno los datos del combo de marcas
        //
        DataGridViewComboBoxColumn comboboxColumn = dataGridView1.Columns["Marca"] as DataGridViewComboBoxColumn;
    
        comboboxColumn.DataSource = ProductosDAL.GetAllMarca();
        comboboxColumn.DisplayMember = "Descripcion";
        comboboxColumn.ValueMember = "IdMarca";
    
        //
        // Asigno los datos del combo de descuentos
        //
        DataGridViewComboBoxColumn dgccomboDescuento = dataGridView1.Columns["Descuento"] as DataGridViewComboBoxColumn;
    
        dgccomboDescuento.DataSource = ProductosDAL.GetAllDescuentos();
        dgccomboDescuento.DisplayMember = "Descripcion";
        dgccomboDescuento.ValueMember = "IdDescuento";
    
        //
        // bindeo los datos de los productos a la grilla
        //
        dataGridView1.AutoGenerateColumns = false;
        dataGridView1.DataSource = ProductosDAL.GetAllProductos();
    
    }

    Pero lo mas importante es ver como se trabaja con el combo, detectando un cambio en el ítem y realizando el calculo del descuento.

    private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
    
        if (dataGridView1.Columns[e.ColumnIndex].Name == "Descuento")
        {
            //
            // se obtiene el valor seleccionado en el combo
            //
            DataGridViewComboBoxCell combo = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex] as DataGridViewComboBoxCell;
    
            int idDescuento = Convert.ToInt32(combo.Value);
    
            //
            // se recupera por el id la info del descuento
            //
            DescuentoEntity descuento = ProductosDAL.GetDescuentosById(idDescuento);
    
            //
            // se calcula el descuento
            //
            DataGridViewCell cellPrecioUnitario = dataGridView1.Rows[e.RowIndex].Cells["Precio"];
            DataGridViewCell cellPrecioFinal = dataGridView1.Rows[e.RowIndex].Cells["PrecioFinal"];
    
            decimal valordescontar = (descuento.Porcentaje * Convert.ToDecimal(cellPrecioUnitario.Value)) / 100;
    
            cellPrecioFinal.Value = Convert.ToDecimal(cellPrecioUnitario.Value) - valordescontar;
        }
    }

    En este caso se hizo uso del evento CellValueChange, y dentro de este se realizando las operaciones para trabajar con el valor seleccionado del combo.

    - Primeramente se valida que siempre se trabaje con la columna que uno quiere operar, en este caso por el nombre se valida que sea la definida para el descuento. Debe recordarse que estos eventos también puede disparase para la edición de otras celdas para las demás columnas. Pero en este caso como el calculo solo interesa hacerlo en la columna de descuento es esta la verificada.

    - Como segundo pasos se toma el id del descuento seleccionado en el combo, debe recordarse que al momento de cargar los ítems del combo, fueron los id los que se unieron a la propiedad ValueMember.

    - Con el id del descuento se accede a la base de datos para recuperar la entidad del descuento  y con esta el valor del porcentaje que será usado en el calculo

    - Por ultimo se recuperas la información de las celdas que restan y se procede a realizar el calculo del porcentaje que será desplegado en la ultima columna de la grilla.

    Algo interesante a notar es que esta ultima columna que se hace llamar “Precio Final” no tiene valor alguno en la propiedad DataPropertyName, es por ello que no se carga ningún valor proveniente de la base de datos.

     

    [C#]
    [VB.NET]

    martes, 19 de enero de 2010

    [ADO.NET] – Parte 5 – Operaciones CRUD

     

    Introducción


    El objetivo de este articulo será el de demostrar con un ejemplo simple como hacer uso de ado.net para trabajar de forma simple con los datos, realizando operación de CRUD (Create, Read, Update and Delete) .

    Si bien esta aplicación de ejemplo hace uso del código desarrollado directamente en el propio formulario, se vera que el uso de funciones en la estructurada ordena el código dejándolo mas legible, permitiendo además mover la funcionalidad de lugar si en el futuro hiciera falta sin afectar el funcionamiento.

    La aplicación de ejemplo solo cuenta con un formularios, que observaran en esta imagen

    pantalla aplicacion

     

    1 – Obtener información


    Si se analiza el código se vera que es por medio de ingreso de un id valido en el campo que corresponde y mediante la acción de la tecla Enter se realiza la búsqueda.

    [C#]

    private void txtId_KeyPress(object sender, KeyPressEventArgs e)
    {
        if((int)e.KeyChar == (int)Keys.Enter)
        {
            //
            // Validaciones
            //
            errProvider.SetError(txtId, "");
    
            int Id = 0;
            if (!int.TryParse(txtId.Text, out Id))
            {
                errProvider.SetError(txtId, "El Id debe ser un valor numerico");
                return;
            }
    
            if (!Exists(Id))
            {
                errProvider.SetError(txtId,"El Id ingresado no existe.");
                return;
            }
    
            Obtener(Id);
        }
    
    }
    
    private void Obtener(int Id)
    {
        string sql = @"SELECT Id
                              ,NombreCompleto
                              ,Direccion
                              ,FechaNacimiento
                          FROM Contactos
                          WHERE Id = @Id";
    
    
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            SqlCommand command = new SqlCommand(sql, conn);
            command.Parameters.AddWithValue("Id", Id);
    
            conn.Open();
    
            SqlDataReader reader = command.ExecuteReader();
    
            if (reader.Read())
            {
                txtNombre.Text = Convert.ToString(reader["NombreCompleto"]);
                txtDireccion.Text = Convert.ToString(reader["Direccion"]);
                dtpFechaNanimiento.Value = Convert.ToDateTime(reader["FechaNacimiento"]);
            }
    
        }
    }
    
    private bool Exists(int Id)
    {
        string sql = @"SELECT COUNT(*)
                          FROM Contactos
                          WHERE Id = @Id";
    
    
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            SqlCommand command = new SqlCommand(sql, conn);
            command.Parameters.AddWithValue("Id", Id);
    
            conn.Open();
    
            int count = Convert.ToInt32(command.ExecuteScalar());
    
            if (count == 0)
                return false;
            else
                return true;
    
        }
    }

    [VB.NET]

       Private Sub txtId_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
    
           If AscW(e.KeyChar) = CInt(Keys.Enter) Then
               '
               ' Validaciones
               '
               errProvider.SetError(txtId, "")
    
               Dim Id As Integer = 0
               If Not Integer.TryParse(txtId.Text, Id) Then
                   errProvider.SetError(txtId, "El Id debe ser un valor numerico")
                   Return
               End If
    
               If Not Exists(Id) Then
                   errProvider.SetError(txtId, "El Id ingresado no existe.")
                   Return
               End If
    
               Obtener(Id)
           End If
    
       End Sub
    
    Private Sub Obtener(Id As Integer)
           Dim sql As String = "SELECT Id" & _
                                   ",NombreCompleto" & _
                                   ",Direccion" & _
                                   ",FechaNacimiento " & _
                               "FROM Contactos " & _
                               "WHERE Id = @Id"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Id", Id)
    
    		conn.Open()
    
    		Dim reader As SqlDataReader = command.ExecuteReader()
    
    		If reader.Read() Then
    			txtNombre.Text = Convert.ToString(reader("NombreCompleto"))
    			txtDireccion.Text = Convert.ToString(reader("Direccion"))
    			dtpFechaNanimiento.Value = Convert.ToDateTime(reader("FechaNacimiento"))
    
    		End If
    	End Using
    End Sub
    
    Private Function Exists(Id As Integer) As Boolean
           Dim sql As String = "SELECT COUNT(*) " & _
                               "FROM Contactos " & _
                               "WHERE Id = @Id"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Id", Id)
    
    		conn.Open()
    
    		Dim count As Integer = Convert.ToInt32(command.ExecuteScalar())
    
    		If count = 0 Then
    			Return False
    		Else
    			Return True
    
    		End If
    	End Using
    End Function	

    En esta sección de código se observara la recuperación de una entidad en base al id ingresado por el usuario.

    En el evento KeyPress del textbox que presenta al Id se detecta la presión de la tecla Enter como medio para indicar la búsqueda de un contacto.

    Entre las validaciones a realizar se verifica que el id ingresado sea numérico, también se valida que el registro exista en la base de datos, para ello es que se realiza la primer consulta que esta encapsulada en la función Exists(), esta tomara el id ingresado y ejecutara una consulta que devolverá el numero de registros que coincidan en este caso con el id ingresado, si el valor es cero entonces el registro no existe.

    El método ExecuteScalar() del objeto SqlCommand es ideal para estos casos ya que devolver el valor del primer registro y la primer columna, como en este caso solo hay un valor pues se hizo uso de la función SQL Count() devolverá dato simple.

    Una vez validada la existencia se procede a recuperar el registro, para ello la función de Obtener() cumplirá la misión, requiriendo que se le pase como parámetro el id del registro a obtener

    Como se observa este tiene una estructura similar a la Exists() solo que no usa el ExcecuteScalar(), sino que obtiene un SqlReader, usando ExecuteReader() del objeto SqlCommand.

    En este caso se sabe con anticipación que solo será devuelto un registro no hace falta hacer un loop por cada registro, por lo general se utiliza la sentencia “while”, en este caso el if cumple la función perfectamente para posicionar el cursor del reader a la primer posición, y poder realizar la lectura, entonces el if cumple básicamente dos funciones:

    - permite validad que hay registros que leer, si por algún fallo no hubiera registro al llegar al if pasaría por false.

    - al ejecutar el Read() del objeto SqlReader, posiciona el cursos para su lectura

    Por ultimo se iguala el valor de cada campo de la consulta, realizando la conversión de tipos y asignadla a los respectivos controles.

    Como se habrá observado hasta aquí y también se notaran en los ejemplos siguientes, los pasos para usar los objetos de ado.net son bastante repetitivos:

    1- Se crea un objeto SqlConnection para establecer la conexión hacia la base de datos

    2- Se crea el objeto SqlCommand, para establecer la consulta que se realizara, y se le asignan a este los parámetros si es que hacen falta

    3- en este punto existirá un bifurcación

    3a- Si se usa un DataReader, se podrá ejecutar directamente mediante el ExecuteReader()

    3b- Si se usa un DataSet o DataTable, será necesario utilizar un SqlDataAdapter, para realizar el Fill() del objeto a cargar con los datos provenientes de la consulta.

     

    2 - Insertar nuevo registro


    La operación de insertar registro tiene algunas particularidades que necesitas ser detalladas.

    Tanto la operación de insertar o actualizar será ejecutada mediante el mismo botón a nivel de interfaz del usuario, para ello es que se identifica si el id que se define existe o no (usando la función Exists(), antes comentada), esto podrá ser visto en el evento Click del botón Guardar.

    El primer punto que hay que aclarar es que en este primer ejemplo no se hace uso de un campo autonumérico (o Identity) para el id de la tabla. Es por ello que se deberá obtener por medio de código cual será el próximo numero identificador a utilizar.

    Para esta operación es que existe la función MaxId(), la cual hace uso de otra función SQL, en este caso MAX() que retornada el valor máximo existente en la tabla para un determinado campo que indiquemos. En este caso por tratarse de un valor simple, nuevamente se usa el ExecuteScalar() del SqlCommand.

    [C#]

            private void btnGuardar_Click(object sender, EventArgs e)
            {
                //
                // Validaciones
                //
                int Id = 0;
                if (!string.IsNullOrEmpty(txtId.Text))
                {
                    if (!int.TryParse(txtId.Text, out Id))
                    {
                        MessageBox.Show("El Id debe ser un valor numerico");
                        return;
                    }
                }
    
                if (Exists(Id))
                {
                    bool result = Update(Id, txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value);
    
                    if (result)
                        MessageBox.Show("El registro se ha actualizado correctamente.");
                }
                else
                {
                    bool result = Insert(txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value);
                    
                    if (result)
                        MessageBox.Show("El registro se ha insertado correctamente.");
                }
            }
    
            private bool Insert(string nombre, string direccion, DateTime fechaNacimiento)
            {
                string sql = @"INSERT INTO Contactos (Id
                                      ,NombreCompleto
                                      ,Direccion
                                      ,FechaNacimiento)
                                  VALUES (@Id, 
                                        @Nombre, 
                                        @Direccion, 
                                        @FechaNacimiento)";
    
    
                using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
                {
                    int NextId = MaxId() + 1;
    
                    SqlCommand command = new SqlCommand(sql, conn);
                    command.Parameters.AddWithValue("Id", NextId);
                    command.Parameters.AddWithValue("Nombre", nombre);
                    command.Parameters.AddWithValue("Direccion", direccion);
                    command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento);
    
                    conn.Open();
    
                    int rowsAffected = command.ExecuteNonQuery();
    
                    if (rowsAffected > 0)
                        return true;
                    else
                        return false;
    
                }
            }
    		
    
            private static int MaxId()
            {
                string sql = @"SELECT MAX(Id)
                                  FROM Contactos";
    
    
                using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
                {
    
                    SqlCommand command = new SqlCommand(sql, conn);
    
                    conn.Open();
    
                    return Convert.ToInt32(command.ExecuteScalar());
    
                }
            }

     

    [VB.NET]

    Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
    	'
    	' Validaciones
    	'
    	Dim Id As Integer = 0
    	If Not String.IsNullOrEmpty(txtId.Text) Then
    		If Not Integer.TryParse(txtId.Text, Id) Then
    			MessageBox.Show("El Id debe ser un valor numerico")
    			Return
    		End If
    	End If
    
    	If Exists(Id) Then
    		Dim result As Boolean = Update(Id, txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value)
    
    		If result Then
    			MessageBox.Show("El registro se ha actualizado correctamente.")
    		End If
    	Else
    		Dim result As Boolean = Insert(txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value)
    
    		If result Then
    			MessageBox.Show("El registro se ha insertado correctamente.")
    		End If
    	End If
    End Sub
    
    Private Function Insert(nombre As String, direccion As String, fechaNacimiento As DateTime) As Boolean
    	If Not ValidateForm() Then
    		Return False
    	End If
    
           Dim sql As String = "INSERT INTO Contactos (Id" & _
                                           ",NombreCompleto" & _
                                           ",Direccion" & _
                                           ",FechaNacimiento)" & _
                                       "VALUES (@Id, " & _
                                           "@Nombre, " & _
                                           "@Direccion, " & _
                                           "@FechaNacimiento)"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    		Dim NextId As Integer = MaxId() + 1
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Id", NextId)
    		command.Parameters.AddWithValue("Nombre", nombre)
    		command.Parameters.AddWithValue("Direccion", direccion)
    		command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento)
    
    		conn.Open()
    
    		Dim rowsAffected As Integer = command.ExecuteNonQuery()
    
    		If rowsAffected > 0 Then
    			ClearControls()
    			Return True
    		Else
    			Return False
    
    		End If
           End Using
    
    End Function
    
    Private Function MaxId() As Integer
           Dim sql As String = "SELECT MAX(Id)" & _
                               "FROM Contactos"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    
    		conn.Open()
    
    
    		Return Convert.ToInt32(command.ExecuteScalar())
    	End Using
    End Function

    En el método Insert() se observara que la consulta hace uso de parámetros para asignar los valores, es por ello que el objeto SqlCommand posee la propiedad Parameters, y en esta se hace uso de la forma rápida por medio de método AddWithValue(), definiendo el nombre del parámetro y el valor.

    Además se hace uso de otra del las funciones del SqlCommand el ExecuteNonQuery(), este es útil ante consultas SQL de actualización, o sea no retornan un conjunto de datos. Este método si devolver un valor entero que indicara la cantidad de registros afectados, si este devuelve cero quiere decir que no se actualizaron registro, en este ejemplo esto indicaría un fallo en la aplicación ya que siempre al menos un registro debe ser insertado de forma correcta.

     

    3 – Actualizar registro existente


    La operación de actualización es bastante similar a la operación anterior de insert, salvando la diferencia que cambia completamente la sentencia sql utilizada.

    Al igual que con el insert, se hacen uso de parámetros para asignar los valores a la consulta, salvo que este necesita contar con el id del registro que se quiere actualizar y será utilizado en la sección del WHERE que permitirá identificar que registro afectar con los cambios.

    [C#]

            private void btnGuardar_Click(object sender, EventArgs e)
            {
                //
                // Validaciones
                //
                int Id = 0;
                if (!string.IsNullOrEmpty(txtId.Text))
                {
                    if (!int.TryParse(txtId.Text, out Id))
                    {
                        MessageBox.Show("El Id debe ser un valor numerico");
                        return;
                    }
                }
    
                if (Exists(Id))
                {
                    bool result = Update(Id, txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value);
    
                    if (result)
                        MessageBox.Show("El registro se ha actualizado correctamente.");
                }
                else
                {
                    bool result = Insert(txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value);
                    
                    if (result)
                        MessageBox.Show("El registro se ha insertado correctamente.");
                }
            }
    		
    		
            private static bool Update(int id, string nombre, string direccion, DateTime fechaNacimiento)
            {
    
                string sql = @"UPDATE Contactos SET 
                                      NombreCompleto = @Nombre
                                      ,Direccion = @Direccion
                                      ,[FechaNacimiento] = @FechaNacimiento
                                WHERE Id = @Id";
    
    
                using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
                {
    
                    SqlCommand command = new SqlCommand(sql, conn);
                    command.Parameters.AddWithValue("Id", id);
                    command.Parameters.AddWithValue("Nombre", nombre);
                    command.Parameters.AddWithValue("Direccion", direccion);
                    command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento);
    
                    conn.Open();
    
                    int rowsAffected = command.ExecuteNonQuery();
    
                    if (rowsAffected > 0)
                        return true;
                    else
                        return false;
    
                }
            }

     

    [VB.NET]

    Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
    	'
    	' Validaciones
    	'
    	Dim Id As Integer = 0
    	If Not String.IsNullOrEmpty(txtId.Text) Then
    		If Not Integer.TryParse(txtId.Text, Id) Then
    			MessageBox.Show("El Id debe ser un valor numerico")
    			Return
    		End If
    	End If
    
    	If Exists(Id) Then
    		Dim result As Boolean = Update(Id, txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value)
    
    		If result Then
    			MessageBox.Show("El registro se ha actualizado correctamente.")
    		End If
    	Else
    		Dim result As Boolean = Insert(txtNombre.Text, txtDireccion.Text, dtpFechaNanimiento.Value)
    
    		If result Then
    			MessageBox.Show("El registro se ha insertado correctamente.")
    		End If
    	End If
    End Sub
    
    Private Function Update(id As Integer, nombre As String, direccion As String, fechaNacimiento As DateTime) As Boolean
    
           If Not ValidateForm() Then
               Return False
           End If
    
           Dim sql As String = "UPDATE Contactos SET " & _
                                   "NombreCompleto = @Nombre" & _
                                   ",Direccion = @Direccion" & _
                                   ",[FechaNacimiento] = @FechaNacimiento " & _
                               "WHERE Id = @Id"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Id", id)
    		command.Parameters.AddWithValue("Nombre", nombre)
    		command.Parameters.AddWithValue("Direccion", direccion)
    		command.Parameters.AddWithValue("FechaNacimiento", fechaNacimiento)
    
    		conn.Open()
    
    		Dim rowsAffected As Integer = command.ExecuteNonQuery()
    
    		If rowsAffected > 0 Then
    			Return True
    		Else
    			Return False
    
    		End If
    	End Using
    End Function

    4 – Eliminar registro


    Eliminar un registro es una operación bastante simple, con solo proporcionar el identificador para definir la sección del WHERE en la sintaxis de eliminación es suficiente.

    En el ejemplo se realiza una operación previa que valida que id exista, pero también se podría haber obviado esta validación utilizando el valor proporcionado por el usuario.

    [C#]

    private void btnEliminar_Click(object sender, EventArgs e)
    {
        //
        // Validaciones
        //
        errProvider.SetError(txtId, "");
    
        int Id = 0;
        if (!int.TryParse(txtId.Text, out Id))
        {
            errProvider.SetError(txtId, "El Id debe ser un valor numerico");
            return;
        }
    
        if (!Exists(Id))
        {
            errProvider.SetError(txtId, "El Id ingresado no existe.");
            return;
        }
    
        //
        // Elimino contacto
        //
        bool result = Delete(Id);
    
        if (result)
        {
            MessageBox.Show("El registro se ha eliminado correctamente.");
            ClearControls();
        }
        
    }
    
    public bool Delete(int Id)
    {
        string sql = @"DELETE FROM Contactos 
                       WHERE Id = @Id";
    
    
        using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
        {
    
            SqlCommand command = new SqlCommand(sql, conn);
            command.Parameters.AddWithValue("Id", Id);
    
            conn.Open();
    
            int rowsAffected = command.ExecuteNonQuery();
    
            if (rowsAffected > 0)
                return true;
            else
                return false;
    
        }
    }

    [VB.NET]

    Private Sub btnEliminar_Click(sender As Object, e As EventArgs)
    	'
    	' Validaciones
    	'
    	errProvider.SetError(txtId, "")
    
    	Dim Id As Integer = 0
    	If Not Integer.TryParse(txtId.Text, Id) Then
    		errProvider.SetError(txtId, "El Id debe ser un valor numerico")
    		Return
    	End If
    
    	If Not Exists(Id) Then
    		errProvider.SetError(txtId, "El Id ingresado no existe.")
    		Return
    	End If
    
    	'
    	' Elimino contacto
    	'
    	Dim result As Boolean = Delete(Id)
    
    	If result Then
    		MessageBox.Show("El registro se ha eliminado correctamente.")
    		ClearControls()
    	End If
    
    End Sub
    
    
    Public Function Delete(Id As Integer) As Boolean
           Dim sql As String = "DELETE FROM Contactos " & _
                               "WHERE Id = @Id"
    
    
    	Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString())
    
    		Dim command As New SqlCommand(sql, conn)
    		command.Parameters.AddWithValue("Id", Id)
    
    		conn.Open()
    
    		Dim rowsAffected As Integer = command.ExecuteNonQuery()
    
    		If rowsAffected > 0 Then
    			Return True
    		Else
    			Return False
    
    		End If
    	End Using
    End Function

    Acerca de los ejemplos


    Para poder ejecutar correctamente los ejemplo necesitar Visual Studio 2008 y Sql Server Express instalado y corriendo localmente.

     

    [C#]
    [VB.NET]

    jueves, 14 de enero de 2010

    WinForms – Cambio del color del Formulario – Usando el archivo de configuracion

     

    Introducción

    El objetivo del ejemplo es mostrar como por medio de un combobox en el formulario cambiarle el color de una forma simple

    Además demuestra como hacer uso del My.Settings para poder conservar la información de configuración del usuario, guardando el color seleccionado

    [vb.net] Descarga

    domingo, 3 de enero de 2010

    [DataGridView] Pasaje de información entre grids en distintos formulario

     

    Introducción


    Este artículo es consecuencia de otros descriptos con anterioridad

    C# – Comunicar formularios de forma desacoplada

    Como se habrá visto comunicar formularios de forma desacoplada puede no se tan simple alguna veces y es justamente cuando se hace uso de información entre grillas que la situación puede ser algo mas compleja

    Este ejemplo intenta demostrar como realizar la comunicación y desde un formulario hijo pasar información de ítems seleccionados al un formulario padre o quien realiza la llamada.

     

    Definición de la Interfaz


    Para la comunicación eficiente de los formulario, se crearan un contrato que permitirá enlazarlos con el mínimo acoplamiento entre ellos.

    interface IAddItem
    {
        void AddNewItem(DataGridViewRow row);
    }

    Como se observa el método que tomara el retorno de la selección del ítem define un DataGridViewRow, o sea un registro completo seleccionado en la grilla del formulario hijo.

     

    Formulario Padre


    Como se observara el formulario padre que realizara la apertura debe implementar la interfaz IAddItem definida en el punto anterior.

     

    public partial class Form1 : Form, IAddItem
    {
        public Form1()
        {
            InitializeComponent();
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
            Form2 formAdd = new Form2();
            formAdd.Show(this);
    
        }
    
        #region IAddItem Members
    
        public void AddNewItem(DataGridViewRow row)
        {
            string item = row.Cells["item"].Value.ToString();
            string desc = row.Cells["Desc"].Value.ToString();
    
            this.dataGridView1.Rows.Add(new []{ item, desc }); 
    
        }
    
        #endregion
    
    
    }

     

    Es importante destacar algunas líneas:

    - Línea 11: es el punto en donde se realiza la apertura del forma hijo, y es allí donde se le indica quien es el padre o quien esta realizando al apertura del formulario, esto se esta indicando al hacer uso del “this” en el parámetro del método Show()

    - Líneas 17-24: en estas línea de código se estará tomando la fila que se retorna de la selección, se recupera cada valor y se arma el nuevo registro, en este punto en caso de que fuera necesario se podría realizar cálculos o modificar los datos, para ser luego insertados en la grilla de ítems seleccionados.

     

    Formulario Hijo


    Este formulario contiene la grilla con los ítems que pueden ser seleccionados, los cuales a modo de ejemplo fueron creados manualmente en un DataTable.

    El punto clave aquí es el botón que envía el registro seleccionado, del datagridview del formulario hijo al formulario padre que realizo la llamada:

    private void button1_Click(object sender, EventArgs e)
    {
        DataGridViewRow row = this.dataGridView1.SelectedRows[0] as DataGridViewRow;
    
    
        IAddItem parent = this.Owner as IAddItem;
        parent.AddNewItem(row);
    
        this.Close();
    }

    Como se observa se toma la fila seleccionada, y acto seguido se llamada al método de la interfaz del formulario que realizo la llamada.

    El parámetro “this” enviado en el método Show() es justamente la propiedad Owner del formulario hijo, y al implementar la interfaz este puede ser casteado a el tipo IAddItem sin problemas, para luego invocar al método que define.

     

    [C#] 
    [VB.NET]