none
DataGridViewComboBoxColumn not accepting input through keyboard

    Question

  • I am facing issue with providing input to DataGridViewComboBoxColumn through keyboard. If a value is selected from the drop drop list of the Combo Box Column, then it is retained and the underlying DataTable's column's value is updated. However, if I enter the Tab key, the focus moves to the next cell, and the displayed value in the combo box is not retained. If the Enter key is pressed, the cell validation procedure kicks in and says that the column can't be left blank (The column is tied to a non-null column in the database).

    I have used the following code to create the column:

    DataGridViewComboBoxColumn column = newDataGridViewComboBoxColumn();
    column.DataPropertyName = "UoMID";
    column.DataSource = unitsTable;
    column.DisplayMember = "UnitName";
    column.ValueMember = "ID";
    column.ValueType = SqlDbType.Int.GetType();
    column.Name = "UoMID";
    column.HeaderText = "Measurement Unit";
    column.DisplayStyleForCurrentCellOnly = true;


    I have also recorded a video and posted it on YouTube to demonstrate the problem. The video named is ApplicationScreenCapture. The forum software won't allow me to post the link to the video.

    I would be grateful if someone could provide me with the solution to this problem.

    Thanks,

    Dinesh

    Monday, November 19, 2012 12:43 PM

Answers

  • Perhaps the following might be common ground

    Requires a DataGridView and button.

    .
    .
    .
        Private Sub Button1_Click(
            ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles Button1.Click
            If bsData.Current IsNot Nothing Then
                Dim Row = CType(bsData.Current, DataRowView)
                If Row.Item(DataPropertyName).ToString = "B" Then
                    Row.Item(DataPropertyName) = "A"
                Else
                    Row.Item(DataPropertyName) = "B"
                End If
                bsData.ResetCurrentItem()
            End If
        End Sub
    End Class


    You are assigning an arbitary value "A" or "B" to the cell. However, I need to assign the value that was displayed in the cell when the focus leaves the cell. The displayed valued could be the first value in the combobox or the value selected in the previous row. How do I find the value that was displayed in the cell when the focus leaves it.

    Yep I am assigning an arbitary value for demong. If I needed to know what the value was if it was changed would be to hook into the underlying DataTable event ColumnChanged.

    Private Sub MyDataTable_ColumnChanged(ByVal sender As Object, ByVal e As System.Data.DataColumnChangeEventArgs)
        If Not e.Row.RowState = DataRowState.Deleted AndAlso Not e.Row.RowState = DataRowState.Detached Then
            If e.Column.ColumnName = DataPropertyName Then
                Console.WriteLine("Original [{0}] Proposed [{1}]",
                                  e.Row(e.Column.ColumnName, DataRowVersion.Original),
                                  e.Row(e.Column.ColumnName, DataRowVersion.Proposed)
                )
            End If
        End If
    End Sub

    Below shows how to implement within the code I provided already

    dt.Rows.Add(New Object() {"B", "BBBBB"})
    dt.Rows.Add(New Object() {"A", "DDDDD"})
    dt.Rows.Add(New Object() {"B", "CCCCC"})
    dt.AcceptChanges()
    bsData.DataSource = dt
    AddHandler dt.ColumnChanged, AddressOf MyDataTable_ColumnChanged
    DataGridView1.DataSource = bsData

    For the DataGridView we can use CellLeave event and invoke EndEdit for the ComboBox column

        Private Sub DataGridView1_CellLeave(
            ByVal sender As Object,
            ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
        Handles DataGridView1.CellLeave
            If e.ColumnIndex = 0 Then
                DataGridView1.EndEdit()
                Label1.Text = String.Format("Cell Leave [{0}]",
                                DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value
                             )
            End If
        End Sub


    KSG

    • Marked as answer by Dinesh KTE Saturday, November 24, 2012 7:06 PM
    Saturday, November 24, 2012 6:26 PM

All replies

  • Hi Dinesh,

    Do you want to input text in the combobox cell? If so, you can set the DisplayStyle property of the column to ComboBox:

    column.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox;

    Then write code in EditingControlShowing event handler to change the style when editing control is shown.

            private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
            {
                if (dataGridView1.CurrentCell.ColumnIndex == 1)
                {
                    ComboBox cb = e.Control as ComboBox;
                    if (cb != null)
                    {
                        cb.DropDownStyle = ComboBoxStyle.DropDown;
                    }
                }
            }
    

    However, the text input should be one of the items in the data source, else it will become empty when you move to other cells. If the formatted text is not retained, would you like to check the item you have selected in the data source?

    Best regards,


    Chester Hong
    MSDN Community Support | Feedback to us
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, November 20, 2012 8:39 AM
  • Hi Chester Hong,

    Thanks for your response.

    >Do you want to input text in the combo box cell?

    No. I wish to select only one of the values listed in the combo box. I don't wish to enter any text in the combo box.

    Let me re-word the problem I am facing: The combo box column doesn't have a default value, and is initially blank. When the focus moves to the combo box column in the grid, the first value of the combo box data source is displayed in the cell. Now, if the Tab or Enter key is pressed (without pressing any other key or using mouse to select an item), the focus moves to the next cell, and the displayed value is not retained.

    My question is how can I retain the displayed value without making any explicit selection in the combo box?


    • Edited by Dinesh KTE Tuesday, November 20, 2012 5:08 PM value was typed as valued.
    Tuesday, November 20, 2012 5:06 PM
  • Hello, from looking at your code you should not have the problem described which means something else is causing the problem.

    What other events are hooked up to the DataGridView?

    Also I have never found a reason to set ValueType or DisplayStyleForCurrentCellOnly. Even so they should not cause a problem.


    KSG

    Thursday, November 22, 2012 7:21 AM
  • Hello KSG,

    I have set up error handler for the following handlers:

    CellValidated
    CellValidating
    DataError
    RowValidated
    SelectionChanged

    DisplayStyleForCurrentCellOnly causes the combo box to be shown only when the cell has the focus. This is I think is better than showing the combo box all the time in the column. ValueType is provided for the sake of completeness.  Anyways, I disabled both the statements, and it made no difference to the outcome.

    -Dinesh

    Thursday, November 22, 2012 10:24 AM
  • Hello KSG,

    I have set up error handler for the following handlers:

    CellValidated
    CellValidating
    DataError
    RowValidated
    SelectionChanged

    DisplayStyleForCurrentCellOnly causes the combo box to be shown only when the cell has the focus. This is I think is better than showing the combo box all the time in the column. ValueType is provided for the sake of completeness.  Anyways, I disabled both the statements, and it made no difference to the outcome.

    -Dinesh

    If you have not already please try your code without any of the events listed that have error handlers.  It could be that something is doing the reset in one of them but can't say not seeing the code. Thinking it was late when I mentioned DisplayStyleForCurrentCellOnly as I do use this and was looking right at it in a project I did.

    Any ways the default response to your keyboard actions is to keep the selection (which of course you already know) so I am confident there is something happening in your code


    KSG

    Thursday, November 22, 2012 12:40 PM
  • Thanks for your continued assistance.

    I am afraid that the grid's event handlers are not playing the spoilsport here as I removed the event handlers from the grid altogether, but the problem persists. I won't mind sending the VS solution to you if you wish.

    -Dinesh

    Thursday, November 22, 2012 2:35 PM
  • Thanks for your continued assistance.

    I am afraid that the grid's event handlers are not playing the spoilsport here as I removed the event handlers from the grid altogether, but the problem persists. I won't mind sending the VS solution to you if you wish.

    -Dinesh

    You can send it to kevininstructor@comcast.net

    Please indicate which version of Visual Studio i.e. VS2008 or VS2010 (what I have installed). I will look at it and report back here.


    KSG

    Thursday, November 22, 2012 2:40 PM
  • I have just sent you the email. It is a VS 2010 solution, and I am using 4.0.3 client profile version of .Net.

    Thanks,

    Dinesh

    Thursday, November 22, 2012 2:50 PM
  • I have just sent you the email. It is a VS 2010 solution, and I am using 4.0.3 client profile version of .Net.

    Thanks,

    Dinesh

    I could not run the project, as I do not have the provider installed for SqlCe. What I did was collapsed all events and procedures that were not relevant to the ComboBox issue, concentrated on the load, and configure code. Absolutely nothing looks out of place to cause the issue. I also looked at the DataGridView properties in the IDE property window and did not see anything to cause an issue either.


    KSG

    Thursday, November 22, 2012 3:30 PM
  • What then could be the reason? Just would like to ask whether the default behaviour I am expecting is indeed default or not i.e. combo box column to update the underlying data table column with the displayed value on just pressing the Tab or Enter key?

    What I will do now is to create a vanila project (i.e. no database), and see whether the datagrid exhibits the supposed default behaviour.

    Thanks anyway to you for your assistance.

    -Dinesh

    Thursday, November 22, 2012 3:42 PM
  • What then could be the reason? Just would like to ask whether the default behaviour I am expecting is indeed default or not i.e. combo box column to update the underlying data table column with the displayed value on just pressing the Tab or Enter key?

    What I will do now is to create a vanila project (i.e. no database), and see whether the datagrid exhibits the supposed default behaviour.

    Thanks anyway to you for your assistance.

    -Dinesh


    I will send you one in a few minutes.

    KSG

    Thursday, November 22, 2012 3:55 PM
  • The following uses MS-Access, don't have one non-database example right now. I did this back about 7 years ago in VB.NET but language should not matter all that is important is it shows the proper/default behavior for interacting with a ComboBox in a DataGridView. Note there is a check box at the bottom of the form which toggles how both ComboBox cells are activated into edit mode via a Language method extension.

    http://kevininstructor.home.comcast.net/~kevininstructor/DotNet/DataGridviewComboBox2.zip

    If you don't have MS-Access I can see about revamping this demo to a non-database project by exporting data from MS-Access into xml and load from xml.


    KSG

    Thursday, November 22, 2012 4:21 PM
  • I ran the project, but it displays nothing even though .Net Framework Data Provider for OLE DB is installed on my system, and recognised by Visual Studio. 

    What I can see in the code is the following lines:

    If chkAutoDropDown.Checked Then   
             If DataGridView1.Columns(e.ColumnIndex).Name = "ContactsColumn" Then    
                If DataGridView1(e.ColumnIndex, e.RowIndex).IsComboBoxCell Then  
                      SendKeys.Send("{F4}")  
                End If  
             End If
    End If

    If I understand correct, then you are programmatically showing the drop down list  of the combobox when the combobox column cell receives the focus. Sorry for being numskull, but how is it related to my problem?

    -Dinesh

    Thursday, November 22, 2012 4:55 PM
  • I made a quick project (http://sdrv.ms/RWRMo2) to check whether the presumed default behaviour (retaining the displayed value on moving out of the cell when no explicit selection is done) is indeed default or not. Unfortunately, it is not.

    So, my question is why can be done in code to implement this desired functionality. I have tried wiring a keypress event handler to DataGridViewComboBoxEditingControl, but it doesn't capture the tab or enter key.


    Thursday, November 22, 2012 5:44 PM
  • I ran the project, but it displays nothing even though .Net Framework Data Provider for OLE DB is installed on my system, and recognised by Visual Studio. 

    What I can see in the code is the following lines:

    If chkAutoDropDown.Checked Then   
             If DataGridView1.Columns(e.ColumnIndex).Name = "ContactsColumn" Then    
                If DataGridView1(e.ColumnIndex, e.RowIndex).IsComboBoxCell Then  
                      SendKeys.Send("{F4}")  
                End If  
             End If
    End If

    If I understand correct, then you are programmatically showing the drop down list  of the combobox when the combobox column cell receives the focus. Sorry for being numskull, but how is it related to my problem?

    -Dinesh

    Concerning not loading, if you are running 64bit OS then change this

    Private Sub MainForm_Load() Handles MyBase.Load
        FillGridView(DataGridView1)
    End Sub

    To

    Private Sub frmMainForm_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        FillGridView(DataGridView1)
    End Sub
    Reason: There are times on 64bit OS that the load event is not called.

    In regards to the code block above, the project was done back around 2005 and is explained in the attached readme file in the project.

    First part of read me file

    This project originally done sometime back in 2005 was to show how to setup a DataGridView column as a ComboBox where one click opened up the current row ComboBox. The original code did not reload data which has been added to show how when done properly the DataGridView columns remain as first setup.

    So that includes the reload and cvs code also


    KSG

    Thursday, November 22, 2012 6:13 PM
  • I was able to run your project after downloading and installing the Jet OLE DB provider, and moving the method FillGridView from the form's Load event handler to the Shown event handler. And yes my OS is 64-bit.

    I find that only the code in the datagrid's CellEnter event that is directly related to this thread. However even this code doesn't serve the purpose. When the focus enters the comboxbox column cell, the code sends the F4 key programmatically, and it causes the drop down list to be shown. If now the Enter key is pressed, the drop list goes away but the grid's underlying datasource is not updated (and that's the problem).

    I consider this counter-intuitive behaviour to be a big flaw with the datagrid, and shall waste no more time finding a workaround to it.

    -Dinesh

    Friday, November 23, 2012 12:02 PM
  • Hello Dinesh,

    Good to hear you were able to run the project as intended. In regards to the only code related to the thread, well there is other code that you have done, ignored it. This was done as indicated before, provide a complete working example of code that deals with working with a DataGridView ComboBox column. As I read into the initial question when making a change to the ComboBox in a specific column cell the information was not changing when tabbing out of the cell. Now I am reading this different, the value changes in the UI but not the underlying database. If I were to take, the code one-step farther by examining the initial data to the current data the current data is different if there are changes. Knowing this I could write a SQL update statement, executed and know it worked and back this up via the code block below that gets changes.

    In regards to the DataGridView being counter-intuitive behavior, that may very well be but personally would rather have the current behavior that the first grid in the Framework or even worst the grid in VB6 were one was better than the prior grid but nowhere near the flexibility of the current DataGridView.

    Private Sub cmdShowChanges_Click(
        ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles cmdShowChanges.Click
        Dim dv As New DataView() With
            {
                .Table = CType(bsPeople.DataSource, DataTable)
            }
        With dv
            .AllowNew = False
            .AllowDelete = False
            .AllowEdit = True
            .RowStateFilter = DataViewRowState.ModifiedCurrent
        End With
        For Each Item As DataRowView In dv
            Console.WriteLine("[{0}]", String.Join(",", Item.Row.ItemArray))
        Next
    End Sub

    Another check that shows current and a version of the data when loaded (note that I did a copy of the original table data and placed it into dtCopy)

    Private Sub cmdPeekAtData_Click(
        ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles cmdPeekAtData.Click
        Dim dt = CType(bsPeople.DataSource, DataTable)
        For row As Integer = 0 To dt.Rows.Count - 1
            Console.WriteLine("{0}{1}{2}{1}",
                String.Join(",", dtCopy.Rows(row).ItemArray),
                Environment.NewLine,
                String.Join(",", dt.Rows(row).ItemArray)
            )
        Next
    End Sub


    KSG


    Friday, November 23, 2012 2:57 PM
  • Dear KSG,

    I can't help but admire your tenacity to help me. 

    I have to note with regret that after several posts in this thread, I am still not able to put across the problem to you (and probably other readers as well). I acknowledge it as a shortcoming of my writing skill and apologise.

    Let me still try to present the problem to you:

    We are beginning with a blank grid that has one of the columns of type DataGridViewComboBoxColumn. When the focus moves to this column, the user is presented with a combobox that contains some items in its list. The first item (or the last selected one) in the combobox is shown in the cell. Say, the user wants to retain the displayed value in the cell so he moves out of the cell using the keyboard or mouse. The user would expect (and justifiably so) that the displayed value in the comboxcolumn cell will be retained when he moves out. However, what happens is that if the value is not explicity selected in the combobox (and the explicit selection doesn't include just pressing the enter key), the displayed value in the cell vanishes the movement the focus moves to another cell. So, the displayed value is updated neither in the UI nor in the grid's datasource. This is causing consternation.

    I can live up with this limitation, but I am asking is there any hack through which we can update the cell value with the displayed item when the cell loses the focus and its value is null.

    -Dinesh


    • Edited by Dinesh KTE Saturday, November 24, 2012 1:38 PM
    Saturday, November 24, 2012 1:37 PM
  • Perhaps the problem is I can not produce the issue you are having. Whenever I change a value in the current row of a DataGridView that is a ComboBox with the DataSource coming from a DataTable the value selected remains selected once the user has made the selection and also retains the value when moving to another row.

    So if I use the demo which in one of my replies above with the screenshot, remove all columns but one ComboBox column I can retain the selected value as mentioned above.


    KSG

    Saturday, November 24, 2012 3:32 PM
  • <q>...the value selected remains selected once the user has made the selection and also retains the value when moving to another row.</q>

    That's the point. The user is too lazy to bother explicitly selecting the displayed value in the cell. His assumption is that if the value is displayed in the cell then it ought to be retained (without any work on his part) when he moves out of the cell.

    How can I implement this desired functionality? I don't really bother if this is not the default behaviour of the GridView control.

    Saturday, November 24, 2012 4:08 PM
  • Perhaps the following might be common ground

    Requires a DataGridView and button.

    Public Class Form1
        WithEvents bsData As New BindingSource
        Private DataPropertyName As String = "Item"
        Private Sub Form1_Load(
            ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles MyBase.Load
            Dim dtProducts As New DataTable
            dtProducts.Columns.Add(New DataColumn With
                                   {
                                       .ColumnName = DataPropertyName,
                                       .DataType = GetType(String)
                                   }
                               )
            Dim Items() As Char = Enumerable.Range(0, 10).Select( _
                Function(i) (Chr(Asc("A") + i))).ToArray()
            For Each item In Items
                dtProducts.Rows.Add(New Object() {item})
            Next
            Dim Column1 As New DataGridViewComboBoxColumn With _
                {
                    .DataPropertyName = DataPropertyName,
                    .DataSource = dtProducts,
                    .DisplayMember = DataPropertyName,
                    .DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing,
                    .Name = "Column1",
                    .HeaderText = "C1",
                    .SortMode = DataGridViewColumnSortMode.NotSortable,
                    .ValueMember = DataPropertyName
                }
            Me.DataGridView1.Columns.AddRange(New DataGridViewColumn() {Column1})
            Dim dt As New DataTable
            dt.Columns.Add(New DataColumn With
                           {
                               .ColumnName = DataPropertyName,
                               .DataType = GetType(String)
                           }
                       )
            dt.Columns.Add(New DataColumn With
                           {
                               .ColumnName = "Name",
                               .DataType = GetType(String)
                           }
                       )
            dt.Rows.Add(New Object() {"B", "BBBBB"})
            dt.Rows.Add(New Object() {"A", "DDDDD"})
            dt.Rows.Add(New Object() {"B", "CCCCC"})
            bsData.DataSource = dt
            DataGridView1.DataSource = bsData
        End Sub
        Private Sub Button1_Click(
            ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles Button1.Click
            If bsData.Current IsNot Nothing Then
                Dim Row = CType(bsData.Current, DataRowView)
                If Row.Item(DataPropertyName).ToString = "B" Then
                    Row.Item(DataPropertyName) = "A"
                Else
                    Row.Item(DataPropertyName) = "B"
                End If
                bsData.ResetCurrentItem()
            End If
        End Sub
    End Class


    KSG

    Saturday, November 24, 2012 4:47 PM
  • Perhaps the following might be common ground

    Requires a DataGridView and button.

    .
    .
    .
        Private Sub Button1_Click(
            ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles Button1.Click
            If bsData.Current IsNot Nothing Then
                Dim Row = CType(bsData.Current, DataRowView)
                If Row.Item(DataPropertyName).ToString = "B" Then
                    Row.Item(DataPropertyName) = "A"
                Else
                    Row.Item(DataPropertyName) = "B"
                End If
                bsData.ResetCurrentItem()
            End If
        End Sub
    End Class


    You are assigning an arbitary value "A" or "B" to the cell. However, I need to assign the value that was displayed in the cell when the focus leaves the cell. The displayed valued could be the first value in the combobox or the value selected in the previous row. How do I find the value that was displayed in the cell when the focus leaves it.

    Saturday, November 24, 2012 5:31 PM
  • Perhaps the following might be common ground

    Requires a DataGridView and button.

    .
    .
    .
        Private Sub Button1_Click(
            ByVal sender As System.Object,
            ByVal e As System.EventArgs) Handles Button1.Click
            If bsData.Current IsNot Nothing Then
                Dim Row = CType(bsData.Current, DataRowView)
                If Row.Item(DataPropertyName).ToString = "B" Then
                    Row.Item(DataPropertyName) = "A"
                Else
                    Row.Item(DataPropertyName) = "B"
                End If
                bsData.ResetCurrentItem()
            End If
        End Sub
    End Class


    You are assigning an arbitary value "A" or "B" to the cell. However, I need to assign the value that was displayed in the cell when the focus leaves the cell. The displayed valued could be the first value in the combobox or the value selected in the previous row. How do I find the value that was displayed in the cell when the focus leaves it.

    Yep I am assigning an arbitary value for demong. If I needed to know what the value was if it was changed would be to hook into the underlying DataTable event ColumnChanged.

    Private Sub MyDataTable_ColumnChanged(ByVal sender As Object, ByVal e As System.Data.DataColumnChangeEventArgs)
        If Not e.Row.RowState = DataRowState.Deleted AndAlso Not e.Row.RowState = DataRowState.Detached Then
            If e.Column.ColumnName = DataPropertyName Then
                Console.WriteLine("Original [{0}] Proposed [{1}]",
                                  e.Row(e.Column.ColumnName, DataRowVersion.Original),
                                  e.Row(e.Column.ColumnName, DataRowVersion.Proposed)
                )
            End If
        End If
    End Sub

    Below shows how to implement within the code I provided already

    dt.Rows.Add(New Object() {"B", "BBBBB"})
    dt.Rows.Add(New Object() {"A", "DDDDD"})
    dt.Rows.Add(New Object() {"B", "CCCCC"})
    dt.AcceptChanges()
    bsData.DataSource = dt
    AddHandler dt.ColumnChanged, AddressOf MyDataTable_ColumnChanged
    DataGridView1.DataSource = bsData

    For the DataGridView we can use CellLeave event and invoke EndEdit for the ComboBox column

        Private Sub DataGridView1_CellLeave(
            ByVal sender As Object,
            ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _
        Handles DataGridView1.CellLeave
            If e.ColumnIndex = 0 Then
                DataGridView1.EndEdit()
                Label1.Text = String.Format("Cell Leave [{0}]",
                                DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value
                             )
            End If
        End Sub


    KSG

    • Marked as answer by Dinesh KTE Saturday, November 24, 2012 7:06 PM
    Saturday, November 24, 2012 6:26 PM
  • YES SOLVED !!!

    Taking cue from your last post, I did this:

    1) Create a form level variable to hold the last value assigned to the combobox column.

    2) Set up an event handler for the ColumnChanged event of the DataTable that is acting as the data source of the DataGridView control. In this event handler, I assign the new value for the column to the form level variable declared in the previous step.

    3) In the cell changed event, I check if the current cell has a value assigned to it. If yes, then I do nothing otherwise a further check is made if the form level variable is initialised or not. If initialised, its value is assigned to the cell, otherwise the first value from the datasource of the combobox column is assigned to the cell.

    It all is working perfectly now. And I can't thank enough to you, KSG. You have been persistent and gracious all through. Many thanks again!!

    -Dinesh

    Saturday, November 24, 2012 7:06 PM
  • Late to the game here, but maybe this will save somebody hours trying to make it work.  Never could get the ComboBox in the grid to reflect changes to the BindingSource, so I just did a work-around to simulate a combobox.  Put a button in the column instead of a combobox.  On the CellEnter event, opened a borderless form with a ListBox.  The grid just binds to one data source (no datasource for the combobox), and everything works pretty straight forward.  

    Saturday, December 21, 2013 12:18 AM