Filtrar y buscar datos de un DataGridView

Problema: Tengo un DataGridView lleno con datos obtenidos de un DataTable, el cual está enlazado al mismo, y necesito que se muestren solo una cantidad determinada de esos registros, como cuando se utiliza el filtro en Microsoft Excel, sin necesariamente borrar dichos datos.
Solución: Utilizar el BindingSource.

Una de las mejores formas de filtrar datos que ya están cargados en un DataGridView es usando el BindingSource, sirve para mostrar una cantidad definida de registros o para reducir la cantidad de datos a procesar, todo depende de cómo se lo utilice. Para un buen ejemplo del su utilización, se puede encontrar aquí: Filtar un control DataGridView

Por mi parte, resumiré las partes importantes, intentando explicar lo más relevante.

Creé un Procedimiento en el cual tengo un DataGridView con datos enlazados y un TextBox en donde el usuario escribirá lo que se desea filtrar, por ejemplo, el nombre de una ciudad.

Private Sub Aplicar_Filtro()
        'Nota: Recordar que se debe asignar el BindingSource al DataGridView antes de realizar este procedimiento por medio del DataSource

        ' verificar que el DataSource no esté vacío  
        If BindingSource.DataSource Is Nothing Then
            ' si no hay registros cambiar el color del TextBox
            TextBox1.BackColor = Color.Red
            Exit Sub
        End If

        Try
            Dim filtro As String = String.Empty
            'la variable filtro debería contener algo así: "[Ciudad] like '%CANADA%'"
            filtro = "[Ciudad] like '%" & TextBox1.Text.Trim & "%'"
            ' asignar el criterio a la propiedad Filter del BindingSource  
            BindingSource.Filter = filtro
            
            'El filtro funciona parecido a una sentencia select del SQL en la parte del where
            'El filtro en realidad es: campo_del_datagridview = variable_a_buscar

            If BindingSource.Count = 0 Then
                ' si no hay registros cambiar el color del TextBox
                TextBox1.BackColor = Color.Red
            Else
                TextBox1.BackColor = Color.White
            End If
        Catch ex As Exception 'Errores
            MsgBox(ex.Message.ToString, MsgBoxStyle.Critical)
        End Try
End Sub

Luego, este procedimiento es llamado desde el TextBox, para que se actualice cada vez que el usuario escriba algo.

Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
        Aplicar_Filtro()
End Sub

Cerrar un proceso que se encuentra en memoria

Problema: Durante el desarrollo de mi aplicación, tuve que utilizar varios procesos nativos del Visual Basic 6, algunas ActiveX que eran de tipo dll y otras de tipo exe, y combinarlos me dio dolores de cabeza. Uno de los problemas con los que tuve que lidiar fue el cerrar aplicaciones ActiveX de tipo exe desde mi aplicación punto net.
Solución: La respuesta la encontré utilizando un proceso que se utiliza en VB6 pero que modificándolo un poco funciona perfectamente para mi problema. La solución la encontré aquí: Listar y cerrar procesos de Windows usando WMi

Mi modificación fue apenas mínima, pero la listo aquí de todas formas.

Private Function MatarProceso(ByVal StrNombreProceso As String, _
    Optional ByVal DecirSINO As Boolean = True) As Boolean
        ' Variables para usar Wmi  
        Dim ListaProcesos As Object
        Dim ObjetoWMI As Object
        Dim ProcesoACerrar As Object

        MatarProceso = False

        ObjetoWMI = GetObject("winmgmts:")

        If ObjetoWMI Is DBNull.Value = False Then

            'instanciamos la variable  
            ListaProcesos = ObjetoWMI.InstancesOf("win32_process")

            For Each ProcesoACerrar In ListaProcesos
                If UCase(ProcesoACerrar.Name) = UCase(StrNombreProceso) Then
                    If DecirSINO Then
                        If MsgBox("¿Matar el proceso " & _
                           ProcesoACerrar.Name & vbNewLine & "...¿Está seguro?", _
                                                 vbYesNo + vbCritical) = vbYes Then
                            ProcesoACerrar.Terminate(0)
                            MatarProceso = True
                        End If
                    Else
                        'Matamos el proceso con el método Terminate  
                        ProcesoACerrar.Terminate(0)
                        MatarProceso = True
                    End If
                End If

            Next
        End If

        'Elimina las variables  
        ListaProcesos = Nothing
        ObjetoWMI = Nothing
End Function

Como quitar Atributos de Oculto en Archivos y Carpetas

Por descuido y falta de tiempo, a la computadora de mi papá le cayó un virus de esos molestoso pero casi inofensivos y solo me di cuenta de que lo tenía cuando conecté el pendrive de mi papá a mi computadora y el antivirus saltó a eliminarlo.

El virus fue removido con facilidad, pero eso hizo que todos los archivos que se encontraban en el pendrive desaparecieran. La capacidad de uso del pendrive se mostraba llena, lo que significaba que los archivos estaban físicamente allí, solo que no se veían.

El problema consistía en que el virus puso los archivos como ocultos y el antivirus no los hizo aparecer de nuevo, así que me tocó hacerlo manualmente.

Para comprobar que en realidad los archivos estuvieran donde debían estar, utilicé el programa Winrar y abrí el pendrive desde dicha aplicación, en el visor se mostraron los archivos aunque estuvieran ocultos. Esto solo me sirvió para saber que los archivos siguiera allí.

Para hacerlos aparecer, abrí una consola de DOS.

Para abrir la consola de DOS se da clic en Inicio y luego en Ejecutar, aparecerá una ventanita llamada Ejecutar, en donde dice Abrir: se escribe CMD.

Al aparecer la consola de DOS (una ventanita con fondo negro y letras blancas), hay que situarse en la ruta del directorio superior donde se encuentran los archivos ocultos. En mi caso era todo el pendrive que era el D: (solamente tuve que escribir D:)

Cuando estuve en la ruta adecuada (D:\>) escribí lo siguiente:

attrib -h -r -s *.* /s /d

Esperé unos segundos y ahora sí ya se veían todos los archivos.
Nota: Estos pasos son para versiones de Windows XP o superior.

Evitar que se ordenen las columnas de un DataGridView

Problema: Tengo un DataGridView, el cual contiene varias columnas, pero al dar clic en la cabecera (header) de cada columna, el grid completo se ordena dependiendo de la columna seleccionada. No deseo que se ordene, pues realizo cierta cantidad de cálculos en un orden definido.
Solución: Usar la opción SortMode de la columna.

En las propiedades de cada columna, en la vista diseño, se encuentra la propiedad SortMode, la cual, si se selecciona NotSortable, evita que el usuario ordene las columnas al dar clic en la cabecera.

También se puede asignar mediante código.

DataGridView1.Columns(0).SortMode = DataGridViewColumnSortMode.NotSortable

Convertir una columna del DataGridView en estilo DateTimePicker para Fechas

Problema: Tengo un grid el cual quiero que una columna sea como el DateTimePicker, o sea, con una lista desplegable en donde se muestre un cuadro de fecha.
Solución: Hay una solución que da la ayuda de Microsoft, se pone DataGridView y DateTimePicker en la ayuda y es el primero que aparece en la lista, pero yo me hice líos con lo que allí se explica, el ejemplo es bastante extenso y algo complejo para quienes estamos iniciando en este mundo del Punto Net. Tuve que buscar un poco en el Internet, y seguía perdiéndome en el camino, hasta que decidí unir los pedazos de cada sitio y "echando a perder", logré que apareciera el famoso cuadro de fecha. Intentaré explicar lo que hice, sin tratar de irme por las ramas, para que se pueda entender.

OJO: El código a utilizar es largo, pero es casi copiar y pegar. Se lo puede pegar al final de todo el código que se tenga, siempre y cuando sea dentro de la clase principal.

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            '-- Aquí va el código que inicia con el formulario
    End Sub
   
    '-- Aquí va todo el código de programación
   
    '-- Aquí se puede pegar el código que se describe abajo
End Class

Esta es la sección que se debe copiar, no se debe modificar nada, pues es un código genérico que sirve para cualquier DataGridView.

#Region "Calendario en DataGridView"
    Public Class CalendarColumn
        Inherits DataGridViewColumn

        Public Sub New()
            MyBase.New(New CalendarCell())
        End Sub

        Public Overrides Property CellTemplate() As DataGridViewCell
            Get
                Return MyBase.CellTemplate
            End Get
            Set(ByVal value As DataGridViewCell)

                ' Ensure that the cell used for the template is a CalendarCell.
                If (value IsNot Nothing) AndAlso _
                    Not value.GetType().IsAssignableFrom(GetType(CalendarCell)) _
                    Then
                    Throw New InvalidCastException("Must be a CalendarCell")
                End If
                MyBase.CellTemplate = value

            End Set
        End Property
    End Class
    
    Public Class CalendarCell
        Inherits DataGridViewTextBoxCell

        Public Sub New()
            ' Use the short date format.
            Me.Style.Format = "d"
        End Sub

        Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, _
            ByVal initialFormattedValue As Object, _
            ByVal dataGridViewCellStyle As DataGridViewCellStyle)

            ' Set the value of the editing control to the current cell value.
            MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, _
                dataGridViewCellStyle)

            Dim ctl As CalendarEditingControl = _
                CType(DataGridView.EditingControl, CalendarEditingControl)
            ctl.Value = CType(Me.Value, DateTime)

        End Sub

        Public Overrides ReadOnly Property EditType() As Type
            Get
                ' Return the type of the editing control that CalendarCell uses.
                Return GetType(CalendarEditingControl)
            End Get
        End Property

        Public Overrides ReadOnly Property ValueType() As Type
            Get
                ' Return the type of the value that CalendarCell contains.
                Return GetType(DateTime)
            End Get
        End Property

        Public Overrides ReadOnly Property DefaultNewRowValue() As Object
            Get
                ' Use the current date and time as the default value.
                Return DateTime.Now
            End Get
        End Property

    End Class
    Class CalendarEditingControl
        Inherits DateTimePicker
        Implements IDataGridViewEditingControl

        Private dataGridViewControl As DataGridView
        Private valueIsChanged As Boolean = False
        Private rowIndexNum As Integer

        Public Sub New()
            Me.Format = DateTimePickerFormat.Short
        End Sub

        Public Property EditingControlFormattedValue() As Object _
            Implements IDataGridViewEditingControl.EditingControlFormattedValue

            Get
                Return Me.Value.ToShortDateString()
            End Get

            Set(ByVal value As Object)
                If TypeOf value Is String Then
                    Me.Value = DateTime.Parse(CStr(value))
                End If
            End Set

        End Property

        Public Function GetEditingControlFormattedValue(ByVal context _
            As DataGridViewDataErrorContexts) As Object _
            Implements IDataGridViewEditingControl.GetEditingControlFormattedValue

            Return Me.Value.ToShortDateString()

        End Function

        Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As  _
            DataGridViewCellStyle) _
            Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl

            Me.Font = dataGridViewCellStyle.Font
            Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
            Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor

        End Sub

        Public Property EditingControlRowIndex() As Integer _
            Implements IDataGridViewEditingControl.EditingControlRowIndex

            Get
                Return rowIndexNum
            End Get
            Set(ByVal value As Integer)
                rowIndexNum = value
            End Set

        End Property

        Public Function EditingControlWantsInputKey(ByVal key As Keys, _
            ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
            Implements IDataGridViewEditingControl.EditingControlWantsInputKey

            ' Let the DateTimePicker handle the keys listed.
            Select Case key And Keys.KeyCode
                Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, _
                    Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp

                    Return True

                Case Else
                    Return Not dataGridViewWantsInputKey
            End Select

        End Function

        Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
            Implements IDataGridViewEditingControl.PrepareEditingControlForEdit

            ' No preparation needs to be done.

        End Sub

        Public ReadOnly Property RepositionEditingControlOnValueChange() _
            As Boolean Implements _
            IDataGridViewEditingControl.RepositionEditingControlOnValueChange

            Get
                Return False
            End Get

        End Property

        Public Property EditingControlDataGridView() As DataGridView _
            Implements IDataGridViewEditingControl.EditingControlDataGridView

            Get
                Return dataGridViewControl
            End Get
            Set(ByVal value As DataGridView)
                dataGridViewControl = value
            End Set

        End Property

        Public Property EditingControlValueChanged() As Boolean _
            Implements IDataGridViewEditingControl.EditingControlValueChanged

            Get
                Return valueIsChanged
            End Get
            Set(ByVal value As Boolean)
                valueIsChanged = value
            End Set

        End Property

        Public ReadOnly Property EditingControlCursor() As Cursor _
            Implements IDataGridViewEditingControl.EditingPanelCursor

            Get
                Return MyBase.Cursor
            End Get

        End Property

        Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)

            ' Notify the DataGridView that the contents of the cell have changed.
            valueIsChanged = True
            Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
            MyBase.OnValueChanged(eventargs)

        End Sub
    End Class
#End Region

Aquí se debe cambiar por el nombre del formulario en el que se encuentra el grid y el nombre del grid.

#Region "Calendario en DataGridView en Form"
    Public Class Form1 'El nombre del formulario
        Inherits Form

        Private DataGridView1 As New DataGridView() 'El nombre del grid
        <STAThreadAttribute()> _                Public Shared Sub Main()
            Application.Run(New Form1())
        End Sub

        Public Sub New()
            Me.DataGridView.Dock = DockStyle.Fill 'Aqui se cambia el nombre del grid
            Me.Controls.Add(Me.DataGridView) 'También aquí
            Me.Text = "Fecha"
        End Sub

    End Class
#End Region

Impedir cambiar de pestaña en un TabControl

Problema: Necesito validar que cuando se esté editando algún tipo de información dentro de una de las pestañas del TabControl, no permita cambiar de pestaña hasta que haya terminado de trabajar con los datos.
Solución: Encontré la solución aquí: Impedir el cambiar de pestaña en un TabControl, solo que he decidido añadirle un poco de código de ejemplo para hacerlo más ilustrativo.

Private Sub TabControl1_Selecting(ByVal sender As Object, ByVal e As System.Windows.Forms.TabControlCancelEventArgs) Handles TabControl1.Selecting
        If grabar = False Then 'Aquí pongo la validación que necesito, en mi caso es una variable pública que me controla si se han grabado los datos
            e.Cancel = True
            MsgBox("Grabe antes de cambiar de pestaña", MsgBoxStyle.Critical, "Error")
        End If
End Sub

Validar que los datos ingresados en un DataGridView sean sólo números

Problema: Tengo un grid con varias columnas, pero en dos de ellas se deben ingresar solo datos numéricos.
Solución: Modifiqué un poquito el código que encontré aquí: DataGridView con solo números

Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
        ' referencia a la celda  
        Dim validar As TextBox = CType(e.Control, TextBox)

        ' agregar el controlador de eventos para el KeyPress  
        AddHandler validar.KeyPress, AddressOf validar_Keypress
End Sub
Private Sub validar_Keypress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs)
        ' evento Keypress  

        ' obtener índice de la columna  
        Dim columna As Integer = DataGridView1.CurrentCell.ColumnIndex

        ' comprobar si la celda en edición corresponde a la columna 1 o 2
        If columna = 1 Or columna = 2 Then
                ' Obtener caracter  
                Dim caracter As Char = e.KeyChar

                ' referencia a la celda  
                Dim txt As TextBox = CType(sender, TextBox)

                ' comprobar si es un número con isNumber, si es el backspace, si el caracter  
                ' es el separador decimal, y que no contiene ya el separador  
                If (Char.IsNumber(caracter)) Or _
                (caracter = ChrW(Keys.Back)) Or _
                (caracter = ",") And _
                (txt.Text.Contains(",") = False) Then
                        e.Handled = False
                Else
                        e.Handled = True
                End If
        End If
End Sub

Gracias a un comentario realizado en mi anterior blog, pude rescatar estas palabras.

Aquí una solución sencilla y con control de decimales.

Private Sub ValidaNro_KeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs)
' hay que poner en e format de cada columna un formato numérico para que controle solo números

        Dim FormatoColumna As String = Malla.Columns(Malla.CurrentCell.ColumnIndex).DefaultCellStyle.Format.ToString
        If FormatoColumna = "" Then Exit Sub

        Select Case e.KeyChar
            Case "0" To "9", vbBack
                e.Handled = False
            Case "."
                If FormatoColumna.Contains(".") Then
                    e.Handled = CType(sender, TextBox).Text.Contains(".")   ' verifica si ya tiene un punto decimal
                Else
                    e.Handled = True
                End If
            Case Else
                e.Handled = True
        End Select
    End Sub

Private Sub Malla_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles Malla.EditingControlShowing
        Dim ValidarNro As TextBox = e.Control
        RemoveHandler ValidarNro.KeyPress, AddressOf ValidaNro_KeyPress
        AddHandler ValidarNro.KeyPress, AddressOf ValidaNro_KeyPress
End Sub

Escribir en mayúsculas en un campo de un DataGridView tipo DataGridViewTextBoxColumn

Problema: Tengo un campo en mi grid, que cuando el usuario ingrese los datos, se presenten en mayúscula, como la opción CharacterCasing de un TextBox.
Solución: La que encontré por allí, solo que en C# y por pedazos, así que la unifico aquí.

Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
        'Verifico que la columna sea de texto
        If TypeOf e.Control Is TextBox Then
                'Indico la columna que deseo cambiar
                If DataGridView1.CurrentCell.ColumnIndex = 1 Then
                        'Pone en mayúsculas la celda del grid
                        DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
                End If
        End If
End Sub

¿Cómo pasar fórmulas de un subinforme al informe principal en Crystal Report?

Problema: Tengo un valor que se calcula o se obtiene en un subinforme y deseo que se muestre en el informe principal.
Solución: Encontré la respuesta aquí: How to get formula field value from Subreport to main report, solo que en inglés y en un foro que explica las cosas dependiendo del hilo de la conversación, así que decidí unificarlo aquí.

Como se indica en la página de referencia, las variables compartidas son usadas desde la versión 7 del Crystal Report y esto es lo que necesito para solucionar mi problema, pues es la forma más fácil de pasar valores de un reporte a otro, como de un subinforme al principal.

También se puede usar para pasar valores entre subinformes o del principal al subinforme, todo depende del modo de usarlo.

Lo que se debe recordar es que el reporte primero evalúa la fórmula en donde se llena el valor de la variable compartida y luego realiza la fórmula en donde se muestran los datos, independientemente de dónde se encuentre la fórmula y la variable, ya sea en el principal o en el subinforme.

Por ejemplo, si deseo pasar el total del subinforme para realizar un cálculo en el informe principal, realizo lo siguiente:

1. En el subinforme, creo una formula parecida a la de abajo:

//@SubFormula
//Guarda el total del campo {Tabla.Total_Campo}
//en una variable compartida llamada 'miTotal'
Shared CurrencyVar miTotal := Sum ({Tabla.Total_Campo})

2. Pongo esta fórmula en el subinforme

3. En el informe principal, creo una fórmula que declara el nombre de la misma variable:

//@FormulaPrincipal
//Retorna el valor que fue guardado en la variable compartida llamada
//miTotal del subinforme
Shared CurrencyVar miTotal;
miTotal

4. Pongo la @FormulaPrincipal en el informe principal debajo de la sección que contiene el subinforme. Esto sirve para asegurar que el Crystal evalue antes la @SubFormula antes de la @FormulaPrincipal.

Una vez que tengo el valor que necesito en la @FormulaPrincipal puedo hacer uso de ella como más me convenga, por ejemplo, ocultar una sección dependiendo del monto que devuelve la @SubFormula.

Dar formato a los números en Crystal Report

Problema: Tengo un campo numérico obtenido desde mi base de datos, el cual presento en un reporte de Crystal, pero el formato con el que se presenta no es el adecuado.

Suponiendo que en mi base de datos tengo un campo llamado valor, el cual es de tipo money y deseo presentarlo como un número sin decimales, pero no se muestra así originariamente.

Es decir, yo quiero algo como 123 pero se muestra 123.00, ¿cómo le quito los ceros sobrantes?

Intenté hacerlo con un campo de fórmula, creía que existía una manera de poner formato a un campo por medio de una función, pero me fui de narices al ver que así no se podía, por lo que tuve que intentar otras opciones.

Según la ayuda de .Net, solo se da clic derecho al campo en cuestión y se escoge el formato que se desea dar en la pestaña Número, pero he aquí, que a mi no me aparecía la famosa pestaña, por lo que no podía darle formato por ese lado.

Después de leer e intentar por un rato, descubrí que el Crystal no estaba tomando en cuenta el tipo de dato que estaba utilizando, que en mi caso, suponía yo que era un integer. Pero la pregunta principal de todas era, ¿qué tipo de dato es el que tiene mi campo? Definitivamente ninguno numérico, pero entonces, ¿dónde definía el tipo de dato de mi campo? Después de revisar mi select, mi SP, mi formulario, mi código y la mitad de mi aplicación me di cuenta que el problema era el dataTable tipado que había creado anteriormente en el dataSet para utilizarlo en el reporte.

Pero lo que no sabía en el momento de crear mi dataTable con los campos, es que cada campo debe tener un tipo específico para que funcione adecuadamente.

¿Cómo hago eso? Pues abro mi dataSet en modo diseño y selecciono el campo que deseo modificar, en la ventana de Propiedades busco la opción DataType y selecciono el tipo de dato que necesito, que en mi caso sería System.Int32.
Y listo. Grabo y actualizo la base de datos del Crystal (clic derecho en la ventana Explorador de Campos, en Campos de base de datos, en el menú desplegable escoger comprobar base de datos). Con esto el campo de mi reporte que antes no podía modificar, ahora sí aparece la pestaña Número al dar clic en Dar formato a objeto.

Validar que Textbox admita solo números

Private Sub TextBox1_KeyPress(ByVal sender As Object, _
                          ByVal e As System.Windows.Forms.KeyPressEventArgs) _
                          Handles TextBox1.KeyPress
If InStr(1, "0123456789" & Chr(8), e.KeyChar) = 0 Then
    e.KeyChar = ""
End If
End Sub

Gracias a un comentario que recibí en mi otro blog, puedo rescatar sus palabras.

Existe otra forma de hacerlo y seria algo así:

If Char.IsNumber(e.KeyChar)  Then
            e.Handled = False
Else
            e.Handled = True
End If

Llenar un DataGridView a partir de un SP (procedimiento almacenado) de una base de datos [Versión VB.Net]

Problema: Mi amada Alondra me ha hecho ver que no tengo una entrada que explique directamente cómo llenar un DataGridView desde un select o un SP (procedimiento almacenado), sino que lo tengo enredado en diferentes entradas, así que para esté contenta, explicaré paso a paso como hacerlo.
Solución: La que explico a continuación.

Necesito llenar mi DataGridView con los datos que tengo en diferentes tablas de mi base de datos, por lo que el select es un poquito complejo y no puedo crearlo directamente con un DataTable tipado, así que tengo que hacerlo por código.

Voy a suponer que mi select sería algo así:

select campo1, campo2, campo3
from tablaA, tablaB
where relacionA1 = relacionB2

Obviamente por la complejidad de mi select, decido ponerlo en un SP, el cual por ejemplo, me quedaría así:

create procedure sp_prueba
        @variable int
as

select campo1, campo2, campo3
from tablaA, tablaB
where relacionA1 = relacionB2
and restriccionB1 = @variable

GO

Esa fue la parte que tengo que realizar en mi base de datos, ahora la parte del Visual Basic .Net.

Tengo mi formulario creado llamado Form1, el cual contiene un DataGridView llamado DataGridView1 junto con los campos necesarios creados en modo diseño.

Ahora creo un procedimiento para llenar este grid.

Private Sub Llenar_DataGridView()
        'Los argumentos de conexión a la base de datos 
        Dim args As String = "Data Source=;" & _ 
                "Initial Catalog=;Integrated Security=SSPI"

        'Abro la conexión
        Using connection As SqlConnection = New SqlConnection(args)
                Dim command As SqlCommand
                Dim adapter As SqlDataAdapter
                Dim dtTable As DataTable
               
                'Indico el SP que voy a utilizar
                command = New SqlCommand("sp_prueba", connection)
                command.CommandType = CommandType.StoredProcedure
                adapter = New SqlDataAdapter(command)
                dtTable = New DataTable
                With command.Parameters
                        'Envió los parámetros que necesito
                        .Add(New SqlParameter("@variable", SqlDbType.Int)).Value = valor
                End With
                Try
                        'Aquí ejecuto el SP y lo lleno en el DataTable
                        adapter.Fill(dtTable)
                        'Enlazo mis datos obtenidos en el DataTable con el grid
                        DataGridView1.DataSource = dtTable
                        'Si no pongo esta línea, se crean automáticamente los campos del grid dependiendo de los campos del DataTable
                        DataGridView1.AutoGenerateColumns = False
                        'Aquí le indico cuales campos del select de mi SP van con los campos de mi grid
                        With DataGridView1
                                .Columns("Campo1").DataPropertyName = "campo1"
                                .Columns("Campo2").DataPropertyName = "campo2"
                                .Columns("Campo3").DataPropertyName = "campo3"
                        End With
                Catch expSQL As SqlException
                        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                        Exit Sub
                End Try
        End Using
End Sub

Este procedimiento lo llamo desde el load de la aplicación.

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

Y listo, con esto, los datos de mi select se mostrarán en el grid de mi aplicación.

¿Quedó claro mi linda Alondra? Muchos besitos para ti.

Colores personalizados en Button o DataGridView guardado y obtenido de una base de datos

Este aporte es gracias a Alondra

1.- Para todo lo relacionado con los colores hay que importar el sistema de dibujo, por lo que hay que poner esta línea al principio de todo.

Imports System.Drawing

2.- Al presionar un Botón, que aparezca el cuadro de diálogo de los colores y que cambie su color.

Para ello insertar un Button y un ColorDialog al formulario En el ejemplo se muestra como cambiar el color del botón.

Private Sub Button1 _Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'Abre el cuadro de los colores al presionar el botón
        ColorDialog1.Color = Button1.BackColor
        If Me.ColorDialog1.ShowDialog = DialogResult.OK Then
                'Si se presiona Aceptar pasa el color seleccionado como fondo del Button1
                Button1.BackColor = ColorDialog1.Color
        End If
End Sub

3.- Para guardar el color en una base de datos.

'Declarar una variable de tipo string
Dim color1 As String

'Pasar  el color del botón a la variable
color1 = Button1.BackColor.ToString()

'Lo que se guarda en esta variable puede contener lo siguiente:
'Color [Yellow]
'o también
'Color [A=255, R=176, G=255, B=176]
'Con todo (la palabra Color y los corchetes)

'Para sacar la palabra color y el corchete [
color1 = Mid(color1, 8 )
'Me queda:
'Yellow]
'o también
'A=255, R=176, G=255, B=176]

'Para sacar el último corchete
color1 = Replace(color1, "]", "")
'Me queda:
'Yellow
'o también
'A=255, R=176, G=255, B=176

'Y esto es lo que guardo en la base de datos

4.- Para obtener el color de la base de datos y ponerlo al botón.

'Declarar tres variable: una de tipo string que en este ejemplo será botoncolo1 y otras dos de tipo integer que será longitud1 y contador
Dim botoncolo1 As String        'Guarda el color
Dim longitud1, contador As Integer        'Guarda la longitud del color y un contador

'Ejemplo de cómo traerlo de la base de datos

'Los argumentos de conexión a la base de datos
Dim args As String = "Data Source=;" & _
        "Initial Catalog=;Integrated Security=SSPI"

Using connection As SqlConnection = New SqlConnection(args)
        Dim txtConexion As String = "select color from tabla where id = 1"
        adapter = New SqlDataAdapter(txtConexion, connection)
        Try
                dtTabla = New DataTable
                adapter.Fill(dtTabla)
                Dim row As DataRow = dtTabla.Rows(0)
                'Obtengo el campo de la tabla
                Dim value As Object = row.Item("color")
                'Convierto el campo en el tipo de dato que necesito
                botoncolo1 = CStr(value)
        Catch expSQL As SqlException
                MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                Exit Sub
        End Try
End Using

'De cualquier forma que se obtenga el valor del color guardado en la base de datos se ingresa en la variable botoncolo1.
'Hay que recordar que traerá:
'Yellow
'o también
'A=255, R=176, G=255, B=176

'Como no sé cuál de los dos casos trae, realizo lo siguiente:
botoncolo1 = Replace(botoncolo1, "A=", "")
botoncolo1 = Replace(botoncolo1, "R=", "")
botoncolo1 = Replace(botoncolo1, "G=", "")
botoncolo1 = Replace(botoncolo1, "B=", "")

'Me queda:
'Yellow
'o también
'255, 176, 255, 176

'Guardo el contenido en un array
Dim c1() = Split(botoncolo1)

'Me queda:
'c1(0) = Yellow
'o también
'c1(0) = 255,
'c1(1) = 176,
'c1(2) = 255,
'c1(3) = 176

'Obtenemos la longitud del array
longitud1 = c1.Length
'La longitud será 1 para el primer caso (Yellow) y 4 para el segundo

'Pasamos el color del array al button
If longitud1 < 2 Then        'Si se trata de Yellow
        Button1.BackColor = Color.FromName(botoncolo1)
        'Habrá ocasiones que no se inserte directamente, entonces colocar:
        Button1.BackColor = Drawing.Color.FromName(botoncolo1)
Else        'Si se trata de 255, 176, 255, 176
        While cc < 4
                'Hay que quitar las comas de cada número guardado en el array
                c1(contador) = Replace(c1(contador), ",", "")
                contador = contador + 1
        End While
       
        Button1.BackColor = Color.FromArgb(c1(0), c1(1), c1(2), c1(3))
        'O prueba con:
        Button1.BackColor = Drawing.Color.FromArgb(c1(0), c1(1), c1(2), c1(3))
End If

5.- El color se lo puede asignar a cualquier cosa, por ejemplo.

'Para colocar el color a la fila del datagridview es de la siguiente manera
DataGridView1.RowTemplate.DefaultCellStyle.BackColor = Color.Blue
'o
DataGridView1.RowTemplate.DefaultCellStyle.BackColor = Drawing.Color.Blue

'Pero también se lo puede asignar con la variable
If longitud1 < 2 Then
        DataGridView1.RowTemplate.DefaultCellStyle.BackColor = Color.FromName(botoncolo1)
Else                       
        While cc < 4
                c1(contador) = Replace(c1(contador), ",", "")
                contador = contador + 1
        End While

        DataGridView1.RowTemplate.DefaultCellStyle.BackColor = Color.FromArgb(c1(0), c1(1), c1(2), c1(3))
End If

'Puede conservar los valores de las variables botoncolo1, longitud1 y del array c1 declarándolos como Private para que las próxima vez solo sea necesario asignarlo así:
If longitud1 < 2 Then
        DataGridView1.RowTemplate.DefaultCellStyle.BackColor = Color.FromName(botoncolo1)
Else
        DataGridView1.RowTemplate.DefaultCellStyle.BackColor = Color.FromArgb(c1(0), c1(1), c1(2), c1(3))
End If

'Si no se quiere trabajar con dos tipos de colores (botoncolo1 y c1(0), c1(1), c1(2), c1(3)), se puede asignar a una variable general
Dim colorGeneral As Color
colorGeneral = Color.FromName(botoncolo1)
'o también
colorGeneral = Color.FromArgb(c1(0), c1(1), c1(2), c1(3))

Para cambiar el titulo de la cabecera de un DataGridView

DataGridView1.Columns("nombre_columna").HeaderText = "Titulo_Nuevo"

Esta aportación es gracias a Alondra.

Evento que se dispara cuando cambia la pestaña de un TabControl

Private Sub TabControl1_Selecting(ByVal sender As Object, ByVal e As TabControlCancelEventArgs) _
Handles TabControl1.Selecting
        If Me.TabControl1.SelectedIndex = 0 Then
                'Aquí se realiza las acciones pertinentes
        End If
End Sub

Esta aportación es gracias a Alondra.

Evento que se dispara al señalar un nodo en un TreeView

Private Sub TreeView1_AfterSelect(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles TreeView1.AfterSelect
        Dim variable as String
        variable = e.Node.Text    'e es el nodo señalado y lo asigno a una variable
End Sub

Esta aportación es gracias a Alondra.

Poner color a un TreeView

TreeView1.Nodes(1).BackColor = Color.White    'fondo
TreeView1.Nodes(1).ForeColor = Color.Black    'letra

Esta aportación es gracias a Alondra.

Poner el TabPage de un TabControl en invisible o deshabilitarlo

Para poner invisible un TabPage.

Me.TabPage1.Parent = Nothing

Para deshabilitar un TabPage.

Me. TabControl1.TabPages(2).Enabled = False

Esta aportación es gracias a Alondra.

Impedir la combinación ALT+F4 para cerrar la aplicación o cerrar el formulario con la X

Private Sub Form1_Unload(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
      e.Cancel = True
End Sub

'Y para salir:

Application.Exit()

Esta aportación es gracias a Alondra.

Problema con SubInformes y dos DataTables en Crystal Reports

Problema: Tengo dos reportes, creados por separado, por ejemplo, informe1 e informe2, pero por varias necesidades tuve que poner el informe2 dentro del informe1 como un subreporte, pero, ¡HORROR!, el informe2 no me muestra ningún dato al momento de mostrarlo.
Solución: Revisar la manera en que se envían los datos a los diferentes informes.

El informe1 se llenan con parámetros de campos fórmula que envío desde mi formulario, el informe2 se llena con un DataTable que contiene la información necesaria. ¿Cuál es el error? Que el informe1 no contiene el DataTable del informe2 y por eso no se muestra la información.

Desgraciadamente, el informe1 o el informe principal debe contener TODOS los DataTables de los otros subinformes (por si tengo más de uno), porque del informe principal se envía los datos a los informes secundarios o subreportes. Solo hay que tener un poquito de cuidado al tratar con la información de los diferentes DataTables, sobre todo si los nombres son iguales.

Recomendación: Usar DataTables con nombres diferentes para cada reporte, porque si no se puede caer en inconsistencias.

De allí, el enviar la información a los diferentes reportes y subinformes es de lo más sencillo, como lo indico en el siguiente ejemplo.

Private Sub Cargar_Rep_Sub()
        'CrystalReport1 es el informe1 (el principal)
        Dim rdInforme As New CrystalReport1
        'El informe principal solo recibe campos fórmulas
        rdInforme.DataDefinition.FormulaFields("campo1").Text = Label1.Text
        rdInforme.DataDefinition.FormulaFields("campo2").Text = Text1.Text
       
        'Anteriormente, en informe1.rpt tuve que añadir el dataTable del informe2.rpt
        'Aquí se envía los datos al informe principal
        rdInforme.SetDataSource(dataTable)
       
        'Asigno el visor al reporte
        Form1.CrystalReportViewer1.ReportSource = rdInforme
End Sub

¿Pero, qué pasa si tengo dos o más dataTables? ¿Cómo envío dos dataTables a un mismo reporte?

Para ello se utiliza un dataSet tipo con los dataTables creados anteriormente, como lo explico en otra sección, la diferencia radica en la forma en que se llenan estas dataTables.

Generalmente, para llenar mis datos que obtengo de diferente lugares (por ejemplo un SP o un select), lo realizo de esta manera.

Try
        Dim dtTabla as New DataTable
        adapter.Fill(dtTabla)
Catch expSQL As SqlException
        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
        Exit Sub
End Try

Pero para llenar las tablas del dataSet tipo se realiza así.

Try
        'dtTabla es el nombre de la tabla que se encuentra dentro del dataSet tipo
        adapter.Fill(dataSet1, "dtTabla")
Catch expSQL As SqlException
        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
        Exit Sub
End Try

Luego, para enviar los datos al Crystal Report se asigna el dataSet.

Private Sub Cargar_Reporte()
        'CrystalReport1 es el informe1 (el principal)
        Dim rdInforme As New CrystalReport1
       
        'Aquí se envía los datos al informe principal
        rdInforme.SetDataSource(dataSet1)
       
        'Asigno el visor al reporte
        Form1.CrystalReportViewer1.ReportSource = rdInforme
End Sub

Pasar datos con campos fórmula a un informe Crystal Report

Problema: Tengo algunos datos que deseo pasar a mi informe, como por ejemplo, el nombre, que se encuentra en un Label o TextBox.
Solución: Pasarlo como campos fórmula al reporte.

No es complicado, en el reporte.rpt creo los campos de fórmula que necesito, todos en blanco, pues solo necesito el nombre del campo, porque se llenan desde mi formulario. Los ubico en el informe, y listo. Solo falta llenarlos.

Tampoco ésto es difícil, solo se necesita obtener el reporte y mandar los datos a las formulas. Para ello hice mi propio pequeño proceso.

Private Sub Cargar_Campos_Crystal()
        Dim rdInforme As New CrystalReport1 'Es el reporte CrystalReport1.rpt
        Form1.CrystalReportViewer1.ReportSource = rdInforme 'Anexo mi formulario con el visor al reporte
       
        'Aquí lleno los campos formula del reporte
        'Si el dato a enviar es texto, va entre comillas
        rdInforme.DataDefinition.FormulaFields("campo_texto").Text = "'" & Label1.Text & "'"
        'Si el dato a enviar es numérico, va sin nada
        rdInforme.DataDefinition.FormulaFields("campo_numerico").Text = Text1.Text
End Sub

Crear un informe Crystal Report a partir de un DataGridView

Problema: Tengo mi aplicación lista y funcionando, ahora solo me falta imprimir los informes o reportes necesarios, pero uno de ellos debe mostrar lo que tengo en un grid.
Solución: Una mezcla de DataSet, DataTable y paciencia.

Explicaré paso a paso lo que descubrí e implementé.

No solía utilizar los datasets "tipados" como suele llamarlos, pues las tablas las creaba directamente por código con DataTables.

¿Qué es un dataset tipado? Pues un archivo DataSet.xsd que se añade al proyecto.

¿Y para qué lo necesito? Para añadir los campos al reporte que crearé.

¿Es necesario crear un DataSet para crear mi reporte? Desgraciadamente, sí. No he encontrado otra forma de hacerlo. Bueno, existen muchas maneras, pero esta es la más sencilla para utilizar los datos de mi DataGridView.

¿Para qué necesito un DataSet si mis datos los tengo en un DataTable (que obviamente muestro en un grid)? Para poner los campos en el reporte, no se puede anexar directamente los campos de un DataTable creado desde código.

¿Cómo lo hago? Click derecho en el Explorador de Soluciones en el nombre del proyecto, se escoge Agregar, luego "Nuevo elemento" y se abre la ventana de "Agregar nuevo elemento" o también en el menú Proyecto - Agregar nuevo elemento...

En la lista de "Categorías" escogemos la sección "Datos" o navegamos hasta encontrar una cosita llamada "Conjunto de Datos", en cuya explicación indica que es un DataSet, le doy Agregar y se abre una ventana en gris que indica que se pueden añadir tablas.

¿Cómo lleno el DataSet con mis datos del DataTable? Pues, directamente desde el diseñador, no se puede, si es que el DataTable se llena por código, como en mi caso, que el DataTable lo lleno con los datos que muestro en un grid, lo que podemos hacer es anexar después, como lo explico más adelante.

Necesito, de ley, una DataTable dentro del DataSet, para poder añadir los campos en el reporte, así que, con paciencia, agrego un dataTable con los campos que necesito.

¿Cómo creo un dataTable dentro del dataset? Una vez abierto el diseñador del dataset (una pantalla en gris), se da clic derecho en cualquier sitio, en el menú despegable escoger Agregar - DataTable.

Esto creará un cuadradito azul con gris de nombre "DataTable1", al dar clic allí se podrá cambiar el nombre de la tabla (sí, ese es el nombre del dataTable), luego, para añadir los campos, se da clic derecho en el área gris del dataTable (justo debajo del nombre), en el menú que aparece escoger Agregar - Columna, y aparecerá un rectángulo blanco con el nombre "DataColumn1", al dar clic allí se podrá cambiar el nombre de la columna.  Para añadir más columnas repetir los pasos anteriores y listo.

OJO: De ser posible, poner el mismo nombre del DataTable con que se llena el grid y los mismos campos. O sea, si en mi grid lleno los datos con un DataTable llamado dtTabla con los campos c1, c2 y c3, esto mismo lo pongo en el DataSet.

¿Cómo lleno el DataSet con la información que tengo en mi grid? Como recordaremos, ya tengo un DataSet el cual contiene los datos que se muestran en mi grid, y por lo tanto, tengo un procedimiento, Sub, Function o lugar donde están siendo llenados, lo único necesario de hacer es llenar también el DataSet tipado en el mismo lugar.

Un ejemplo, tengo mi proceso que lleno el DataTable el cual está enlazado a mi grid, y le añadí el DataSet.

Private Sub Llenar_Grid_y_Reporte(ByVal cotizacion_codigo As Integer)
        'Obtengo los datos
        Using connection As SqlConnection = New SqlConnection(args) 'Previamente tendremos nuestra variable de conexión
                command = New SqlCommand("sp_llena_grid", connection)
                command.CommandType = CommandType.StoredProcedure
                adapter = New SqlDataAdapter(command)
                With command.Parameters
                        .Add(New SqlParameter("@var1", SqlDbType.Int)).Value = var1
                End With
                Try
                        adapter.Fill(dtTablaGrid) 'Este DataTable es que utilizo para llenar el grid
                        adapter.Fill(dataSet1, "dtTablaReporte") 'Este es el DataSet tipado que añadí
                Catch expSQL As SqlException
                        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                        Exit Sub
                End Try
        End Using
        
        'Lleno el grid
        DataGridView1.AutoGenerateColumns = False
        DataGridView1.DataSource = dtTablaGrid
        With DataGridView1
                .Columns("col1").DataPropertyName = "col1"
                .Columns("col2").DataPropertyName = "col2"
        End With
End Sub

Bien, tengo mi DataSet con mi DataTable, ahora tengo que crear el reporte.

Agrego un nuevo elemento al proyecto, pero esta vez busco la categoría Reporte y escojo el que dice Crystal Reports.

Luego aparecerá una ventana para crear un nuevo documento de Crystal Report, el cual está seleccionado Usar asistente para informes, de momento escojo ese por ser el más fácil. Abajo escojo "Estandar" y doy click en Aceptar. Mi reporte se llamará CrystalReport1.rpt.

Expandir las carpetas "Datos del proyecto" y la "ADO.NET DataSets", allí debe encontrarse el DataSet que creé anteriormente junto con la DataTable también creada, la añado y continúo con el asistente.

El asistente indicará los campos que deseo añadir al reporte, el modo de agrupamiento, si deseo un subconjunto de datos e infinidad de cosas por el estilo, pero como yo deseo ver mi reporte ya, solo le doy click en Finalizar. No me preocupo, porque luego se puede añadir todo lo que deseo o se me haya olvidado.

Una vez abierto el informe en modo diseño, puedo revisar como se visualiza desde la lengüeta "Vista previa del Informe", pero me mostrará datos ficticios o en el peor de los casos, ningún dato.

Tengo mi archivo del reporte, ahora necesito un "visor" de dicho informe, esto es, un formulario que muestre el informe que he creado. Para ello, agrego otro Windows Forms, el cual llamo Form1, y que le doy el tamaño y la personalización que quiero, al final, desde el "Cuadro de herramientas", en la sección Informe, escojo un "CrystalReportViewer" que lo llamo CrystalReportViewer1 y lo añado al formulario. El viewer o visor se puede personalizar a gusto. No es necesario anexarle un reporte, pues esto lo hago más adelante.

Bien, tengo mi formulario que contiene el visor, el reporte Crystal Report, mi DataSet con una DataTable, ahora queda unirlo todo. Desde el formulario en que tengo mi grid, tengo además, un botón que dice Imprimir, el cual mostrará el reporte o informe con los datos de mi grid. Para ello creé un proceso que junta todo y que se llama desde el botón Imprimir.

Private Sub Imprimir_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Imprimir.Click
        'Invoco al procedimiento que llena los datos del reporte
        Cargar_Crystal()
        'Abro el formulario que contiene el reporte
        Form1.ShowDialog()
End Sub
Private Sub Cargar_Crystal()
        'El CrystalReport1 es el nombre de mi archivo reporte CrystalReport1.rpt
        Dim rdInforme As New CrystalReport1
        'El dtTabla es mi DataTable que contiene los datos del DataGridView llenado anteriormente
        rdInforme.SetDataSource(dtTabla)
        'El Form1 es el formulario que contiene el visor llamado CrystalReportViewer1
        Form1.CrystalReportViewer1.ReportSource = rdInforme
End Sub

Si todo sale bien (y espero que así sea) se mostrarán los datos de los campos añadidos en el reporte. Recuerda que los nombres son importantes, los DataTables del grid y del reporte (DataSet) deben llamarse iguales, con todo y mayúsculas, así mismo con los campos.

Nota: Esto está creado en Microsoft Visual Basic 2008 y el Crystal Reports Basic para Visual Studio 2008.

Cómo leer (recuperar, u obtener) una imagen de una base de datos (SQL Server) en un PictureBox

Encontré mucha información acerca de cómo obtener una imagen de una base de datos SQL Server, pero hice mi propio proceso, un poco más sencillo de entender a mi manera.

Lo primero es tener una tabla que contenga la imagen que guardo en mi base de datos, por ejemplo, mi tabla sería algo así.

CREATE TABLE Imagenes
(
        id INT IDENTITY NOT NULL,
        fotos IMAGE NOT NULL
)

El llenar la tabla, con las imágenes, no lo indicó aquí, pues es un proceso aparte.

Así que suponiendo que mi tabla está llena, necesito obtener la imagen de un registro específico, por ejemplo, la foto de alguien, por lo que sería algo así.

Private Sub Buscar_Imagen()
        'La variable Byte tiene que ir obligatoriamente con ()
        Dim byImagen() As Byte = Nothing
        Dim oMemoryStream As MemoryStream
        Dim bmpImagen As Bitmap
        Dim dtTabla As New DataTable
        'Los argumentos de conexión a la base de datos
        Dim args As String = "Data Source=;" & _
                "Initial Catalog=;Integrated Security=SSPI";

        Using connection As SqlConnection = New SqlConnection(args)
                Dim txtConexion As String = "select fotos "
                txtConexion = txtConexion + "from Imagenes "
                'La variable codigo la lleno antes con la identificación de la persona que voy a mostrar
                txtConexion = txtConexion + "where id = " & codigo
                adapter = New SqlDataAdapter(txtConexion, connection)
                Try
                        dtTabla = New DataTable
                        adapter.Fill(dtTabla)
                        'Si no hay registros en la tabla no se muestra nada
                        If dtTabla.Rows.Count = 0 Then
                                Exit Sub
                        End If
                        Dim row As DataRow = dtTabla.Rows(0)
                        'Obtengo el campo de la tabla
                        Dim value As Object = row.Item("fotos")
                        'Si está en null no se muestra nada
                        If Not value Is DBNull.Value Then
                                'Convierto el campo en el tipo de dato que necesito
                                byImagen = CType(value, Byte())
                                oMemoryStream = New MemoryStream(byImagen)
                                bmpImagen = New Bitmap(oMemoryStream)
                                'Lo muestro en el PictureBox
                                PictureBox1.Image = bmpImagen
                        End If
                Catch expSQL As SqlException
                        MsgBox(expSQL.ToString, MsgBoxStyle.OkOnly, "SQL Exception")
                        Exit Sub
                End Try
        End Using
End Sub

Nota: Recordar importar lo siguiente.

'Sirve para las sentencias SQL
Imports System.Data.SqlClient
'Sirve para la memoria
Imports System.IO

Ajustar la celda de un DataGridView a su contenido

Problema: Necesito que la celda de un grid se vuelva más ancha de lo que aparece normalmente y que se ajuste a su contenido, sobre todo, cuando en una celda puede contener varías líneas de texto.
Solución: Hacerlo directamente desde el diseñador del DataGridView es un dolor de cabeza, pues se me confunden las propiedades que pongo para las filas, columnas y demás yerbas, por lo que, generalmente, no logro el efecto adecuado. Por lo que encontré una manera de hacerlo vía código, que es sencillo y se puede usar en cualquier parte.

Supongamos que tengo un grid llamado DataGridView1, por lo que hago lo siguiente.

'Determinamos el alto de las filas
DataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells

'Referenciamos la columna
Dim col As DataGridViewColumn = DataGridView1.Columns("nombre_columna")

'Ajustamos la celda a su contenido.
col.DefaultCellStyle.WrapMode = DataGridViewTriState.True

Mostrar errores con ErrorProvider

Una de las "cositas" nuevas que me han gustado, ha sido la validación visual de los errores de usuario, ¿a que me refiero?, pues cuando un usuario ingresa información, siempre existe la posibilidad de equivocarse, por lo que es responsabilidad del programador el validar el tipo de datos que el usuario ingresa, sobre todo si genera errores al guardarse en la base de datos.

Muchas veces se muestra un mensaje en pantalla, tipo MsgBox, que indica el error, pero si el usuario escribió muy rápido o no tomó las precauciones al leerlo, suele volver a repetirse u olvidarse por parte del usuario.

Pero con la opción del ErrorProvider, a parte de validar el error (de eso no me salvo), se muestra un icono de error (el típico circulo rojo con el signo de admiración) en el lugar exacto donde se cometió el error, esto significa que puedo poner el icono de error en donde yo lo prefiera y personalizar a mi gusto.

Un ejemplo sencillo de su uso sería así.

If Texto1.Text = "" Then
        MsgBox("Debe escribir un valor antes de continuar", MsgBoxStyle.Critical, "Error")
        'Aquí acciono el icono de error, indicando en donde debe colocarse el icono
        'y lo que el mensaje debe decir
        ErrorProvider1.SetError(Texto1, "Escriba un valor indicativo")
Else
        'Si el error ha sido superado, se debe borrar
        ErrorProvider1.SetError(Texto1, "")
End If

Interacción con mensaje de cuadro de diálogo MsgBox

El cuadro de diálogo MsgBox es una de las opciones que más utilizo para depuración de datos y seguimiento de eventos, suelo utilizar MsgBox(variable) para saber cuales son los resultados cuando estoy programando, pero existen veces que necesito que el usuario me indique si desea realizar una u otra acción, por ejemplo cuando le pregunto si desea eliminar un registro del grid, es aquí cuando utilizo las demás funcionalidades del MsgBox.

Pongo lo que Microsoft indica sobre el MsgBox.

MsgBox.
Muestra un mensaje en un cuadro de diálogo, espera a que el usuario haga clic en un botón y, a continuación, devuelve un entero que indica el botón en el que el usuario ha hecho clic.

No voy a enumerar la lista de los valores de los estilos del MsgBox, pero pongo el ejemplo para mejor entendimiento.

Dim mensaje As String
Dim titulo As String
Dim estilo As MsgBoxStyle
Dim respuesta As MsgBoxResult

mensaje = "¿Desea continuar?"   'Define el mensaje
estilo = MsgBoxStyle.DefaultButton2 Or _
   MsgBoxStyle.Critical Or MsgBoxStyle.YesNo
titulo = "Acción a realizar"   'Define el titulo del cuadro de diálogo
'Muestra el cuadro de diálogo.
respuesta = MsgBox(mensaje, estilo, titulo)
If respuesta = MsgBoxResult.Yes Then   'El usuario escogió SI
      'Aquí se realizan las acciones del SI
Else 'El usuario escogió NO
      'Aquí se realizan las acciones del NO
End If

Escribir en la ventana Resultado

Muchas veces necesito saber el resultado de una variable, o un rango de resultados, por lo que utilizo la ventana de Resultados del Visual Studio para ver mejor los datos que se devuelven. Para ello utilizo la función Console.WriteLine.

Console.WriteLine() 'Inserta una línea en blanco
Console.WriteLine(variable) 'Muestra el valor de la variable

Mover fila en el DataGridView arriba y abajo con botón DataGridViewButtonColumn

Problema: Tengo un grid con dos columnas tipo botón, un botón indica subir una fila y el otro botón indica bajar una fila. Debo mover las filas de arriba hacia abajo dependiendo del clic en su respectivo botón.
Solución: Después de buscar por un buen rato en el Internet, no encontré una forma directa, así que tuve que realizar mi propia opción.

Nota: Mi DataGridView está enlazado a un BindingSource el cual está enlazado a un DataTable (que enredo), lo que significa que los datos iniciales (los que se presentan en el grid) son fueron llenados originariamente en el DataTable, por lo que debo modificar directamente el DataTable.

La acción de mover no es otra cosa que eliminar la fila actual e insertarla una posición arriba o abajo de la que se encontraba originariamente.

'Debo obtener la acción clic de la columna botón
Private Sub DataGridView1_CellContentClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellContentClick
       'Sirve para obtener el índice de la fila a mover
        Dim indice As Integer
       'Se crea una nueva fila a partir de la DataTable que está enlazada al grid
        Dim dr As DataRow = DataTable.NewRow()
        'Obtiene la posición actual de la fila a moverse
        indice = DataGridView1.CurrentRow.Index
        'Pregunto si se dio clic en la columna botón arriba
        If DataGridView1.Columns(e.ColumnIndex).Name = "arriba" Then
                'Se encuentra en la parte superior del grid
                If indice = 0 Then
                        Exit Sub
                End If
                'Se deben de insertar los datos en la fila, campo x campo
                dr(0) = DataGridView1.Rows(indice).Cells("campo0").Value
                dr(1) = DataGridView1.Rows(indice).Cells("campo1").Value
                'Primero se elimina la fila actual
                DataTable.Rows.RemoveAt(indice)
                'Después se inserta la fila copiada una posición arriba
                DataTable.Rows.InsertAt(dr, indice - 1)
                'Posiciona el foco en la posición actual
                DataTable.CurrentCell = DataTable.Rows(indice - 1).Cells(0)
        End If
        'Se repiten los mismos pasos para la columna botón abajo
        'Solo que se cambia el signo
        'Click en la columna botón abajo
        If DataGridView1.Columns(e.ColumnIndex).Name = "abajo" Then
                'Se encuentra en la parte inferior del grid
                If indice = DataGridView1.Rows.Count - 1 Then
                        Exit Sub
                End If
                'Se deben de insertar los datos en la fila, campo x campo
                dr(0) = DataGridView1.Rows(indice).Cells("campo0").Value
                dr(1) = DataGridView1.Rows(indice).Cells("campo1").Value
                'Primero se elimina la fila actual
                DataTable.Rows.RemoveAt(indice)
                'Después se inserta la fila copiada una posición abajo
                DataTable.Rows.InsertAt(dr, indice + 1)
                'Posiciona el foco en la posición actual
                DataTable.CurrentCell = DataTable.Rows(indice + 1).Cells(0)
        End If
End Sub

No se necesita actualizar el BindingSource o el DataGridView, si están enlazados al DataTable, puesto que se hace automáticamente.

Crear una columna de botón DataGridViewButtonColumn con imagen

Gracias a esta página encontré una forma para agregar una imagen a una columna botón de un DataGridView

Crear una columna de botones con imagen usando la clase DataGridViewButtonColumn

El link indicado está en C#, así que lo cambié a VB.Net

Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) _
Handles DataGridView1.CellPainting
        If e.ColumnIndex >= 0 AndAlso DataGridView1.Columns(e.ColumnIndex).Name = "icono" _
        AndAlso e.RowIndex >= 0 Then
                e.Paint(e.CellBounds, DataGridViewPaintParts.All)
                Dim celBoton As DataGridViewButtonCell = _
                TryCast(DataGridView1.Rows(e.RowIndex).Cells("icono"), DataGridViewButtonCell)
                Dim icoAtomico As New Icon(Environment.CurrentDirectory + "\icono.ico")
                e.Graphics.DrawIcon(icoAtomico, e.CellBounds.Left + 3, e.CellBounds.Top + 3)
                DataGridView1.Rows(e.RowIndex).Height = icoAtomico.Height + 10
                DataGridView1.Columns(e.ColumnIndex).Width = icoAtomico.Width + 10
                e.Handled = True
        End If
End Sub

Nota: El icono tiene que estar en la ruta que se encuentra el ejecutable.

Pasar filas de un DataGridView a otro con DataGridViewCheckBoxCell

Gracias a esta página pude encontrar una solución a mi problema
C# - [DataGridView] - Pasar Registros entre Grillas

Problema: Tengo dos formularios, cada uno con un grid. Necesito pasar los datos del grid del form1 al form2, pero solo los registros que estén seleccionados por la columna DataGridViewCheckBoxCell.
Solución: La que indica la página de referencia, pero en VB.Net.

Como indica en el link, existen dos formas de añadir los datos, dependiendo de si están los datos enlazados o no al grid de origen. En mi caso, los datos no están enlazados, y lo hice de la siguiente manera.

Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles button1.Click
       'Se define una lista temporal de registro seleccionados
        Dim FilasSeleccionadas As New List(Of DataGridViewRow)()
          'Se recorre cada registro del grid de origen
        For Each row As DataGridViewRow In DataGridView1.Rows
                    'Se recupera el campo que representa el checkbox,
                    'se valida que esté seleccionado y agregándolo a la lista temporal
                    Dim cellSelecion As DataGridViewCheckBoxCell = _
                    TryCast(row.Cells("Seleccion"), DataGridViewCheckBoxCell)

                    If Convert.ToBoolean(cellSelecion.Value) Then
                        FilasSeleccionadas.Add(row)
                    End If
        Next
        'Se agrega el ítem seleccionado al grid de destino
         'eliminando la fila del grid original
        For Each row As DataGridViewRow In FilasSeleccionadas
                    DataGridView2.Rows.Add(row.Cells("campo1").Value, row.Cells("campo2"))
                    DataGridView1.Rows.Remove(row)
        Next
End Sub

Formas para convertir un número decimal y darle formato

Muchas veces he necesitado convertir mi texto en número, sobre todo en decimal. Para mi caso, necesitaba convertir lo que traía de mis datos a formato decimal, de dos cifras y con separación de miles. Como solo es un decimal de dos cifras (parecido al money en SQL) usé el tipo de dato Single para convertir. Lo primero fue transformar el texto a Single, pero encontré toda una gama de opciones para hacerlo.

Ejemplo:

Dim formato As Single

formato = System.Convert.ToSingle(valor_txt)
formato = CSng(valor_txt)
formato = CType(valor_txt, Single)
formato = Double.Parse(valor_txt)

Cualquiera de ellos transforma un texto a modo Single. Pero para formatearlo a dos decimales y con separación de miles, usé la siguiente opción:

formato = Format(formato, "##,##0.00")

Buscar o validar si existe un dato en el DataGridView

Cuando un usuario está ingresando nuevos datos en un grid, muchas veces necesito validar que lo ingresado no se encuentre repetido entre los otros datos, por ejemplo el código, cédula, serie, marca, modelo, etc. Por lo que he creado una función que busca, entre todos los registros del grid, si dicho dato se repite.

Nota: Recordar que los datos nuevos que se ingresan, se hacen en la ultima fila

Private Function ExisteDatoEnGrid() As Boolean
        Dim UltimaFila As Integer
        Dim cVal as String
       
        'Obtengo la ultima fila para que no se procese hasta el final
        UltimaFila = DataGridView1.Rows.Count - 1
        'Obtengo el valor de la primera columna ingresado por el usuario
        cVal = DataGridView1.Rows(UltimaFila).Cells(0)
       
        'Para cada fila del grid
        For Each row As DataGridViewRow In DataGridView1.Rows
            'Si es la última fila, se termina la función
            If row.Index = UltimaFila Then
                Exit Function
            End If
           
            'Pregunto si el dato de la primera columna es igual a lo ingresado en
            'la ultima fila
            If System.Convert.ToString(row.Cells(0).Value) = cVal Then
                MsgBox("Este dato ya existe", MsgBoxStyle.Critical)
                ExisteDatoEnGrid = True
                Exit Function
            Else
                ExisteDatoEnGrid = False
            End If
        Next
End Function

El uso de esta función es, por ejemplo, cuando se da clic en el botón grabar.

Private Sub button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles button1.Click
        if ExisteDatoEnGrid = True Then
                Exit Sub
        Else
                'Aquí puedo continuar
        End If
End Sub

Recorrer todos los nodos de un TreeView

Necesitaba guardar los cambios del treeview realizados por el usuario, por lo que debo recorrer todo el árbol.

Para ello utilizo la siguiente manera.

Private Sub PrintRecursive(ByVal n As TreeNode)
   System.Diagnostics.Debug.WriteLine(n.Text) 'Muestra el texto del nodo en la ventana de inmediato
   MessageBox.Show(n.Text) 'Muestra el mismo mensaje por pantalla
   
   '*** Es aquí donde añado lo que necesito guardar de cada nodo ***  
   Dim aNode As TreeNode
   'Por cada nodo de la raíz
   For Each aNode In n.Nodes
      PrintRecursive(aNode)
   Next
End Sub

Private Sub CallRecursive(ByVal aTreeView As TreeView)
   Dim n As TreeNode
   'Por cada raíz
   For Each n In aTreeView.Nodes
      PrintRecursive(n)
   Next
End Sub

Obtener la ruta de un TreeView y modificarlo

Para guardarla entre mis datos, necesito la ruta completa de un nodo en particular.

Para obtener la ruta de un nodo, hago lo siguiente:

Dim RutaCompleta As String = TreeNode.FullPath
'La variable RutaCompleta más o menos contendrá (dependiendo de la ruta): Raíz\Nodopadre\NodoHijo\ ... etc

Hasta allí estoy bien, pero sucede que la ruta la necesito con el signo de división (/) para poder guardarlo, así que utilizo una función.

Dim RutaModificada As String = Replace(RutaCompleta, "\", "/")

Con esto la variable RutaModificada queda así: Raíz/NodoPadre/NodoHijo/ ... etc

Limpiar TreeView

Muchísimas gracias a esta página que me ayudó con la duda que tenía.

Limpiar TreeView Manual de Referencia

Repito lo que se encuentra en esa página para mayor comodidad.

Eliminar todos los nodos de un TreeView:

TreeView1.Nodes.Clear

Eliminar un determinado nodo de un TreeView:

TreeView1.Nodes.Remove 123
TreeView1.Nodes.Remove "abc"
'(donde 123 y "abc" representan el índice o la clave del nodo a eliminar)

No agradezco directamente en esa página porque es un foro de un tema cerrado desde hace mucho tiempo.