Colabora
 

LINQ + Vb .Net

Formulario de Pedidos usando LINQ y VB .Net

 

Fecha: 17/Feb/2008 (16-02-08)
Actualizado: 30/Jul/200/
Autor: José Paz Aliaga

 


Introducción

Este ejemplo nos muestra un Formulario de Pedido usando LINQ, por cuestiones de Documentación, es posible que NO ESTE USANDO CORRECTAMENTE todo el potencial de LINQ, asi que espero sugerencias para mejoras, asi mismo debo decirles que hago un abuso de métodos a nivel de formulario las cuales estarían mejor dentro de clases pero creo que para un inicio esto esta bien y cumple con su objetivo.

 

Modelo de Base de Datos

Bueno me di la libertad de ponerles el esquema de mi base de datos de pruebas que suelo usar para este tipo de ejemplos.

Obviamente los Tipos de Datos funciones y demas, estan dentro del archivo para descargar, asi que no se preocupen, por cierto me olvidaba, estoy usando Ms SQL Server Express.

 

Agregando el Objeto LINQ to SQL

Lo primero que debemos hacer es presionar la siguiente combinacion de teclas Ctrl + Shift + A, inmediatamente nos aparecera la siguiente ventana en la cual seleccionaremos la opcion el Elemento LINQ to SQL classes, a la cual le pondremos de nombre "bdVentasLinq.dbml".

A continuación lo que debemos hacer es arrastrar todos Nuestras Tablas de la Base de Datos hacia el Elemento LINQ to SQL, el cual debera quedar de la siguiente forma.

 

Agregando Clase clsDetalleVenta

A continuacióna gregamos un elemento clase y le damos el nombre de clsDetalleVenta, esta clase nos permitira almacenar el Detalle de un Pedido en especifico.

Public Class clsDetalleVenta
    Private idProducto As String
    Private nombreProducto As String
    Private cantidad As Integer
    Private precio As Double
    Private subtotal As Double
    Private stockArticulo As Double

    Public Property _stockArticulo() As Integer
        Get
            Return stockArticulo
        End Get
        Set(ByVal value As Integer)
            stockArticulo = value
        End Set
    End Property

    Public Property _idProducto() As String
        Get
            Return idProducto
        End Get
        Set(ByVal value As String)
            idProducto = value
        End Set
    End Property

    Public Property _nombreProducto() As String
        Get
            Return nombreProducto
        End Get
        Set(ByVal value As String)
            nombreProducto = value
        End Set
    End Property

    Public Property _cantidad() As Integer
        Get
            Return cantidad
        End Get
        Set(ByVal value As Integer)
            cantidad = value
        End Set
    End Property

    Public Property _precio() As Double
        Get
            Return precio
        End Get
        Set(ByVal value As Double)
            precio = value
        End Set
    End Property

    Public Property _subtotal() As Double
        Get
            Return subtotal
        End Get
        Set(ByVal value As Double)
            subtotal = value
        End Set
    End Property
End Class

Y agregamos un modulo donde metere algunas funciones rutinarias, tales como Cuadros de Dialogo y otras cosas que se me ocurran, a este modulo le pondremos como nombre mensajes.

Module mensajes
    Public Sub msginformacion(ByVal mensaje As String)
        MessageBox.Show(mensaje, "Sis. Ventas", MessageBoxButtons.OK, _
                        MessageBoxIcon.Information)
    End Sub

    Public Sub msgerror(ByVal mensaje As String)
        MessageBox.Show("Ocurrio el Error: " & Environment.NewLine _
                        & mensaje, "Sis. Ventas", _
                        MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Sub

    Public Sub msgpregunta(ByVal mensaje As String)
        MessageBox.Show(mensaje, "Sis. Ventas", _
                        MessageBoxButtons.OK, MessageBoxIcon.Question)
    End Sub

End Module

Diseñando el Programa

Diseño del Formulario de Busqueda de Clientes

Ya sabemos que debemos presionar Ctrl + Shift + A, y acontinuacion agregamos un elemento Dialog y le ponemos de nombre frmClientes, el cual luego de agregar los siguientes controles.

  • 1 DataGridView
  • 1 Label
  • 1 TextBox

No menciono los 2 Button ya que etos viene por defecto en el Objeto Dialog, luego de agregados los controles deberia quedar algo asi...

Codificando Nuestro Formulario frmClientes

DataContext:
Se encarga de traducir las peticiones de objetos en consultas SQL (ver el esquema de la arquitectura de LINQ) y devolver los resultados como objetos.

Imports System.Windows.Forms

Public Class frmClientes
    'Instanciamos un objeto de la clase bdVentasLinqDataContext
    Dim bd As New bdVentasLinqDataContext
    'Declaro una variable ID de Forma Publica
    Public id As String
    Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles OK_Button.Click
        'Extraigo el indice del elemento Seleccionado en el DataGridView
        Dim row As Integer = Me.DataGridView1.CurrentRow.Index
        'Extraigo el elemento situado en la columna DNI_cli
        id = Me.DataGridView1.Item("DNI_cli", row).Value.ToString
        'Se establece como OK el resultado del Cuadro de Dialogo
        Me.DialogResult = System.Windows.Forms.DialogResult.OK
        'Cierro el Formulario
        Me.Close()
    End Sub

    Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Cancel_Button.Click
        'Se establece como CANCEL el resultado del Cuadro de Dialogo
        Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
        'Cierro El formulario
        Me.Close()
    End Sub

    Private Sub Dialog1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles MyBase.Load
        'Este codigo usa una consulta LINQ para obtener una secuencia
        'para obtener una secuencia IEnumerable de objetos Clientes

        Dim query = From cli In bd.Clientes _
                    Select cli.DNI_cli, cli.ape_cli, cli.nom_cli _
                    Order By ape_cli Ascending
        Me.DataGridView1.DataSource = query.ToList
    End Sub

    Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles TextBox1.TextChanged
        'Este codigo usa una consulta LINQ para obtener una secuencia
        'para obtener una secuencia IEnumerable de objetos Clientes
        'los cuales concuerden con el texto contenido en la caja de
        'texto TextBox1 la cual se concatena con el caracter *
        'el que nos proporciona la funcionalidad del % en T-SQL
        'Todo esto con la finalidad de ser utilizado con la expresion
        'LIKE de nuestra consulta LINQ

        Dim cadena As String
        cadena = Me.TextBox1.Text & "*"
        Dim query = From cli In bd.Clientes _
                    Where cli.ape_cli Like cadena _
                    Select cli.DNI_cli, cli.ape_cli, cli.nom_cli _
                    Order By ape_cli Ascending
        'Se asocia como Origen de Datos al DataGridView1
        'el resultado de la consulta LINQ
        Me.DataGridView1.DataSource = query.ToList
    End Sub
End Class

 

Diseño del Formulario de Busqueda de Productos

Ya sabemos que debemos presioanr Ctrl + Shift + A, y acontinuacion agregamos un elemento Dialog y le ponemos de nombre frmProductos, el cual luego de agregar los siguientes controles.

  • 1 DataGridView
  • 1 Label
  • 1 TextBox

No menciono los 2 Button ya que etos viene por defecto en el Objeto Dialog, luego de agregados los controles deberia quedar algo asi...

Codificando Nuestro Formulario frmClientes

Imports System.Windows.Forms

Public Class frmProductos
    'Todo este proceso es muy similar al del Formulario frmCLientes
    Dim bd As New bdVentasLinqDataContext
    Public id As String
    Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles OK_Button.Click
        Dim row As Integer = Me.DataGridView1.CurrentRow.Index
        id = Me.DataGridView1.Item("IdArticulo", row).Value.ToString
        Me.DialogResult = System.Windows.Forms.DialogResult.OK
        Me.Close()
    End Sub

    Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Cancel_Button.Click
        Me.DialogResult = System.Windows.Forms.DialogResult.Cancel
        Me.Close()
    End Sub

    Private Sub frmProductos_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles MyBase.Load
        Dim query = From art In bd.Articulos _
                    Select art.IdArticulo, art.Modelo, art.pre_arti, art.stock_arti _
                    Order By Modelo Ascending
        Me.DataGridView1.DataSource = query.ToList
    End Sub

    Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) 
    Handles TextBox1.TextChanged
        Dim cadena As String
        cadena = Me.TextBox1.Text & "*"
        Dim query = From art In bd.Articulos _
                    Where art.Modelo Like cadena _
                    Select art.IdArticulo, art.Modelo, art.pre_arti, art.stock_arti _
                    Order By Modelo Ascending
        Me.DataGridView1.DataSource = query.ToList
    End Sub
End Class

 

Diseño del Formulario de Pedidos

Agrgamos un Formulario, al cual le agregaremos los siguientes controles:

  • 6 TextBox
  • 6 Label
  • 1 DataGridView
  • 8 Button

Y deberian quedar de la siguiente forma:

Codificando Nuestro Formulario frmClientes

Definimos de Manera Global las Siguientes Variables

    'Definir el objeto BD de la clase bdVentasLinqDataContext
    Dim bd As New bdVentasLinqDataContext
    'Definir un objeto de la clase clsDetalleVenta
    Dim objDetVta As clsDetalleVenta
    Dim nombreArticulo As String
    Dim idArticulo As String
    Dim precioArticulo As Double
    Dim DNI As String
    Dim stockArticulo As Integer
    'Definir una LISTA de Objetos clsDetalleVenta
    Dim ArrDetVta As New List(Of clsDetalleVenta)

A continuacion codificaremos el Button que se encuentra al lado del TextBox DNI

    Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Button1.Click
        'Instanciamos un Objeto del Formulario frmClientes
        Dim dialog As New frmClientes
        'Mostramos el Formulario
        dialog.ShowDialog()
        'Si el resultado del Formulario es OK
        '----------------------------------------------------
        'Si el resultado del Formulario es diferente a OK
        'Nos mostrara un mensaje en el que nos indique que no 
        'hay ningun Cliente Seleccionado
        If dialog.DialogResult = Windows.Forms.DialogResult.OK Then
            'Usamos una instruccion LINQ para seleccionar a un Cliente en Especifico
            'El cual lo recuperamos de la variable ID que fue declarada como PUBLICA
            'En el Formulario frmClientes
            Dim query = From qCliente In bd.Clientes _
                        Where qCliente.DNI_cli = dialog.id _
                        Select qCliente.DNI_cli, qCliente.ape_cli, qCliente.nom_cli
            'Cargamos los valores del resultado de la consulta 
            'LINQ a los TextBox correspondientes
            Me.txtDNI.Text = query.ToList.Item(0).DNI_cli.ToString
            Me.txtNombre.Text = query.ToList.Item(0).nom_cli & " " & _
                            query.ToList.Item(0).ape_cli
            DNI = query.ToList.Item(0).DNI_cli.ToString
        Else
            mensajes.msginformacion("No ha seleccionado Ningun Cliente")
            Me.txtDNI.Text = ""
            Me.txtNombre.Text = ""
            DNI = ""
        End If
    End Sub

Codificaremos el Button que se encuentra al lado del TextBox Nombre del Producto

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Button2.Click
        'Se Aplica el mismo Procedimiento que en el Evento Anterior
        'para seleccionar un Articulo
        Dim dialog As New frmProductos
        dialog.ShowDialog()
        If dialog.DialogResult = Windows.Forms.DialogResult.OK Then
            Dim query = From qArti In bd.Articulos _
                  Where qArti.IdArticulo = dialog.id _
                  Select qArti.IdArticulo, qArti.Modelo, qArti.pre_arti, qArti.stock_arti

            Me.txtNombreProducto.Text = query.ToList.Item(0).Modelo.ToString
            Me.txtPrecioProducto.Text = query.ToList.Item(0).pre_arti.ToString("C")
            nombreArticulo = query.ToList.Item(0).Modelo.ToString
            idArticulo = query.ToList.Item(0).IdArticulo.ToString
            precioArticulo = Convert.ToDouble(query.ToList.Item(0).pre_arti.ToString)
            stockArticulo = Convert.ToInt32(query.ToList.Item(0).stock_arti.ToString)
            txtCantidad.Focus()
        Else
            mensajes.msginformacion("No ha seleccionado Ningun Producto")
            Me.txtNombreProducto.Text = ""
            Me.txtPrecioProducto.Text = ""
            nombreArticulo = ""
            idArticulo = ""
            precioArticulo = ""
        End If
    End Sub

Ahora programaremos el Boton Agregar

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        'Verificamos que el Nombre del Producto, Precio y Cantidad
        'NO esten vacios
        If txtNombreProducto.Text <> "" And txtPrecioProducto.Text <> "" And txtCantidad.Text <> "" Then
            'Verificamos que el Stock del Artículo Seleccionado
            'NO sea menor que el solicitado
            If Convert.ToInt32(stockArticulo) < Convert.ToInt32(txtCantidad.Text) Then
                'Mostramos una Alerta si la verificacion Anterior es Verdadera
                mensajes.msginformacion("La Cantidad Requerida es " & _
                                        "Superior a la del Stock")
                'Como la Cantidad Solicitada es Mayor a lo que se Tiene en Stock
                'Entonces se pregunta si se kiere establecer el STOCk Actual
                'Como la nueva cantidad requerida
                If MessageBox.Show("Desea Establecer el Stock Actual como la " & _
                                   "Nueva Cantidad Requerida", "Sis. Ventas", _
                                   MessageBoxButtons.YesNo) = _
                                   Windows.Forms.DialogResult.Yes Then
                    'Establecemos la Nueva Cantidad
                    txtCantidad.Text = stockArticulo
                    'Inicializamos el Objeto de la Clase clsDetalleVenta
                    objDetVta = New clsDetalleVenta
                    'A continuacion llenamos los valores 
                    'Necesarios para este Objeto
                    objDetVta._cantidad = Convert.ToInt32(txtCantidad.Text)
                    objDetVta._idProducto = idArticulo
                    objDetVta._nombreProducto = nombreArticulo
                    objDetVta._precio = precioArticulo
                    objDetVta._stockArticulo = stockArticulo
                    objDetVta._subtotal = precioArticulo * Convert.ToInt32(txtCantidad.Text)
                    'En esta parte Verificamos si es que el Aritculo solicitado
                    'Se encuentra ya en la Lista de Pedidos, de ser Falso
                    'Lo Agregamos a Nuestros LIST de DetalleVenta
                    If buscarDetVta(ArrDetVta, objDetVta) = False Then
                        'Agregamos nuestro Pedido al LIST de DetalleVenta
                        ArrDetVta.Add(objDetVta)
                    End If
                    'Asociamos el LIST de DetalleVenta como origen de 
                    'Datos del DataGridView
                    Me.DataGridView1.DataSource = ArrDetVta
                Else
                    'Si no se desea establecer el Stock Actual como
                    'La nueva cantidad a Solicitar se vuelve al Formulario
                    'Para escoger un Articulo
                    Button2_Click(Nothing, Nothing)
                End If
            Else
                'Como la Cantidad Requerida es Menor al Stock Actual
                'inicializamos el Objeto de la clase clsDetalleVenta
                objDetVta = New clsDetalleVenta
                'A continuacion llenamos los valores 
                'Necesarios para este Objeto
                objDetVta._cantidad = Convert.ToInt32(txtCantidad.Text)
                objDetVta._idProducto = idArticulo
                objDetVta._nombreProducto = nombreArticulo
                objDetVta._precio = precioArticulo
                objDetVta._stockArticulo = stockArticulo
                objDetVta._subtotal = precioArticulo * Convert.ToInt32(txtCantidad.Text)
                'En esta parte Verificamos si es que el Aritculo solicitado
                'Se encuentra ya en la Lista de Pedidos, de ser Falso
                'Lo Agregamos a Nuestros LIST de DetalleVenta
                If buscarDetVta(ArrDetVta, objDetVta) = False Then
                    'Agregamos nuestro Pedido al LIST de DetalleVenta
                    ArrDetVta.Add(objDetVta)
                End If
                'Asociamos el LIST de DetalleVenta como origen de 
                'Datos del DataGridView
                Me.DataGridView1.DataSource = ArrDetVta.ToList
            End If
        End If
        'Una vez Agregado un Nuevo Detalle para el Pedido
        'Limpiamos los textBox
        Me.limpiar_producto()
        Button2.Focus()
        'Calculamos el Total del Pedido
        Me.calcular_total()
    End Sub

Metodo para veirifcar si es que ya se Solicito un Articulo Anteriomente

    Public Function buscarDetVta(ByVal array As List(Of clsDetalleVenta), _
ByVal
objDV As clsDetalleVenta) As Boolean 'Funcion para verificar si es que ya se solicito un Articulo For Each obj As clsDetalleVenta In array If obj._idProducto = objDV._idProducto Then Dim nCantidad As Integer 'De encontrarse el Arituclo en la Lista de Pedidos 'Lo que se hace es incrementar la Cnatidad Solicitada 'Mas la cantidad Pedida Anteriormente nCantidad = obj._cantidad + objDV._cantidad If nCantidad <= obj._stockArticulo Then obj._cantidad = nCantidad obj._subtotal = obj._precio * obj._cantidad Else mensajes.msgerror("El Stock Actual es insuficiente para el Requerimiento") End If Return True End If Next Return False End Function

Metodo para Limpiar los TextBox Asociados al Articulo

    Sub limpiar_producto()
        'Metodo para limpiasr TextBox
        Me.txtCantidad.Clear()
        Me.txtNombreProducto.Clear()
        Me.txtPrecioProducto.Clear()
    End Sub

Metodo para Calcular el Total del Pedido

    Sub calcular_total()
        'Metodo para Calcular El Total del Pedido
        Dim sum As Double
        For Each i As clsDetalleVenta In ArrDetVta
            sum += i._subtotal
        Next
        Me.txtTotal.Text = sum.ToString("C")
    End Sub

Ahora Programaremos el Boton Eliminar

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Button4.Click
        'Removemos un Articulo seleccionado en el DataGridView
        ArrDetVta.RemoveAt(Me.DataGridView1.CurrentRow.Index)
        'Vovlemos a Asociar nuestro LIST como origen de Datos del DataGridView
        Me.DataGridView1.DataSource = ArrDetVta.ToList
        'Volvemos a Calcular el Total del Pedido
        Me.calcular_total()
    End Sub

Programamos el Boton Cancelar

    Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles Button5.Click
        limpiar_cliente()
        limpiar_producto()
        limpiar_venta()
        Me.calcular_total()
    End Sub

Metodo para limpiar los Controles asociados al Cliente

    Sub limpiar_cliente()
        'Metodo para limpiar los textBox Asociados al Cliente
        Me.txtDNI.Clear()
        Me.txtNombre.Clear()
    End Sub

Metodo para borrar el Detalle del Pedido

    Sub limpiar_venta()
        'Limpiamos el LIST usando su Metodo CLEAR
        ArrDetVta.Clear()
        'Vovlemos a Asociar nuestro LIST como origen de Datos del DataGridView
        Me.DataGridView1.DataSource = ArrDetVta.ToList
    End Sub

Y por Ultimo y quizas lo mas importante, la codificacion del Boton Grabar

    Private Sub btngrabar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles btngrabar.Click
        'Aprovechando que nuestra Base de Datos
        'Esta relacionada, usaremos a LINQ para que 
        'Nos haga las Inserciones y actualizaciones
        'Que necesitamos
        Try
            'La siguiente Expresion me devuelve un Objeto Cliente
            'buscandolo por su id es decir por el Campo DNI_Cli
            Dim C As Cliente = bd.Clientes.Single(Function(p) p.DNI_cli = Me.txtDNI.Text)
            'Creo un nuevo Encabezado de Boleta
            'Y procedo a Inicializarlo
            '-------------------------------------------------
            Dim EB As New EncabezadoBoleta
            EB.Fech_Bol = Date.Now
            EB.Total_Bol = CDec(Me.txtTotal.Text)
            EB.Anulado = "F"
            EB.Cliente = C
            EB.IdCliente = C.IdCliente
            '--------------------------------------------------
            '
            '
            'Ya que tengo todo el Detalle del Pedido en un LIST
            'Aprovechare este detalle para generar mis Objetos Pedido
            For Each obj As clsDetalleVenta In ArrDetVta
                'Instacion un Objeto Articulo Seleccionandolo
                'Por el Campo IdArticulo
                Dim Art As Articulo = _
                bd.Articulos.Single(Function(a) a.IdArticulo = obj._idProducto.ToString())
                'Creo un nuevo Detalle de Boleta 
                'Y procedo a Inicializarlo
                Dim DB As New DetalleBoleta
                DB.Articulo = Art
                DB.Cant_det = obj._cantidad
                DB.Importe = obj._subtotal
                DB.Prec_det = obj._precio
                'Agrego el Detalle de Boleta a mi Encabezado de Boleta
                EB.DetalleBoletas.Add(DB)
            Next
            'Aqui Actualizo la Base De Datos con el Metodo SubmitChanges
            bd.SubmitChanges()
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

 

Conclusiones

Como vemos, el modelo de programación que hemos usado para hacer todo esto es realmente limpio y orientado a objetos.


Espacios de nombres usados en el código de este artículo:

System.Data
System.Linq
System.Xml.Linq

 



Compromiso del autor del artículo con el sitio del Guille:

Lo comentado en este artículo está probado (y funciona) con la siguiente configuración:

El autor se compromete personalmente de que lo expuesto en este artículo es cierto y lo ha comprobado usando la configuración indicada anteriormente.

En cualquier caso, el Guille no se responsabiliza del contenido de este artículo.

Si encuentras alguna errata o fallo en algún link (enlace), por favor comunícalo usando este link:

Gracias.


Código de ejemplo (comprimido):

 

Fichero con el código de ejemplo: jpa_LINQVBNet_Ventas.zip - 260.00 KB

Nota del Guille 30/Jul/2008:
He actualizado el ZIP ya que quité el fichero .LDF y sin ese fichero puede que no te funcione al ejecutar.

(MD5 checksum: 3951C1E1A2B92B2F7AB382187CDA8AFC)

 


Ir al índice principal de el Guille