DataGridView ComboBox列将接受任何文本

时间:2016-12-22 11:17:56

标签: c# winforms datagridview combobox

我希望DataGridView的列使用ComboBoxStyle.DropDown样式的ComboBox,用户可以在其中选择下拉列表中的一个条目,也可以键入任意文本。

目前,我正在使用this answer中的代码,我可以自由地键入ComboBox的文本框部分,但是如果我输入的内容不是在-b在列表中,它不会被提交回数据源,该字段将恢复为原始选择。此外,如果我以编程方式将文本设置为不在下拉列表中的内容,则会收到DataError事件" DataGridViewComboBoxCell值无效。"

我正在使用数据绑定; DataGridView本身绑定到BindingList<T>

this question不同,我不希望将自由文字添加到下拉列表中。

要明确的是,列数据类型为string,并且我不希望它根据ComboBox的下拉列表(或其他任何内容)进行验证。

(我是否必须按How to: Host Controls in Windows Forms DataGridView Cells中所述创建自己的自定义DataGridViewColumn后代?)

4 个答案:

答案 0 :(得分:1)

我找到了一个简单的,如果详细的答案。 (但我仍然想知道是否有办法使用标准DataGridViewComboBoxColumn类型执行此操作。)

我按照How to: Host Controls in Windows Forms DataGridView Cells中的方法进行操作。我的完整解决方案在此处发布的时间太长,但我可以总结一下这些更改,以便使用ComboBox代替示例DateTimePicker控件。

  1. 分别重命名三个班级DropDownComboBoxColumnDropDownComboBoxCellDropDownComboBoxEditingControl

  2. DateTime替换为string

  3. 将属性public ComboBoxStyle DropDownStyle { get; set; }添加到DropDownComboBoxColumn以允许调用代码设置下拉样式。

  4. DropDownComboBoxCell构造函数中删除代码。

  5. DropDownComboBoxEditingControl构造函数中删除代码。

  6. DropDownComboBoxEditingControl来自ComboBox而不是DateTimePicker

  7. OnValueChanged替换为OnTextChanged,以便考虑ComboBoxDateTimePicker中的不同命名。

  8. 使EditingControlFormattedValue属性获取并设置继承的Text属性(而不是Value),并且不需要解析。

  9. 设置ApplyCellStyleToEditingControl设置ForeColorBackColor而不是CalendarForeColorCalendarMonthBackground

  10. EditingControlWantsInputKey声明为F4,以便它可用于打开和关闭下拉列表。

  11. 将以下代码添加到PrepareEditingControlForEdit

    DropDownComboBoxColumn col = _dataGridView.Columns[_dataGridView.CurrentCell.ColumnIndex] as DropDownComboBoxColumn;
    if (col == null)
    {
      throw new InvalidCastException("Must be in a DropDownComboBoxColumn");
    }
    DropDownStyle = col.DropDownStyle;
    // (If you don't explicitly set the Text then the current value is
    // always replaced with one from the drop-down list when edit begins.)
    Text = _dataGridView.CurrentCell.Value as string;
    SelectAll();
    
  12. 处理DataGridView的{​​{1}}事件,如OhBeWise's回答相关问题,以设置下拉项目,如果需要,还可以设置自动完成模式:

    EditingControlShowing

    如果您希望所有行都使用相同的下拉项,那么您可以始终将此private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) { ComboBox box = e.Control as ComboBox; if (box != null) { box.AutoCompleteSource = AutoCompleteSource.ListItems; box.AutoCompleteMode = AutoCompleteMode.SuggestAppend; box.DataSource = _dropDownItems; } } 属性设为DropDownComboBoxColumn,并将其设置为DropDownStyle以避免必须处理PrepareEditingControlForEdit

答案 1 :(得分:0)

为此,您可以通过下面提到的后端代码在数据网格视图中添加列:

using System.Data.SqlServerCe;

string sqlConnection = "Data Source";
SqlCeConnection conn = new SqlCeConnection(sqlConnection);
//Get bind from database.
string qryGetCategory = "Query to get data for combo box";
SqlCeCommand cmdCat = new SqlCeCommand(qryGetCategory, conn);
SqlCeDataAdapter daCat = new SqlCeDataAdapter(qryGetCategory, conn);
DataTable dtCat = new DataTable();
daCat.Fill(dtCat);

//Combobox column.
DataGridViewComboBoxColumn ComboBoxCol = new DataGridViewComboBoxColumn();
ComboBoxCol.DataSource = dtCat;
ComboBoxCol.Name = "Column name";
ComboBoxCol.ValueMember = "Value of member";
ComboBoxCol.DisplayMember = "Member to be show";
ComboBoxCol.DropDownStyle = ComboBoxStyle.DropDown;
datagridview.Columns.Add(ComboBoxCol);

请尝试它可能会解决您的问题。

答案 2 :(得分:0)

我有一个基于Ian Goldby解决方案的解决方案。

唯一的区别是,可以像设置普通DataGridViewComboBoxColumn一样设置DataSource,DisplayMember和ValueMember。这意味着不必像Ian Goldby的解决方案中那样更改“ EditingControlShowing”事件。

结果也有所不同,因为添加到DataGridViewDropDownComboBoxColumn中的值(不是“数据源”的一部分)不会添加到DropDown中。

有关VB代码,请参见以下内容:

Public Class DataGridViewDropDownComboBoxColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New DataGridViewDropDownComboBoxCell)

    End Sub

    Public Property DropDownStyle As ComboBoxStyle
    Public Property DataSource As Object
    Public Property ValueMember As Object
    Public Property DisplayMember As Object

    Public Overrides Property CellTemplate As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set
            ' Ensure that the cell used for the template is a DataGridViewDropDownComboBoxCell.
            If ((Not (Value) Is Nothing) AndAlso Not Value.GetType.IsAssignableFrom(GetType(DataGridViewDropDownComboBoxCell))) Then
                Throw New InvalidCastException("Must be a DropDownCell")
            End If

            MyBase.CellTemplate = Value
        End Set
    End Property
End Class
Public Class DataGridViewDropDownComboBoxCell
    Inherits DataGridViewTextBoxCell

    Public Sub New()
        MyBase.New
    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 DataGridViewDropDownComboBoxEditingControl = CType(DataGridView.EditingControl, DataGridViewDropDownComboBoxEditingControl)
        ' 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 DataGridViewDropDownComboBoxCell uses.
            Return GetType(DataGridViewDropDownComboBoxEditingControl)
        End Get
    End Property

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

    Public Overrides ReadOnly Property DefaultNewRowValue As Object
        Get
            ' Use the current date and time as the default value.
            Return ""
        End Get
    End Property
End Class
Class DataGridViewDropDownComboBoxEditingControl
    Inherits ComboBox
    Implements IDataGridViewEditingControl

    Private dataGridView As DataGridView

    Private valueChanged As Boolean = False

    Private rowIndex As Integer

    Public Sub New()
        MyBase.New
    End Sub

    ' Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
    ' property.
    Public Property EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
        Get
            Return Me.Text
        End Get
        Set
            If (TypeOf Value Is String) Then
                Me.Text = Value
            End If

        End Set
    End Property

    ' Implements the 
    ' IDataGridViewEditingControl.GetEditingControlFormattedValue method.
    Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
        Return Me.EditingControlFormattedValue
    End Function

    ' Implements the 
    ' IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
    Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
        Me.Font = dataGridViewCellStyle.Font
        Me.ForeColor = dataGridViewCellStyle.ForeColor
        Me.BackColor = dataGridViewCellStyle.BackColor
    End Sub

    ' Implements the IDataGridViewEditingControl.EditingControlRowIndex 
    ' property.
    Public Property EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
        Get
            Return Me.rowIndex
        End Get
        Set
            Me.rowIndex = Value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 
    ' method.
    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, Keys.F4
                Return True
            Case Else
                Return Not dataGridViewWantsInputKey
        End Select

    End Function

    ' Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
    ' method.
    Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
        Dim col As DataGridViewDropDownComboBoxColumn = CType(dataGridView.Columns(dataGridView.CurrentCell.ColumnIndex), DataGridViewDropDownComboBoxColumn)
        If (col Is Nothing) Then
            Throw New InvalidCastException("Must be in a DropDownComboBoxColumn")
        End If

        DropDownStyle = col.DropDownStyle
        DataSource = col.DataSource
        ValueMember = col.ValueMember
        DisplayMember = col.DisplayMember

        ' (If you don't explicitly set the Text then the current value is
        ' always replaced with one from the drop-down list when edit begins.)
        Text = CType(dataGridView.CurrentCell.Value, String)
    End Sub

    ' Implements the IDataGridViewEditingControl
    ' .RepositionEditingControlOnValueChange property.
    Public ReadOnly Property RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
        Get
            Return False
        End Get
    End Property

    ' Implements the IDataGridViewEditingControl
    ' .EditingControlDataGridView property.
    Public Property EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
        Get
            Return Me.dataGridView
        End Get
        Set
            Me.dataGridView = Value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl
    ' .EditingControlValueChanged property.
    Public Property EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
        Get
            Return Me.valueChanged
        End Get
        Set
            Me.valueChanged = Value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl
    ' .EditingPanelCursor property.
    Public ReadOnly Property EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
        Get
            Return MyBase.Cursor
        End Get
    End Property

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

答案 3 :(得分:0)

感谢Gero90提供的解决方案!! 但是,我遇到了一些问题。我没有意识到,如果ComboboxStyle很简单,那是行不通的。同样,如果您输入自定义值,则选择下拉菜单,然后浏览掉它,将其重置为初始值。我已经解决了发现的问题,这是可以根据需要复制的新代码:

Public Class DataGridViewDropDownComboBoxColumn
    Inherits DataGridViewColumn

    Public Sub New()
        MyBase.New(New DataGridViewDropDownComboBoxCell)

    End Sub

    Public Property DropDownStyle As ComboBoxStyle
    Public Property DataSource As Object
    Public Property ValueMember As Object
    Public Property DisplayMember As Object

    Public Overrides Property CellTemplate As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set
            ' Ensure that the cell used for the template is a DataGridViewDropDownComboBoxCell.
            If ((Not (Value) Is Nothing) AndAlso Not Value.GetType.IsAssignableFrom(GetType(DataGridViewDropDownComboBoxCell))) Then
                Throw New InvalidCastException("Must be a DropDownCell")
            End If

            MyBase.CellTemplate = Value
        End Set
    End Property
End Class

Public Class DataGridViewDropDownComboBoxCell
    Inherits DataGridViewTextBoxCell


    Public Sub New()
        MyBase.New
    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 DataGridViewDropDownComboBoxEditingControl = CType(DataGridView.EditingControl, DataGridViewDropDownComboBoxEditingControl)
        ' Use the default row value when Value property is null.
        If (Me.Value Is Nothing) Or IsDBNull(Me.Value) Then
            ctl.Text = CType(Me.DefaultNewRowValue, String)
        Else
            ctl.Text = CType(Me.Value, String)
        End If
        'ctl.BringToFront()
        'ctl.Focus()
        'ctl.DroppedDown = True

    End Sub

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

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

    Public Overrides ReadOnly Property DefaultNewRowValue As Object
        Get
            ' Use the current date and time as the default value.
            Return ""
        End Get
    End Property
End Class
Class DataGridViewDropDownComboBoxEditingControl
    Inherits ComboBox
    Implements IDataGridViewEditingControl

    Private dataGridView As DataGridView

    Private valueChanged As Boolean = False

    Private rowIndex As Integer

    Public Sub New()
        MyBase.New
    End Sub

    Public Shadows Property DropDownStyle() As ComboBoxStyle
        Get
            Return MyBase.DropDownStyle
        End Get
        Set(ByVal value As ComboBoxStyle)
            If value = ComboBoxStyle.Simple Then
                'Throw New NotSupportedException("ComboBoxStyle.Simple not supported")
                value = ComboBoxStyle.DropDown
            End If
            MyBase.DropDownStyle = value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
    ' property.
    Public Property EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
        Get
            Return Me.Text
        End Get
        Set
            If (TypeOf Value Is String) Then
                Me.Text = Value
            End If

        End Set
    End Property

    ' Implements the 
    ' IDataGridViewEditingControl.GetEditingControlFormattedValue method.
    Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
        Return Me.EditingControlFormattedValue
    End Function

    ' Implements the 
    ' IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
    Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
        Me.Font = dataGridViewCellStyle.Font
        Me.ForeColor = dataGridViewCellStyle.ForeColor
        Me.BackColor = dataGridViewCellStyle.BackColor
    End Sub

    ' Implements the IDataGridViewEditingControl.EditingControlRowIndex 
    ' property.
    Public Property EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
        Get
            Return Me.rowIndex
        End Get
        Set
            Me.rowIndex = Value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 
    ' method.
    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, Keys.F4
                Return True
            Case Else
                Return Not dataGridViewWantsInputKey
        End Select

    End Function

    ' Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
    ' method.
    Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
        Dim col As DataGridViewDropDownComboBoxColumn = CType(dataGridView.Columns(dataGridView.CurrentCell.ColumnIndex), DataGridViewDropDownComboBoxColumn)
        If (col Is Nothing) Then
            Throw New InvalidCastException("Must be in a DropDownComboBoxColumn")
        End If


        DropDownStyle = col.DropDownStyle

        Items.Clear()
        If IsDBNull(dataGridView.CurrentCell.Value) Then
            Text = ""
        Else
            Text = CType(dataGridView.CurrentCell.Value, String)
        End If
        Items.Add(Text)

        Dim dt As DataTable = New DataTable
        Dim ct = 0, cx = 0
        Try
            dt = DirectCast(col.DataSource, DataTable)
            If Not col.DisplayMember Is Nothing Then
                For Each c As DataColumn In dt.Columns
                    If c.ColumnName = col.DisplayMember Then
                        cx = ct
                    End If
                    ct += 1
                Next
            End If
            For Each r As DataRow In dt.Rows
                If Not col.DisplayMember Is Nothing Then
                    If Not Items.Contains(r(cx)) Then Items.Add(r(cx))
                Else
                    If dt.Columns.Count = 1 Then
                        If Not Items.Contains(r(0)) Then Items.Add(r(0))
                    Else
                        If Not Items.Contains(r(dt.Columns.Count - 1)) Then Items.Add(r(dt.Columns.Count - 1))
                    End If
                End If
            Next
        Catch ex As Exception
        End Try

        'DropDownStyle = col.DropDownStyle
        'ValueMember = col.ValueMember
        'DisplayMember = col.DisplayMember
        'DataSource = col.DataSource

        ' (If you don't explicitly set the Text then the current value is
        ' always replaced with one from the drop-down list when edit begins.)
        'If IsDBNull(dataGridView.CurrentCell.Value) Then
        '    Text = ""
        'Else
        '    Text = CType(dataGridView.CurrentCell.Value, String)
        'End If

    End Sub

    ' Implements the IDataGridViewEditingControl
    ' .RepositionEditingControlOnValueChange property.
    Public ReadOnly Property RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
        Get
            Return False
        End Get
    End Property

    ' Implements the IDataGridViewEditingControl
    ' .EditingControlDataGridView property.
    Public Property EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
        Get
            Return Me.dataGridView
        End Get
        Set
            Me.dataGridView = Value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl
    ' .EditingControlValueChanged property.
    Public Property EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
        Get
            Return Me.valueChanged
        End Get
        Set
            Me.valueChanged = Value
        End Set
    End Property

    ' Implements the IDataGridViewEditingControl
    ' .EditingPanelCursor property.
    Public ReadOnly Property EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
        Get
            Return MyBase.Cursor
        End Get
    End Property

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

    Protected Overrides Sub OnSelectedIndexChanged(ByVal e As EventArgs)
        ' Notify the DataGridView that the contents of the cell
        ' have changed.
        Me.valueChanged = True
        Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
        MyBase.OnSelectedIndexChanged(e)
    End Sub
End Class