.net - how to display custom comboxbox in event DataGridView1 EditingControlShowing with VB.NET - Stack Overflow

admin2025-05-02  1

I'm trying to display custom comboxbox in event DataGridView1 EditingControlShowing with VB.NET

One more thing I want the column to not display the ComboBox Column but the TextBox Column. Is it possible to apply?

The result of my code does not appear the custom component when I edit in the DatagridView event.

Maybe the code I posted is still wrong

so for column type DataGridViewTextBoxColumn in header textProduct Name cmb in design name ProductNamecmb I want to display custom combobox in EditingControlShowing event datagridview.

Please Guide me

Thanks

Information

column type DataGridViewTextBoxColumn
in header text `Product Name cmb`
in design name `ProductNamecmb`
column type DataGridViewTextBoxColumn
in header text `Product Name txt`
in design name `ProductNametxt`

Code in form1

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim row() As String = {"1", "Product 1", "Product 1", "1000"}
        DataGridView1.Rows.Add(row)
        row = New String() {"2", "Product 2", "Product 2", "2000"}
        DataGridView1.Rows.Add(row)
        row = New String() {"3", "Product 3", "Product 3", "3000"}
        DataGridView1.Rows.Add(row)
        row = New String() {"4", "Product 4", "Product 4", "4000"}
        DataGridView1.Rows.Add(row)
    End Sub
    Public Sub addItems(ByVal col As AutoCompleteStringCollection)
        col.Add("Product 1")
        col.Add("Product 2")
        col.Add("Product 3")
        col.Add("Product 4")
        col.Add("Product 5")
        col.Add("Product 6")
    End Sub

    Private Sub DataGridView1_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
        Dim titleText As String = DataGridView1.Columns("ProductNametxt").HeaderText
        If titleText.Equals("Product Name txt") Then
            Dim autoText As TextBox = TryCast(e.Control, TextBox)
            If autoText IsNot Nothing Then
                autoText.AutoCompleteMode = AutoCompleteMode.Suggest
                autoText.AutoCompleteSource = AutoCompleteSource.CustomSource
                Dim DataCollection As New AutoCompleteStringCollection()
                addItems(DataCollection)
                autoText.AutoCompleteCustomSource = DataCollection
            End If
        End If
        Dim titlecmb As String = DataGridView1.Columns("ProductNamecmb").HeaderText
        If titlecmb.Equals("Product Name cmb") Then
            Dim autoText As FlatComboBox = TryCast(e.Control, FlatComboBox)
            If autoText IsNot Nothing Then
                autoText.DropDownStyle = ComboBoxStyle.DropDown
                autoText.AutoCompleteMode = AutoCompleteMode.Suggest
                autoText.AutoCompleteSource = AutoCompleteSource.CustomSource
                Dim DataCollection As New AutoCompleteStringCollection()
                addItems(DataCollection)
                autoText.AutoCompleteCustomSource = DataCollection
            End If
        End If

    End Sub
    Private Sub DataGridView1_DataError(sender As Object, e As DataGridViewDataErrorEventArgs) Handles DataGridView1.DataError
        If e.Context = DataGridViewDataErrorContexts.Commit Then

        End If
    End Sub
End Class

Code in custom component

Imports System.ComponentModel
Imports System.Runtime.InteropServices

Public Class FlatComboBox
    Inherits ComboBox
    Private _borderColor As Color = Color.Green
    <DefaultValue(GetType(Color), "Green")>
    Public Property BorderColor As Color
        Get
            Return _borderColor
        End Get
        Set(ByVal value As Color)
            If _borderColor <> value Then
                _borderColor = value
                Invalidate()
            End If
        End Set
    End Property
    Private _buttonColor As Color = Color.LightGray
    <DefaultValue(GetType(Color), "LightGray")>
    Public Property ButtonColor As Color
        Get
            Return _buttonColor
        End Get
        Set(ByVal value As Color)
            If _buttonColor <> value Then
                _buttonColor = value
                Invalidate()
            End If
        End Set
    End Property
    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_PAINT AndAlso DropDownStyle <> ComboBoxStyle.Simple Then
            Dim clientRect = ClientRectangle
            Dim dropDownButtonWidth = SystemInformation.HorizontalScrollBarArrowWidth
            Dim outerBorder = New Rectangle(clientRect.Location, New Size(clientRect.Width - 1, clientRect.Height - 1))
            Dim innerBorder = New Rectangle(outerBorder.X + 1, outerBorder.Y + 1, outerBorder.Width - dropDownButtonWidth - 2, outerBorder.Height - 2)
            Dim innerInnerBorder = New Rectangle(innerBorder.X + 1, innerBorder.Y + 1, innerBorder.Width - 2, innerBorder.Height - 2)
            Dim dropDownRect = New Rectangle(innerBorder.Right + 1, innerBorder.Y, dropDownButtonWidth, innerBorder.Height + 1)
            If RightToLeft = RightToLeft.Yes Then
                innerBorder.X = clientRect.Width - innerBorder.Right
                innerInnerBorder.X = clientRect.Width - innerInnerBorder.Right
                dropDownRect.X = clientRect.Width - dropDownRect.Right
                dropDownRect.Width += 1
            End If
            Dim innerBorderColor = If(Enabled, BackColor, SystemColors.Control)
            Dim outerBorderColor = If(Enabled, BorderColor, SystemColors.ControlDark)
            Dim buttonColor As Color = If(Enabled, Me.ButtonColor, SystemColors.Control)
            Dim middle = New Point(dropDownRect.Left + dropDownRect.Width \ 2, dropDownRect.Top + dropDownRect.Height \ 2)
            Dim arrow = New Point() {
                New Point(middle.X - 3, middle.Y - 2),
                New Point(middle.X + 4, middle.Y - 2),
                New Point(middle.X, middle.Y + 2)
            }
            Dim ps = New PAINTSTRUCT()
            Dim shoulEndPaint As Boolean = False
            Dim dc As IntPtr
            If m.WParam = IntPtr.Zero Then
                dc = BeginPaint(Handle, ps)
                m.WParam = dc
                shoulEndPaint = True
            Else
                dc = m.WParam
            End If
            Dim rgn = CreateRectRgn(innerInnerBorder.Left, innerInnerBorder.Top, innerInnerBorder.Right, innerInnerBorder.Bottom)
            SelectClipRgn(dc, rgn)
            DefWndProc(m)
            DeleteObject(rgn)
            rgn = CreateRectRgn(clientRect.Left, clientRect.Top, clientRect.Right, clientRect.Bottom)
            SelectClipRgn(dc, rgn)
            Using g = Graphics.FromHdc(dc)
                Using b = New SolidBrush(buttonColor)
                    g.FillRectangle(b, dropDownRect)
                End Using
                Using b = New SolidBrush(outerBorderColor)
                    g.FillPolygon(b, arrow)
                End Using
                Using p = New Pen(innerBorderColor)
                    g.DrawRectangle(p, innerBorder)
                    g.DrawRectangle(p, innerInnerBorder)
                End Using
                Using p = New Pen(outerBorderColor)
                    g.DrawRectangle(p, outerBorder)
                End Using
            End Using
            If shoulEndPaint Then
                EndPaint(Handle, ps)
            End If
            DeleteObject(rgn)
        Else
            MyBase.WndProc(m)
        End If
    End Sub

    Private Const WM_PAINT As Integer = &HF
    <StructLayout(LayoutKind.Sequential)>
    Public Structure RECT
        Public L, T, R, B As Integer
    End Structure
    <StructLayout(LayoutKind.Sequential)>
    Public Structure PAINTSTRUCT
        Public hdc As IntPtr
        Public fErase As Boolean
        Public rcPaint_left As Integer
        Public rcPaint_top As Integer
        Public rcPaint_right As Integer
        Public rcPaint_bottom As Integer
        Public fRestore As Boolean
        Public fIncUpdate As Boolean
        Public reserved1 As Integer
        Public reserved2 As Integer
        Public reserved3 As Integer
        Public reserved4 As Integer
        Public reserved5 As Integer
        Public reserved6 As Integer
        Public reserved7 As Integer
        Public reserved8 As Integer
    End Structure
    <DllImport("user32.dll")>
    Private Shared Function BeginPaint(ByVal hWnd As IntPtr, <[In], [Out]> ByRef lpPaint As PAINTSTRUCT) As IntPtr
    End Function

    <DllImport("user32.dll")>
    Private Shared Function EndPaint(ByVal hWnd As IntPtr, ByRef lpPaint As PAINTSTRUCT) As Boolean
    End Function

    <DllImport("gdi32.dll")>
    Public Shared Function SelectClipRgn(ByVal hDC As IntPtr, ByVal hRgn As IntPtr) As Integer
    End Function

    <DllImport("user32.dll")>
    Public Shared Function GetUpdateRgn(ByVal hwnd As IntPtr, ByVal hrgn As IntPtr, ByVal fErase As Boolean) As Integer
    End Function
    Public Enum RegionFlags
        [ERROR] = 0
        NULLREGION = 1
        SIMPLEREGION = 2
        COMPLEXREGION = 3
    End Enum
    <DllImport("gdi32.dll")>
    Friend Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll")>
    Private Shared Function CreateRectRgn(ByVal x1 As Integer, ByVal y1 As Integer, ByVal x2 As Integer, ByVal y2 As Integer) As IntPtr
    End Function

End Class

desired result in datagridview event EditingControlShowing display like below :

Code recommendation update from @dr.null & update code OnTextChanged in class FlatComboBox

TextboxCell

Public Class TextboxCell
    Inherits DataGridViewTextBoxCell

    Public Sub New()
        ' Use the text format.
        Me.Style.Format = Nothing
    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 FlatComboBox =
            CType(DataGridView.EditingControl, FlatComboBox)

        ' Use the default row value when Value property is null.
        If (Me.Value Is Nothing) Then
            ctl.Text = CType(Me.DefaultNewRowValue, String)
        Else
            ctl.Text = CType(Me.Value, String)
        End If
    End Sub

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

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

    Public Overrides ReadOnly Property DefaultNewRowValue() As Object
        Get
            ' Use the current string as the default value.
            Return Nothing
        End Get
    End Property
End Class

TextboxColumn

Public Class TextboxColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New TextboxCell())
    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 TextboxCell.
            If (value IsNot Nothing) AndAlso
                Not value.GetType().IsAssignableFrom(GetType(TextboxCell)) _
                Then
                Throw New InvalidCastException("Must be a String")
            End If
            MyBase.CellTemplate = value

        End Set
    End Property

End Class

FlatComboBox

Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Public Class FlatComboBox
    Inherits ComboBox
    Implements IDataGridViewEditingControl

    Private dataGridViewControl As DataGridView
    Private valueIsChanged As Boolean = False
    Private rowIndexNum As Integer
    Private _borderColor As Color = Color.Green
    <DefaultValue(GetType(Color), "Green")>
    Public Property BorderColor As Color
        Get
            Return _borderColor
        End Get
        Set(ByVal value As Color)
            If _borderColor <> value Then
                _borderColor = value
                Invalidate()
            End If
        End Set
    End Property
    Private _buttonColor As Color = Color.LightGray
    <DefaultValue(GetType(Color), "LightGray")>
    Public Property ButtonColor As Color
        Get
            Return _buttonColor
        End Get
        Set(ByVal value As Color)
            If _buttonColor <> value Then
                _buttonColor = value
                Invalidate()
            End If
        End Set
    End Property
    Public Property EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
        Get
            Return Me.Text.ToString()
        End Get

        Set(ByVal value As Object)
            Try
                ' This will throw an exception of the string is 
                ' null, empty, or not in the format of a text.
                Me.Text = CStr(value)
            Catch
                ' In the case of an exception, just use the default
                ' value so we're not left with a null value.
                Me.Text = Nothing
            End Try
        End Set
    End Property

    Public Function GetEditingControlFormattedValue(context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
        Return Me.Text.ToString()
    End Function
    Public Sub ApplyCellStyleToEditingControl(dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
        Me.Font = dataGridViewCellStyle.Font
        Me.ForeColor = dataGridViewCellStyle.ForeColor
        Me.BackColor = 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(selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit

        ' No preparation needs to be done.
    End Sub
    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 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 ReadOnly Property EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor

        Get
            Return MyBase.Cursor
        End Get
    End Property


    Protected Overrides Sub OnSelectedIndexChanged(ByVal eventargs As EventArgs)

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

    End Sub

'update code
    Protected Overrides Sub OnTextChanged(ByVal eventargs As EventArgs)
        ' Notify the DataGridView that the contents of the cell
        ' have changed.
        Me.valueIsChanged = True
        Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
        MyBase.OnTextChanged(eventargs)
    End Sub

    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = WM_PAINT AndAlso DropDownStyle <> ComboBoxStyle.Simple Then
            Dim clientRect = ClientRectangle
            Dim dropDownButtonWidth = SystemInformation.HorizontalScrollBarArrowWidth
            Dim outerBorder = New Rectangle(clientRect.Location, New Size(clientRect.Width - 1, clientRect.Height - 1))
            Dim innerBorder = New Rectangle(outerBorder.X + 1, outerBorder.Y + 1, outerBorder.Width - dropDownButtonWidth - 2, outerBorder.Height - 2)
            Dim innerInnerBorder = New Rectangle(innerBorder.X + 1, innerBorder.Y + 1, innerBorder.Width - 2, innerBorder.Height - 2)
            Dim dropDownRect = New Rectangle(innerBorder.Right + 1, innerBorder.Y, dropDownButtonWidth, innerBorder.Height + 1)
            If RightToLeft = RightToLeft.Yes Then
                innerBorder.X = clientRect.Width - innerBorder.Right
                innerInnerBorder.X = clientRect.Width - innerInnerBorder.Right
                dropDownRect.X = clientRect.Width - dropDownRect.Right
                dropDownRect.Width += 1
            End If
            Dim innerBorderColor = If(Enabled, BackColor, SystemColors.Control)
            Dim outerBorderColor = If(Enabled, BorderColor, SystemColors.ControlDark)
            Dim buttonColor As Color = If(Enabled, Me.ButtonColor, SystemColors.Control)
            Dim middle = New Point(dropDownRect.Left + dropDownRect.Width \ 2, dropDownRect.Top + dropDownRect.Height \ 2)
            Dim arrow = New Point() {
                New Point(middle.X - 3, middle.Y - 2),
                New Point(middle.X + 4, middle.Y - 2),
                New Point(middle.X, middle.Y + 2)
            }
            Dim ps = New PAINTSTRUCT()
            Dim shoulEndPaint As Boolean = False
            Dim dc As IntPtr
            If m.WParam = IntPtr.Zero Then
                dc = BeginPaint(Handle, ps)
                m.WParam = dc
                shoulEndPaint = True
            Else
                dc = m.WParam
            End If
            Dim rgn = CreateRectRgn(innerInnerBorder.Left, innerInnerBorder.Top, innerInnerBorder.Right, innerInnerBorder.Bottom)
            SelectClipRgn(dc, rgn)
            DefWndProc(m)
            DeleteObject(rgn)
            rgn = CreateRectRgn(clientRect.Left, clientRect.Top, clientRect.Right, clientRect.Bottom)
            SelectClipRgn(dc, rgn)
            Using g = Graphics.FromHdc(dc)
                Using b = New SolidBrush(buttonColor)
                    g.FillRectangle(b, dropDownRect)
                End Using
                Using b = New SolidBrush(outerBorderColor)
                    g.FillPolygon(b, arrow)
                End Using
                Using p = New Pen(innerBorderColor)
                    g.DrawRectangle(p, innerBorder)
                    g.DrawRectangle(p, innerInnerBorder)
                End Using
                Using p = New Pen(outerBorderColor)
                    g.DrawRectangle(p, outerBorder)
                End Using
            End Using
            If shoulEndPaint Then
                EndPaint(Handle, ps)
            End If
            DeleteObject(rgn)
        Else
            MyBase.WndProc(m)
        End If
    End Sub

    Private Const WM_PAINT As Integer = &HF
    <StructLayout(LayoutKind.Sequential)>
    Public Structure RECT
        Public L, T, R, B As Integer
    End Structure
    <StructLayout(LayoutKind.Sequential)>
    Public Structure PAINTSTRUCT
        Public hdc As IntPtr
        Public fErase As Boolean
        Public rcPaint_left As Integer
        Public rcPaint_top As Integer
        Public rcPaint_right As Integer
        Public rcPaint_bottom As Integer
        Public fRestore As Boolean
        Public fIncUpdate As Boolean
        Public reserved1 As Integer
        Public reserved2 As Integer
        Public reserved3 As Integer
        Public reserved4 As Integer
        Public reserved5 As Integer
        Public reserved6 As Integer
        Public reserved7 As Integer
        Public reserved8 As Integer
    End Structure
    <DllImport("user32.dll")>
    Private Shared Function BeginPaint(ByVal hWnd As IntPtr, <[In], [Out]> ByRef lpPaint As PAINTSTRUCT) As IntPtr
    End Function

    <DllImport("user32.dll")>
    Private Shared Function EndPaint(ByVal hWnd As IntPtr, ByRef lpPaint As PAINTSTRUCT) As Boolean
    End Function

    <DllImport("gdi32.dll")>
    Public Shared Function SelectClipRgn(ByVal hDC As IntPtr, ByVal hRgn As IntPtr) As Integer
    End Function

    <DllImport("user32.dll")>
    Public Shared Function GetUpdateRgn(ByVal hwnd As IntPtr, ByVal hrgn As IntPtr, ByVal fErase As Boolean) As Integer
    End Function
    Public Enum RegionFlags
        [ERROR] = 0
        NULLREGION = 1
        SIMPLEREGION = 2
        COMPLEXREGION = 3
    End Enum
    <DllImport("gdi32.dll")>
    Friend Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll")>
    Private Shared Function CreateRectRgn(ByVal x1 As Integer, ByVal y1 As Integer, ByVal x2 As Integer, ByVal y2 As Integer) As IntPtr
    End Function


End Class



result code in gif after update code from @dr.null & update code OnTextChanged in class FlatComboBox

转载请注明原文地址:http://www.anycun.com/QandA/1746133125a92031.html