Cómo simular un Grid con celdas editables

Usando el control FlexGrid

Publicado: 17/May/2001
Actualizado: 17/May/2001


En esta ocasión vamos a ver el código que podemos usar para simular un Grid con casillas editables.
En el ejemplo veremos cómo usar tanto un control TextBox como un comboBox.
como verás en el código es fácil decidir si debemos mostrar el textbox o el combo... dependiendo de lo que necesitemos usar... en caso de que necesites usar diferentes combos, te recomiendo que uses un array del control combo1 para que te resulte fácil de interceptar las pulsaciones y otras acciones con dichos controles... puede que en otra ocasión amplíe este ejemplo con esto que digo, además de usar un control checkbox... pero eso será en otra ocasión... así que... paciencia.

Te explico lo que el código hace... aunque puedes seguirlo sin problemas, eso espero, con los comentarios incluidos en el código de ejemplo.

En principio el Grid sólo tendrá una fila "disponible", cuando pulsamos en una de las celdas de la última fila, la cual está identificada con ">>*", se creará una nueva fila... cuando pruebes el código de ejemplo, sabrás de que estoy hablando.

Para introducir algo en cualquiera de las celdas, podemos hacerlo de varias formas:
-haciendo doble-click en la celda a editar,
-escribiendo directamente en el grid,
-pulsando la tecla F2

Para aceptar lo que hemos escrito, pulsaremos Intro o simplemente haciendo Click en cualquier otra celda.
Si se pulsa ESC, se cancela la edición de la celda.

En el código mostrado, uso dos procedimientos para guardar el contenido del grid en un fichero de texto y para leer de ese fichero y asignarlo al grid. Este código tendrás que adaptarlo a los campos que tengas en el grid, aunque usado tal y como lo muestro te puede ser útil al 100%.

Bueno, me dejo de "cháchara" y te muestro el código.

El formulario contiene los siguientes controles:
Un control FlexGrid llamado Grid2,
un TextBox llamado Text1,
un ComboBox llamado Combo1,
un PictureBox llamado picStatus con la propiedad Align = 2 (para que se ajuste a la parte inferior),
una etiqueta llamada lblStatus, (insertada en el picStatus),
un botón llamado cmdSalir, (insertado en el picStatus).

Nota:
No tienes que preocuparte por "insertar" los dos últimos controles mencionados en el PictureBox, ya que eso se hace en el código del evento Form_Load

Como bono extra, una función para "interpretar" fechas y asignar el formato que queramos a partir de varias formas de entrada, por ejemplo, acepta fechas con y sin separadores, e incluso sin indicar el año...

 

Aquí tienes el código:


'------------------------------------------------------------------------------
' Prueba de Grid con celdas editables                               (09/May/01)
' Revisado: 17/May/2001
'
' Ejemplo de código con TextBox y ComboBox
'
' ©Guillermo 'guille' Som, 2001
'------------------------------------------------------------------------------
Option Explicit

Private sFicDatos As String         ' Fichero con los datos del grid
Const cNuevaFila As String = ">>*"  ' Para indicar que es una nueva fila
Private ControlVisible As Boolean   ' Si el control está o no visible (editándose)
Private LastRow As Long             ' La última fila en que se editó
Private LastCol As Long             ' La última columna en que se editó

Private Sub cmdSalir_Click()
    Unload Me
End Sub

Private Sub Combo1_Change()
    If Combo1.Visible Then
        Grid2.TextMatrix(LastRow, LastCol) = Combo1.Text
        AsignarCelda
    End If
End Sub

Private Sub Combo1_Click()
    Combo1_Change
End Sub

Private Sub Combo1_KeyPress(KeyAscii As Integer)
    If KeyAscii = vbKeyReturn Then
        KeyAscii = 0
        AsignarCelda
        SiguienteCelda
    ElseIf KeyAscii = vbKeyEscape Then
        KeyAscii = 0
        Combo1.Visible = False
    End If
End Sub

Private Sub Form_Load()
    Dim i As Long
    Dim s As String
    '
    picStatus.Height = 585
    With lblStatus
        .Caption = " Código de ejemplo: ©Guillermo 'guille' Som, 2001 <mensaje@elguille.info>"
        Set .Container = picStatus
        .Height = 285
        .BorderStyle = vbFixedSingle
        .Left = 90
        .Top = 120
        .Visible = True
    End With
    With cmdSalir
        Set .Container = picStatus
        .Height = 405
        .Top = 60
        .Visible = True
    End With
    '
    s = App.Path
    sFicDatos = s & IIf(Right$(s, 1) = "\", "", "\") & "PruebaGrid.txt"
    '
    With Combo1
        .Clear
        For i = 1 To 20
            .AddItem i
        Next
    End With
    '
    OcultarControles
    '
    CabeceraGrid
    LeerDatos
End Sub

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    ' Guardar los datos del grid
    GuardarDatos
End Sub

Private Sub Form_Resize()
    ' Reajustar el tamaño del grid al de la ventana
    ' Si tuviesemos otro control, por ejemplo una barra de estado,
    ' restarle el ancho de la misma al Grid
    If WindowState <> vbMinimized Then
        Grid2.Move 0, 0, ScaleWidth, ScaleHeight - picStatus.Height
        With cmdSalir
            .Left = picStatus.ScaleWidth - .Width - 90
            lblStatus.Width = .Left - 120 - lblStatus.Left
        End With
    End If
End Sub

Private Sub Grid2_Click()
    ' Cuando se hace un sólo click en otra columna,
    ' asigna el valor seleccionado, (como si se pulsara intro)
    AsignarCelda
End Sub

Private Sub Grid2_DblClick()
    ' Editar al hacer dobleclick
    LastRow = Grid2.Row
    LastCol = Grid2.Col
    '
    OcultarControles
    '
    MostrarCelda
End Sub

Private Sub Grid2_KeyDown(KeyCode As Integer, Shift As Integer)
    ' Editar si se pulsa F2
    If KeyCode = vbKeyF2 Then
        MostrarCelda
    ElseIf KeyCode = vbKeyDelete Then
        ' Borrar las filas seleccionadas                            (13/May/01)
        BorrarFilas
    End If
End Sub

Private Sub Grid2_KeyPress(KeyAscii As Integer)
    Select Case KeyAscii
    ' Si se pulsa Intro, editar la celda
    Case vbKeyReturn
        KeyAscii = 0
        MostrarCelda
    ' Cancelar si se pulsa ESC
    Case vbKeyEscape
        KeyAscii = 0
        AsignarCelda
    ' Si se pulsa cualquier letra, editar la celda
    Case 32 To 255
        MostrarCelda
        With Text1
            If .Visible Then
                '.Text = .Text & Chr$(KeyAscii)
                .Text = Chr$(KeyAscii)
                .SelStart = Len(.Text) + 1
            End If
        End With
    End Select
End Sub

Private Sub Grid2_Scroll()
    ' Comprobar si la columna en la que está el control está visible
    ' si es así, ocultar los controles
    '
    If Grid2.ColIsVisible(LastCol) = False Then
        OcultarControles
        Exit Sub
    End If
    If Grid2.RowIsVisible(LastRow) = False Then
        OcultarControles
        Exit Sub
    End If
    ' Comprobar si estaba visible antes de ocultarlo
    ' y posicionarlo en la misma celda
    If ControlVisible Then
        MostrarCelda
    End If
End Sub

Private Sub MostrarCelda()
    Static YaEstoy As Boolean
    '
    ' Salir si es una de las celdas fijas
    If Grid2.Col <= Grid2.FixedCols - 1 Or Grid2.Row <= Grid2.FixedRows - 1 Then
        Exit Sub
    End If
    '
    If YaEstoy Then Exit Sub
    YaEstoy = True
    '
    OcultarControles
    '
    LastRow = Grid2.Row
    LastCol = Grid2.Col
    '
    ' Si es una nueva celda
    With Grid2
        If .TextMatrix(LastRow, 0) = cNuevaFila Then
            .Rows = .Rows + 1
            .TextMatrix(LastRow, 0) = LastRow
            .TextMatrix(.Rows - 1, 0) = cNuevaFila
        End If
    End With
    '
    Select Case LastCol
    Case 2
        Combo1.Text = Grid2.TextMatrix(LastRow, LastCol)
        Combo1.Move Grid2.CellLeft - Screen.TwipsPerPixelX, Grid2.CellTop - Screen.TwipsPerPixelY
        Combo1.Width = Grid2.CellWidth + Screen.TwipsPerPixelX * 2
        Combo1.Visible = True
        Combo1.ZOrder
        Combo1.SetFocus
    Case Else
        Text1.Move Grid2.CellLeft - Screen.TwipsPerPixelX, Grid2.CellTop - Screen.TwipsPerPixelY, Grid2.CellWidth + Screen.TwipsPerPixelX * 2, Grid2.CellHeight + Screen.TwipsPerPixelY * 2
        Text1.Text = Grid2.Text
        If Len(Grid2.Text) = 0 Then
            If LastRow > 1 Then
                Text1.Text = Grid2.TextMatrix(LastRow - 1, LastCol)
            End If
        End If
        Text1.Visible = True
        If Text1.Visible Then
            Text1.ZOrder
            Text1.SetFocus
        End If
    End Select
    '
    ControlVisible = True
    '
    YaEstoy = False
End Sub

Private Sub SiguienteCelda()
    If Grid2.Col < Grid2.Cols - 1 Then
        Grid2.Col = Grid2.Col + 1
    Else
        Grid2.Col = 1
        If Grid2.Row < Grid2.Rows - 1 Then
            Grid2.Row = Grid2.Row + 1
        End If
    End If
End Sub

Private Sub Text1_GotFocus()
    With Text1
        ' Posicionar el cursor al final
        .SelStart = Len(.Text)
    End With
End Sub

Private Sub Text1_KeyPress(KeyAscii As Integer)
    ' Si se pulsa Intro, aceptar lo que se ha escrito
    If KeyAscii = vbKeyReturn Then
        KeyAscii = 0
        AsignarCelda
        SiguienteCelda
    ' Si se pulsa ESC, cancelar la edición
    ElseIf KeyAscii = vbKeyEscape Then
        KeyAscii = 0
        Text1.Visible = False
        ControlVisible = False
    End If
End Sub

Private Sub AsignarCelda()
    ' Asignar al grid el texto escrito o seleccionado del combo
    Dim s As String
    '
    OcultarControles
    ControlVisible = False
    '
    ' Asignar el texto anterior a la celda
    Select Case LastCol
    Case 2
        '
        Grid2.TextMatrix(LastRow, LastCol) = Combo1.Text
    Case Else
        s = Text1.Text
        ' si es la columna de la fecha...
        If LastCol = 1 Then ' Fecha
            s = AjustarFecha(s)
        End If
        Grid2.TextMatrix(LastRow, LastCol) = s
    End Select
End Sub

Private Function AjustarFecha(ByVal sFecha As String) As String
    ' Ajustar la cadena introducida a formato de fecha              (27/Abr/01)
    Dim i As Long
    Dim s As String
    '
    If sFecha = "" Then
        AjustarFecha = ""
        Exit Function
    End If
    '
    'On Error Resume Next
    On Error GoTo 0
    '
    ' Comprobar si se usan puntos como separador
    ' si es así, cambiarlos por /
    Do
        i = InStr(sFecha, ".")
        If i Then
            Mid$(sFecha, i, 1) = "/"
        End If
    Loop While i
    '
    ' Comprobar si se usan - como separador
    ' si es así, cambiarlos por /
    Do
        i = InStr(sFecha, "-")
        If i Then
            Mid$(sFecha, i, 1) = "/"
        End If
    Loop While i
    '
    s = ""
    Do
        i = InStr(sFecha, "/")
        If i Then
            s = s & Right$("0" & Left$(sFecha, i - 1), 2) & "/"
            sFecha = Mid$(sFecha, i + 1)
        End If
    Loop While i
    sFecha = s & sFecha
    '
    If InStr(sFecha, "/") Then
        If Len(sFecha) = 5 Then
            ' Si es igual a 5 caracteres, es que falta el año
            sFecha = sFecha & "/"
        ElseIf Len(sFecha) < 3 Then
            ' Si es menor de 3 caracteres es que falta el mes
            sFecha = sFecha & "/" & CStr(Month(Now)) & "/"
        End If
    ElseIf Len(sFecha) < 3 Then
        sFecha = sFecha & "/" & CStr(Month(Now)) & "/"
    Else
        s = ""
        For i = 1 To 2
            s = s & "/" & Mid$(sFecha, (i - 1) * 2 + 1, 2)
        Next
        s = s & "/" & Mid$(sFecha, 5)
        sFecha = s
    End If
    sFecha = Trim$(sFecha)
    '
    ' Comprobar si tiene una barra al principio, si es así, quitarla
    If Left$(sFecha, 1) = "/" Then
        sFecha = Mid$(sFecha, 2)
    End If
    ' Si tiene una barra al final, es que falta el año
    If Right$(sFecha, 1) = "/" Then
        sFecha = sFecha & CStr(Year(Now))
    End If
    '
    ' Convertir la fecha, por si no se especifican todos los caracteres
    ' Nota: Aquí puedes usar el formato que más te apetezca
    sFecha = Format$(sFecha, "dd/mm/yyyy")
    '
'    ' Si no es una fecha correcta...
'    If IsDate(sFecha) = False Then
'        AjustarFecha = sFecha
'    Else
'        AjustarFecha = sFecha
'    End If
    '
    Err = 0
    '
    AjustarFecha = sFecha
End Function

Private Sub CabeceraGrid()
    ' Asignar las cabeceras del grid y asignación de valores predeterminados
    Dim i As Long
    '
    With Grid2
        .FixedRows = 1
        .FixedCols = 1
        .ScrollBars = flexScrollBarBoth
        .AllowUserResizing = flexResizeColumns
        .Cols = 11                  ' Número de columnas, contando la cabecera
        .Rows = 2                   ' Número de filas, contando la cabecera
                                    ' el número de filas se asignará dinámicamente
        .ColWidth(0) = 600          ' El ancho de la columna 0
        '
        ' Asignar los nombres de las cabeceras y el ancho de las columnas
        .TextArray(1) = "Fecha"
        .ColWidth(1) = 1100
        .TextArray(2) = "Número"
        .ColWidth(2) = 900
        .TextArray(3) = "Nombre"
        .ColWidth(3) = 1500
        .TextArray(4) = "Apellidos"
        .ColWidth(4) = 2000
        .TextArray(5) = "Domicilio"
        .ColWidth(5) = 2500
        .TextArray(6) = "Población"
        .ColWidth(6) = 2000
        .TextArray(7) = "Provincia"
        .ColWidth(7) = 1600
        .TextArray(8) = "Teléfonos"
        .ColWidth(8) = 1500
        .TextArray(9) = "e-mail"
        .ColWidth(9) = 1200
        .TextArray(10) = "Observaciones"
        .ColWidth(10) = 2500
        '
        ' Mostrar los números en las filas
        For i = 1 To .Rows - 1
            .TextMatrix(i, 0) = i
        Next
        '
        ' Esto indicará que es una nueva fila
        ' (asignarla a la primera columna de la última fila)
        .TextMatrix(.Rows - 1, 0) = cNuevaFila
    End With
End Sub

Private Sub BorrarFilas()
    ' Borrar las filas seleccionadas                                (13/May/01)
    Dim i As Long
    Dim j As Long
    Dim k As Long
    Dim n As Long
    '
    ' Si está seleccionada la última fila, no borrarla
    If Grid2.RowSel = Grid2.Rows - 1 Then
        Beep
        Exit Sub
    End If
    If Grid2.Row = Grid2.Rows - 1 Then
        Beep
        Exit Sub
    End If
    '
    ' Borrar siempre desde la fila mayor a la menor
    i = Grid2.Row
    j = Grid2.RowSel
    If i < j Then
        k = i
        i = j
        j = k
    End If
    For n = i To j Step -1
        Grid2.RemoveItem n
    Next
    LastRow = Grid2.Rows - 1
    LastCol = 1
    Grid2.Col = LastCol
    Grid2.Row = LastRow
    Grid2.RowSel = LastRow
    Grid2.ColSel = LastCol
End Sub

Private Sub OcultarControles()
    ' Ocultar los controles de edición                              (17/May/01)
    ' (aunque son pocos, se pueden ampliar y así es más fácil saber dónde
    ' poner el código para ocultarlos)
    Text1.Visible = False
    Combo1.Visible = False
End Sub

Private Sub LeerDatos()
    ' Leer los datos y asignarlos al grid
    Dim nFic As Long
    Dim r As Long
    Dim c As Long
    Dim s As String
    '
    ' Si no existe el fichero, nada que hacer...
    If Len(Dir$(sFicDatos)) = 0 Then Exit Sub
    '
    r = Grid2.Rows - 2
    nFic = FreeFile
    Open sFicDatos For Input As nFic
    Do While Not EOF(nFic)
        r = r + 1
        Grid2.Rows = r + 2
        Grid2.TextMatrix(r, 0) = r
        For c = 1 To Grid2.Cols - 1
            If Not EOF(nFic) Then
                Line Input #nFic, s
                Grid2.TextMatrix(r, c) = s
            Else
                Exit For
            End If
        Next
    Loop
    Close nFic
    '
    With Grid2
        .TextMatrix(.Rows - 1, 0) = cNuevaFila
        LastRow = .Rows - 1
        LastCol = 1
        .Col = LastCol
        .Row = LastRow
        .RowSel = LastRow
        .ColSel = LastCol
    End With
End Sub

Private Sub GuardarDatos()
    ' Guardar los datos del Grid
    Dim nFic As Long
    Dim r As Long
    Dim c As Long
    '
    nFic = FreeFile
    Open sFicDatos For Output As nFic
    ' No guardar la última fila, (si se usa el indicador de nueva fila)
    For r = 1 To Grid2.Rows - 2
        For c = 1 To Grid2.Cols - 1
            Print #nFic, Grid2.TextMatrix(r, c)
        Next
    Next
    Close nFic
End Sub

Para bajarte el código de ejemplo, pulsa en este link. (GridEdit.zip 5.50 KB)

 

Nos vemos.
Guillermo


ir al índice