5

I have a DataGridView with a few DataGridViewComboBoxColumns. There is a CellEnter event handler on the DataGridView for the purpose of single-click dropping down of the comboboxes.

The column is bound to a List of KeyValuePairs, ValueMember being "Key", and DisplayMember being "Value".

When I click on a combobox column, it works fine. However, if the cell is in the "dropdown" state and I click on another combobox (same column, different row), it properly deselects the old cell, selects and drops down the new cell, however the selected value on top changes to the value from the old cell for a split second, before changing back to the correct one.

For example, let's say the list is A, B, C. In row1, A is selected, in row2, B is selected. I click the cell in row1, all is as it should be. Then, while this cell is dropped down, I click on the cell in row2. It drops down properly, but the selected value on top becomes A, then switches back to B (the correct one) immediately.

If I click on a cell in some other column before clicking the second combobox cell, this doesn't happen.

Is there a way to prevent this from happening?

Example code to reproduce the problem (the event handlers are hooked up to the obvious events):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace PDGV
{
    public partial class Form1 : Form
    {
        List<KeyValuePair<string, string>> bindingList = new List<KeyValuePair<string, string>>();

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            dataGridView1.Rows.Add(10);
            bindingList.Add(new KeyValuePair<string,string>("aaa", "111"));
            bindingList.Add(new KeyValuePair<string,string>("bbb", "222"));
            bindingList.Add(new KeyValuePair<string,string>("ccc", "333"));
            bindingList.Add(new KeyValuePair<string,string>("ddd", "444"));
            bindingList.Add(new KeyValuePair<string,string>("eee", "555"));
            BindComboList(2, bindingList);

        }

        private void BindComboList(int columnIndex, object list)
        {
            var column = dataGridView1.Columns[columnIndex] as DataGridViewComboBoxColumn;
            if (column != null)
            {
                column.DataSource = new BindingSource(list, null);
                column.DisplayMember = "Value";
                column.ValueMember = "Key";
            }
        }

        private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
        {
            if (e.RowIndex == -1)
                return;

            dataGridView1.BeginEdit(true);
            var control = dataGridView1.EditingControl as DataGridViewComboBoxEditingControl;
            if (control != null)
                control.DroppedDown = true;
        }
    }
}
svinja
  • 5,495
  • 5
  • 25
  • 43
  • It isn't clear what your problem is - I've tried recreating this from your description and can't duplicate anything like what you describe. Please provide a minimal and complete code example of this - the code behind from a form the duplicates this issue (with only related code) would be best. – David Hall Jul 16 '12 at 13:29
  • I have added the code. Note - it is mandatory that the combo box opens with a single click. I cannot just remove this functionality. – svinja Jul 16 '12 at 14:08
  • Still not sure what your problem is - could you please attach all your events in code behind and if possible also add columns. It isn't clear exactly what you have done in the designer so a completely standard datagridview (just dragged onto an empty form) with changes made in code would be easier to follow. Then I'm trying to follow the exact problem you have - If I have two combo cells, both with values selected, then I drop down one cell and select the second cell - you say you momentarily see the selected value from the first cell in the second cell? I don't see that at all. – David Hall Jul 16 '12 at 14:24
  • Strike that - finally managed to see what your problem is. – David Hall Jul 16 '12 at 14:26
  • Still looking at this - have the horrible feeling that you are out of luck. The issue is that the DataGridView only has **1** instance of an editing control when you moved between cells in the same column. So you see the last cells value sometimes because it is redrawing the control before updating it. I'm trying to find an event to hook into to force the editing control to redraw in time, but no luck so far. As an aside - it is better to force the control to open in the EditingControlShowing event than in the CellEnter event. – David Hall Jul 16 '12 at 15:11
  • This issue has bothered me for a year. I ended up tossing my subclassed combobox beneath the textbox cell/textbox column, so the combobox only appears when they click on the cell. The cell normally appears as a textbox, with the combobox value in it. It looks a lot cleaner. – Isaac Bolinger Jul 18 '12 at 06:35

3 Answers3

4

The Cuase:

Deep down in DataGridView's EndEdit method it stores a copy of the previously used EditingControl this.latestEditingControl = this.editingControl; Then when you start editing another cell it Kicks in BeginEditInternal. During this it checks if the latestEditingControl is not null and the editing types are the same as the last cell, if so it reuses the control, which is why you see the flash, it really is the same control.

The Solution:

Set the latestEditingControl to null, this control is not accessible through normal properties/methods, have to use reflection. NOTE: This leads to a totally different annoyance, now the cell you are leaving flashes white :), but it does resolve the specific issue you asked for help on.

    private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
    {
        if (e.RowIndex == -1)
            return;

        //this.latestEditingControl
        Type t = dataGridView1.GetType();
        FieldInfo viewSetter = t.GetField("latestEditingControl", BindingFlags.Default | BindingFlags.NonPublic | BindingFlags.Instance);
        viewSetter.SetValue(dataGridView1, null);
    }
Peter
  • 9,643
  • 6
  • 61
  • 108
  • Thanks! Not a perfect solution due to the white flashing, but at least something. :D – svinja Jul 23 '12 at 14:02
  • Works for me without any white flashing (VS 2015) :D – Kobbe Jul 28 '16 at 11:35
  • Great tip! I have the same issue and this fix the problem. Anyway I have the "flashes white" unwanted behavior. –  Jul 24 '19 at 13:25
  • @ElmoDev001 I'm glad you got it working. I saw your previous comment and had no idea how to help you after 7 years :) – Peter Jul 24 '19 at 13:26
  • It was a trivial issue, my dataGridView is a inherit class so I have to use dataGridView1.GetType().BaseType (got solution here https://stackoverflow.com/questions/48008545/c-sharp-cant-reflect-into-private-field ) –  Jul 24 '19 at 13:29
2

I didn't 100% repro the problem as described (I never saw a lag), but try unsubscribing to the CellEnter event and try changing your code to this (from How do I get DataGridView comboboxes to display their drop down list in one click?)

public Form1() {
  InitializeComponent();

  dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
  dataGridView1.EditingControlShowing += dataGridView1_EditingControlShowing;
}

void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
  if (e.Control is ComboBox) {
    SendKeys.Send("{F4}");
  }
}

that should pop open the ComboBox when the user enters the cell.


or from this solution Open dropdown(in a datagrid view) items on a single click

void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
  if (e.Control is ComboBox) {
    ComboBox ctl = e.Control as ComboBox;
    ctl.Enter -= new EventHandler(ctl_Enter);
    ctl.Enter += new EventHandler(ctl_Enter);
  }
}

void ctl_Enter(object sender, EventArgs e) {
  (sender as ComboBox).DroppedDown = true;
}
Community
  • 1
  • 1
LarsTech
  • 80,625
  • 14
  • 153
  • 225
  • To repro the issue bind a combobox column with the data given, and have each cell with a selected item. Once that is done selecting cells and then moving to other cells will occasionally show an instant of the old selected value. I'm 99% sure this is the editing control being drawn before it is properly refreshed. Tried a whole lot of messing around with various events to force the control to paint correctly but couldn't get anything to work. My last idea was to subclass the ComboBoxColumn but perhaps you have a better solution than that :) – David Hall Jul 16 '12 at 19:12
  • @DavidHall I only see the old value on empty cells. Otherwise, I don't see this ghost value, and I definitely don't see the delay. Maybe it's a windows animation thing since it's trying to make a "slide" effect on dropping it down. – LarsTech Jul 16 '12 at 19:26
1

I assume its with that 3d styled combobox of windows 7 (maybe vista too, I haven't checked)

I've never seen this happen if you

comboboxcolumn.FlatStyle = System.Windows.Forms.FlatStyle.Flat;

You might not like how it looks, though. You can hide the ugly flat down arrow with

comboboxcolumn.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;

Like the other guy said, there's only one combobox control per column (the 'editing control') and that same combobox is displayed in the currently active cell of that column. When the cell isn't selected, its up to the drawing methods of the column class to paint a fake combobox for the user to see. So it could be some kind of internal combox drawing issue for that particular 3d popup style that happens when you push a value to an invisible combobox then redraw it in a different spot, who knows.

Try switching to the 'classic' windows theme. The comboboxes should behave themselves then. It would be interesting if you could force a combobox to render the way it does under the classic theme while being in Aero. I'm not sure if its possible, but you might want to look into it.

But anyway, the fact that the dgv doesn't have this problem with the other combobox styles makes me think that its just that new combobox style not fitting well with the datagridview.

Isaac Bolinger
  • 7,328
  • 11
  • 52
  • 90
  • If I do this, the previous cell flashes white, similar to what happens if I do what Peter suggested. This makes me think that the editing control is reset by default in this style, while it's reused in the new style. – svinja Jul 23 '12 at 14:07