Click here to Skip to main content
15,943,148 members
Please Sign up or sign in to vote.
3.00/5 (1 vote)
I think I found a bug in DataGridView. When I select item in DataGridView Combobox only by mouse, then dirtystate event fires 2 times. First time when drop down is opened, and second time when I select an item from Combobox dropdown. I have overriden ProcessCmdKey so I can now open Combobox dropdown list by hitting Enter on selected cell, then selecting item with arrow keys and hitting Enter again - selects an item in Combobox. This works great. Using Enter "dirty state" event also is fired two times.

The problem (possible BUG) is when I open Combobox dropdown list by hitting Enter, and then select the item by Mouse left click. In this case dirty state event does not fire.

I wonder, why this happens? Could anyone from experienced C# gurus explain why dirty state event does not fire up?

I add a simple example for demonstration:

C#
public sealed class CustomDataGridView : DataGridView
{
    public CustomDataGridView()
    {
        AllowDrop = false;
        AllowUserToResizeColumns = false;
        AllowUserToResizeRows = false;
        RightToLeft = RightToLeft.No;
        EnableHeadersVisualStyles = false;
        ScrollBars = ScrollBars.Vertical;

        CurrentCellDirtyStateChanged += OnCurrentCellDirtyStateChanged;
    }

    private void OnCurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        var dataGridView = sender as DataGridView;
        if (dataGridView != null)
        {
            if (dataGridView.CurrentRow != null && !dataGridView.CurrentRow.IsNewRow)
            {
                if (dataGridView.CurrentCell != null)
                {
                    if (dataGridView.CurrentCell.GetType() == typeof(DataGridViewComboBoxCell))
                    {
                        if (dataGridView.IsCurrentCellDirty)
                        {
                            var inputValue = ((string)dataGridView.CurrentCell.EditedFormattedValue);
                            MessageBox.Show("Selected value is: " + inputValue);
                        }
                    }
                }
            }
        }
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (keyData == Keys.Enter)
        {
            if (CurrentCell != null)
            {
                if (CurrentCell.GetType() == typeof(DataGridViewComboBoxCell))
                {
                    if (CurrentCell.IsInEditMode)
                    {
                        var comboBox = EditingControl as DataGridViewComboBoxEditingControl;
                        if (comboBox != null)
                        {
                            //If cell is edited and combobox is shown, then on Enter press - select current item
                            if (comboBox.DroppedDown)
                            {
                                var inputValue = comboBox.GetItemText(comboBox.SelectedItem);
                                comboBox.DroppedDown = false;
                                comboBox.SelectedIndex = comboBox.Items.IndexOf(inputValue);
                                EndEdit();
                                NotifyCurrentCellDirty(true);
                                return true;
                            }
                        }
                    }
                    else
                    {
                        //If cell is selected, then on Enter press - open combobox drop down
                        BeginEdit(false);
                        var comboBox = EditingControl as DataGridViewComboBoxEditingControl;
                        if (comboBox != null)
                        {
                            comboBox.SelectedIndex = 0;
                            comboBox.DropDownStyle = ComboBoxStyle.DropDownList;
                            comboBox.Focus();
                            comboBox.DroppedDown = true;
                        }
                        return true;
                    }
                }

            }
        }
        //Process all other keys as expected
        return base.ProcessCmdKey(ref msg, keyData);
    }

}
Posted
Comments
Sinisa Hajnal 18-Nov-15 4:57am    
I experienced same behavior...I didn't try to fight it, but took reference to cell internal control, added handler to it and handled combobox changed event instead. You could also try handling cellvalidating/cellvalidated instead of celldirtystate event...

As for why it happens, I'm guessing that hosted control triggers an event on opening and again on closing and somehow your override got in the way
AcidUser85 18-Nov-15 12:28pm    
In my solution I remove rows if combobox seleted value is empty or "custom" in case if user moves to another cell or another row, another control is focused or user clicks outside of datagridview control - on to form.
That's why I cannot use cellvalidating and cellvalidated events - these event's do not allow to remove row. Also I experienced instability when using multiple events, that's why I handle everything in "DirtyStateChanged" event.
I tried assigning "SelectedIndexChanged" to combobox cell editing control, but this never worked for me.
Sinisa Hajnal 19-Nov-15 2:28am    
Hm, strange. I agree getting combobox out of the cell is not most intuitive, but it should work. Anyhow, I don't know what else to suggest except redesign. Consider doing delete in two steps: one - mark the row for deletion, finish events that triggered it (such as cellvalidating/ed); two - go through whole grid and delete what you have to delete.

Alternate, don't delete rows at all when the cell is custom, just set it to readonly, background color to something gray (or red) and font color to light gray - or however you want to describe your inactive row :)
AcidUser85 19-Nov-15 3:01am    
Hi, I forgot to mention that my solution also does automatic sort of datagridview rows by 2 columns when focus is lost or another control selected. So marking for deletion would mess up things.

It works like this:
DataGridView is in my case of two columns.
1-st column is column of comboboxes (sort of type).
2-nd column is column of textboxes (sort of text description).
A combobox has items, let's say "Custom","AAAA","BBBB","CCCC".
When user selects "Custom", then ComboBox is set to editable and user can type custom value, that will be added to ComboBox items.

I thought about making cell read-only when selected item is "Custom".
But I cannot force user to input correct value for combobox before navigating away. And since DataGridView automatically adds new blank row, it can result in multiple not correctly formatted rows.
Sinisa Hajnal 19-Nov-15 6:20am    
Marking for deletion followed immediately by deleting the rows would (from users standpoint) be invisible. Adding new row can be solved by setting property allow users to add rows to false and handling that yourself (adding a button "Add new", handle Insert key to do the same and you're done.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900