none
DataGridView Presentar un Combobox o un TextBox en la misma columna del grid según el valor de un campo del datasource RRS feed

  • Pregunta

  • Hola a todos y gracias de antemano

    Estoy implementando una celda personaliza de un DataGridView, la particularidad de esta celda es que el contenido que debe mostrar al usuario depende de otra columna del grid.

    Según el valor que exista en esa columna mostrará o bien una celda del tipo Combobox o bien una celda del tipo TextBox.

    Lo primero es crear un tipo de columna personalizada que hereda de una columna combo

    Imports System.Windows.Forms
    Public Class ComboTextBoxGridColumn
        Inherits DataGridViewComboBoxColumn
        Sub New()
            Dim template As ComboTextBoxGridCell = New ComboTextBoxGridCell()
            template.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton
            template.FlatStyle = Windows.Forms.FlatStyle.Popup
            Me.CellTemplate = template      
            Me.Width = Width       
        End Sub  
    End Class

    Lo siguiente es pintar nuestra celda personalizada

    Imports System.Windows.Forms
    Imports System.Drawing
    Public Class ComboTextBoxGridCell
        Inherits DataGridViewComboBoxCell
        Private _cntOffset As Integer
        Public Enum TipoControl As Integer
            ComboBox = 1
            TextBox = 2
        End Enum
        Sub New()
            _cntOffset = 1
            'MyBase.ValueType = System.Type.GetType("System.String")
        End Sub
        Protected Overrides Sub OnMouseClick(e As DataGridViewCellMouseEventArgs)
            'MyBase.OnMouseClick(e)
           
        End Sub
        Protected Overrides Sub OnContentClick(e As DataGridViewCellEventArgs)
            'MyBase.OnContentClick(e)

        End Sub
        Protected Overrides Sub OnClick(e As DataGridViewCellEventArgs)
            'MyBase.OnClick(e)
        End Sub
        Protected Overrides Sub OnEnter(rowIndex As Integer, throughMouseClick As Boolean)
            ' MyBase.OnEnter(rowIndex, throughMouseClick)
            SelectedStyle()

            Dim fila As DataGridViewRow = Me.DataGridView.Rows(rowIndex)
            If fila IsNot Nothing AndAlso Not IsDBNull(fila) Then
                Dim txtText As String = String.Empty
                Dim val As Object = fila.Cells(Me.ColumnIndex).Value
                If val IsNot Nothing AndAlso Not IsDBNull(val) Then
                    txtText = val.ToString()
                End If

            End If

        End Sub
        Private Sub SelectedStyle()
            Dim fc As Color = Me.DataGridView.DefaultCellStyle.SelectionForeColor
            Dim bc As Color = Me.DataGridView.DefaultCellStyle.SelectionBackColor
            Me.Style.BackColor = bc
            Me.Style.ForeColor = fc
        End Sub
        Private Sub LeaveStyle()
            Dim fc As Color = Me.DataGridView.DefaultCellStyle.ForeColor
            Dim bc As Color = Me.DataGridView.DefaultCellStyle.BackColor
            Me.Style.BackColor = bc
            Me.Style.ForeColor = fc
        End Sub
        Protected Overrides Sub Paint( _
                                     graphics As Drawing.Graphics, _
                                     clipBounds As Drawing.Rectangle, _
                                     cellBounds As Drawing.Rectangle, _
                                     rowIndex As Integer, cellState As DataGridViewElementStates, _
                                     value As Object, _
                                     formattedValue As Object, _
                                     errorText As String, _
                                     cellStyle As DataGridViewCellStyle, _
                                     advancedBorderStyle As DataGridViewAdvancedBorderStyle, _
                                     paintParts As DataGridViewPaintParts)
            ' Pintar el fondo de la celda
            If (paintParts And DataGridViewPaintParts.Background) = DataGridViewPaintParts.Background Then
                If (rowIndex Mod 2) <> 0 Then
                    'Es una fila Alterna
                    If Inicializacion.cfgUsr.ColorFondoFilasAlternas <> 0 Then
                        Dim cellBackground As New SolidBrush(Color.FromArgb(Inicializacion.cfgUsr.ColorFondoFilasAlternas))
                        graphics.FillRectangle(cellBackground, cellBounds)
                        cellBackground.Dispose()
                    Else
                        Dim cellBackground As New SolidBrush(cellStyle.BackColor)
                        graphics.FillRectangle(cellBackground, cellBounds)
                        cellBackground.Dispose()
                    End If
                Else
                    Dim cellBackground As New SolidBrush(cellStyle.BackColor)
                    graphics.FillRectangle(cellBackground, cellBounds)
                    cellBackground.Dispose()
                End If
            End If
            ' Pintar el borde de la celda.
            If (paintParts And DataGridViewPaintParts.Border) = DataGridViewPaintParts.Border Then
                MyBase.PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
            End If
            'Obtenemos la fila actual del grid
            Dim fila As DataGridViewRow = Me.DataGridView.Rows(rowIndex)
            If fila IsNot Nothing AndAlso Not IsDBNull(fila) Then
                If Not fila.Cells("TESTIPORES").Value Is Nothing AndAlso Not IsDBNull(fila.Cells("TESTIPORES").Value) Then
                    Dim valCompare As String = fila.Cells("TESTIPORES").Value.ToString()
                    If IsNumeric(valCompare) Then
                        'convierte el valor a comparar a númerico para hacer la comparación contra la enumeración
                        Dim intcompre As Integer = valCompare
                        If intcompre.Equals(TipoControl.ComboBox) Then
                            'Aprobechamos el método de la clase base para pintar el combobox
                            MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
                        ElseIf intcompre.Equals(TipoControl.TextBox) Then
                            'Personalizamos el renderizado del control para pintar un control TextBox
                            '
                            'Establecer propiedades de la celda
                            'fila.Cells(Me.ColumnIndex).ReadOnly = True
                            'fila.Cells(Me.ColumnIndex).Style = cellStyle
                            'Obtener la fuente definida en la celda
                            Dim fnt As Font = fila.Cells(Me.ColumnIndex).Style.Font
                            'Obtener el texto que debe aparecer en el TextBox
                            Dim txtText As String = String.Empty
                            Dim val As Object = fila.Cells(Me.ColumnIndex).Value
                            If val IsNot Nothing AndAlso Not IsDBNull(val) Then
                                txtText = val.ToString()
                            End If
                            ' Calcular el area del TextBox usando el algoritmo para ajustar los bordes1
                            Dim cntArea As Rectangle = cellBounds
                            Dim cntAdjustment As Rectangle = BorderWidths(advancedBorderStyle)
                            cntArea.X += cntAdjustment.X - _cntOffset
                            cntArea.Y += cntAdjustment.Y - _cntOffset
                            cntArea.Height += cntAdjustment.Height
                            cntArea.Width += cntAdjustment.Width
                            'Dibujar el TextBox
                            TextBoxRenderer.DrawTextBox(graphics, cntArea, txtText, fnt, VisualStyles.TextBoxState.Normal)
                           
                        End If
                    End If
                End If
            End If

        End Sub
    End Class

    Hasta aquí todo bien... según el valor de la fila para la columna TESTIPORES se pinta un combo o un textbox.

    El problema viene a la hora de ponerte encima de la celda para editar el contenido.

    El problema es que no se que debo hacer para simular el comportamiento de un Textbox al posicionarme dentro de la celda.

    Alguien puede orientarme un poco?

    Gracias de antemano.

    • Cambiado Enrique M. Montejo jueves, 13 de octubre de 2016 8:50 Pregunta relacionada con controles de Windows Forms.
    martes, 11 de octubre de 2016 15:47

Todas las respuestas

  • He avanzado un poquito, pero todavía tengo algunos problemillas....

    Hasta ahora ya pinto correctamente el TextBox incluso dejo el texto seleccionado una vez entras dentro del TextBox, pero no se como mostrar el cursor y tampoco me llegan las pulsaciones de teclas a sus respectivos eventos...

    Cualquier ayuda será bien recibida ;)

    Imports System.Windows.Forms
    Imports System.Drawing
    Public Class ComboTextBoxGridCell
        Inherits DataGridViewComboBoxCell
        Private _cntOffset As Integer
        Private enter As Boolean = False
        Public Enum TipoControl As Integer
            ComboBox = 1
            TextBox = 2
        End Enum
        Sub New()
            _cntOffset = 1
        End Sub
        'Public Overrides Function KeyEntersEditMode(e As KeyEventArgs) As Boolean
        '    If Not enter Then
        '        Return MyBase.KeyEntersEditMode(e)
        '    End If
        '    Return False
        'End Function
        'Public Overrides Sub InitializeEditingControl(rowIndex As Integer, initialFormattedValue As Object, dataGridViewCellStyle As DataGridViewCellStyle)
        '    If Not enter Then
        '        MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
        '    End If
        'End Sub

        Protected Overrides Sub OnLeave(rowIndex As Integer, throughMouseClick As Boolean)
            If Not enter Then
                MyBase.OnLeave(rowIndex, throughMouseClick)
            Else
                enter = False
            End If
        End Sub
        Protected Overrides Sub OnMouseClick(e As DataGridViewCellMouseEventArgs)
            If Not enter Then
                MyBase.OnMouseClick(e)
            End If
        End Sub
        Protected Overrides Sub OnContentClick(e As DataGridViewCellEventArgs)
            If Not enter Then
                MyBase.OnContentClick(e)
            End If
        End Sub
        Protected Overrides Sub OnEnter(rowIndex As Integer, throughMouseClick As Boolean)
            Dim fila As DataGridViewRow = Me.DataGridView.Rows(rowIndex)
            If fila IsNot Nothing AndAlso Not IsDBNull(fila) Then
                If Not fila.Cells("TESTIPORES").Value Is Nothing AndAlso Not IsDBNull(fila.Cells("TESTIPORES").Value) Then
                    Dim valCompare As String = fila.Cells("TESTIPORES").Value.ToString()
                    If IsNumeric(valCompare) Then
                        Dim intcompre As Integer = valCompare
                        If intcompre.Equals(TipoControl.ComboBox) Then
                            enter = False
                            Dim pres As New Presentacion()
                            Dim dt As DataTable = Me.DataSource
                            Dim vista As New DataView(dt)
                            vista.RowFilter = String.Format("UNITIPO = '{0}'", fila.Cells("TESTIPOUNI").Value.ToString())
                            dt = vista.ToTable()
                            dt = pres.NingunaOpcion(dt, "F", "IDENTIFICADOR", "UNIID")
                            Me.DataSource = dt
                            MyBase.OnEnter(rowIndex, throughMouseClick)
                        Else
                            enter = True
                        End If
                    End If
                End If
            End If
        End Sub
        Protected Overrides Sub Paint( _
                                     graphics As Drawing.Graphics, _
                                     clipBounds As Drawing.Rectangle, _
                                     cellBounds As Drawing.Rectangle, _
                                     rowIndex As Integer, cellState As DataGridViewElementStates, _
                                     value As Object, _
                                     formattedValue As Object, _
                                     errorText As String, _
                                     cellStyle As DataGridViewCellStyle, _
                                     advancedBorderStyle As DataGridViewAdvancedBorderStyle, _
                                     paintParts As DataGridViewPaintParts)
            ' Pintar el fondo de la celda
            If (paintParts And DataGridViewPaintParts.Background) = DataGridViewPaintParts.Background Then
                If Not Me.Selected Then
                    If enter Then
                        cellBounds.Inflate((_cntOffset + _cntOffset) * -1, (_cntOffset + _cntOffset) * -1)
                    End If
                    If (rowIndex Mod 2) <> 0 Then
                        'Es una fila Alterna
                        If Inicializacion.cfgUsr.ColorFondoFilasAlternas <> 0 Then
                            Dim cellBackground As New SolidBrush(Color.FromArgb(Inicializacion.cfgUsr.ColorFondoFilasAlternas))
                            graphics.FillRectangle(cellBackground, cellBounds)
                            cellBackground.Dispose()
                        Else
                            Dim cellBackground As New SolidBrush(cellStyle.BackColor)
                            graphics.FillRectangle(cellBackground, cellBounds)
                            cellBackground.Dispose()
                        End If
                    Else
                        Dim cellBackground As New SolidBrush(cellStyle.BackColor)
                        graphics.FillRectangle(cellBackground, cellBounds)
                        cellBackground.Dispose()
                    End If
                Else
                    'Si la celda esta seleccionada
                    Dim cellBackground As New SolidBrush(Color.FromArgb(Inicializacion.cfgUsr.ColorFondoRowSelectedGrid))
                    graphics.FillRectangle(cellBackground, cellBounds)
                    cellBackground.Dispose()
                End If
            End If
            ' Pintar el borde de la celda.
            If (paintParts And DataGridViewPaintParts.Border) = DataGridViewPaintParts.Border Then
                MyBase.PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
            End If
            'Obtenemos la fila actual del grid
            Dim fila As DataGridViewRow = Me.DataGridView.Rows(rowIndex)
            If fila IsNot Nothing AndAlso Not IsDBNull(fila) Then
                If Not fila.Cells("TESTIPORES").Value Is Nothing AndAlso Not IsDBNull(fila.Cells("TESTIPORES").Value) Then
                    Dim valCompare As String = fila.Cells("TESTIPORES").Value.ToString()
                    If IsNumeric(valCompare) Then
                        'convierte el valor a comparar a númerico para hacer la comparación contra la enumeración
                        Dim intcompre As Integer = valCompare
                        If intcompre.Equals(TipoControl.ComboBox) Then
                            'Aprobechamos el método de la clase base para pintar el combobox
                            MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
                        ElseIf intcompre.Equals(TipoControl.TextBox) Then
                            'Personalizamos el renderizado del control para pintar un control TextBox
                            '
                            'Obtener la fuente definida en la celda
                            Dim fnt As Font = Me.DataGridView.Font
                            'Obtener el texto que debe aparecer en el TextBox
                            Dim txtText As String = String.Empty
                            Dim val As Object = fila.Cells(Me.ColumnIndex).Value
                            If val IsNot Nothing AndAlso Not IsDBNull(val) Then
                                txtText = val.ToString()
                            End If
                            ' Calcular el area del TextBox usando el algoritmo para ajustar los bordes1
                            Dim cntArea As Rectangle = cellBounds
                            Dim cntAdjustment As Rectangle = BorderWidths(advancedBorderStyle)
                            cntArea.X += cntAdjustment.X
                            cntArea.Y += cntAdjustment.Y
                            cntArea.Height -= cntAdjustment.Height
                            cntArea.Width -= cntAdjustment.Width
                            cntArea.Inflate(_cntOffset * -1, _cntOffset * -1)
                            'Dibujar el TextBox
                            If enter Then
                                Dim colorText As Color = Color.FromArgb(Inicializacion.cfgUsr.ColorTextoRowSelectedGrid)
                                Dim colorFondo As Color = Color.FromArgb(Inicializacion.cfgUsr.ColorFondoRowSelectedGrid)
                                TextBoxRenderer.DrawTextBox(graphics, cntArea, VisualStyles.TextBoxState.Normal)
                                Dim pointText As New Point(cntArea.Location.X + _cntOffset * 2, cntArea.Location.Y + _cntOffset * 2)
                                TextRenderer.DrawText(graphics, txtText, fnt, pointText, colorText, colorFondo)
                            Else
                                TextBoxRenderer.DrawTextBox(graphics, cntArea, txtText, fnt, VisualStyles.TextBoxState.Normal)
                            End If
                        End If
                    End If
                End If
            End If

        End Sub
    End Class

    viernes, 14 de octubre de 2016 7:20