DataGridView ComboBox Column: изменить значение ячейки после выбора из раскрывающегося списка?

Я установил ComboBoxColumn для моего DataGridView и установил его выбираемые значения из перечисления. Он в основном работает, как я хотел бы, со следующим исключением.

Всякий раз, когда я нажимаю стрелку вниз, а затем выбираю одно из значений перечисления, оно остается своего рода "промежуточным" состоянием, в котором событие CellValueChanged не запускается. Мне нужно сосредоточиться на другой ячейке или другом элементе управления для запуска события.

У меня также есть обработчик события для вывода DataGridView, который "проверяет" содержимое, убедившись, что ни одна ячейка не пуста.

Итак, если я создаю строку и заполню все ячейки и перейду в столбец (в настоящее время пустой) ComboBox, измените ее на значение и затем нажмите кнопку "Выполнить"; мой диалог с ошибкой появляется, потому что выбор ComboBox не был "сохранен".

Как я могу обойти это? Есть ли способ, который после того, как я выберем значение из раскрывающегося списка, автоматически "устанавливает" значение?

Спасибо!

Ответы

Ответ 1

Вы должны использовать событие CurrentCellDirtyStateChanged и принудительно изменить фиксацию в сетке:

    private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
    }

Надеюсь, что это поможет!

Ответ 2

Я бы продолжил ответ Moop, проверив тип ячейки вместо типа столбца.

dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;

void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (CurrentCell is DataGridViewComboBoxCell)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        dataGridView1.EndEdit();
    }
}

Ответ 3

Я продолжил бы ионный ответ, проверив, является ли DataGridViewColumn типом DataGridViewComboBoxColumn перед тем, как заставить CommitEdit. Это предотвратит слишком много других объектов DataGridViewColumn.

    dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;

    void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        DataGridViewColumn col = dataGridView1.Columns[dataGridView1.CurrentCell.ColumnIndex];
        if (col is DataGridViewComboBoxColumn)
        {
            dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        }
    }

Ответ 4

Событие CurrentCellDirtyStateChanged фиксировало взаимодействие с мышью для этой проблемы, но оно нарушает взаимодействие с клавиатурой - с помощью F4, стрелки вверх/вниз, каждый стрелок вызывает изменение состояния грязной и совершает редактирование. Решение, которое я нашел, состояло в том, чтобы захватить "DataGridViewComboBoxEditingControl" при его создании и прикрепить к нему событие DropDownClosed. Это работает для взаимодействия с клавиатурой и мышью. В этом примере мы расширили DataGridView, чтобы каждый экземпляр наследовал эту функциональность:

    protected override void OnEditingControlShowing(DataGridViewEditingControlShowingEventArgs e)
    {
        DataGridViewComboBoxEditingControl control = e.Control as DataGridViewComboBoxEditingControl;
        if (control != null)
        {
            control.DropDownClosed -= ComboBoxDropDownClosedEvent;
            control.DropDownClosed += ComboBoxDropDownClosedEvent;
        }
        base.OnEditingControlShowing(e);
    }

    void ComboBoxDropDownClosedEvent(object sender, EventArgs e)
    {
        DataGridViewComboBoxCell cell = CurrentCell as DataGridViewComboBoxCell;
        if ((cell != null) && cell.IsInEditMode)
        {
            CommitEdit(DataGridViewDataErrorContexts.Commit);
            EndEdit();
        }
    }

Ответ 5

В некоторых случаях значение не будет удерживаться до тех пор, пока фокус не покинет строку целиком. В этом случае единственным способом принудительного завершения текущего редактирования является его завершение во всем контексте привязки:

mGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
mGridView.BindingContext[mGridView.DataSource].EndCurrentEdit(); // <<===

Я нашел этот совет здесь.

Ответ 6

Вот как я решил проблему

Private Sub dgvEcheancier_CurrentCellDirtyStateChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles dgvEcheancier.CurrentCellDirtyStateChanged
        nbreClick += 1
            With dgvEcheancier
                Select Case .CurrentCell.ColumnIndex
                Case 9
                    Dim col As DataGridViewComboBoxColumn = .Columns(9)
                    If TypeOf (col) Is DataGridViewComboBoxColumn Then
                        dgvEcheancier.CommitEdit(DataGridViewDataErrorContexts.Commit)
                        If nbreClick = 2 Then
                            MessageBox.Show("y" & "val=" & .CurrentCell.Value)
                            nbreClick = 0
                        End If
                    End If

            End Select
            End With

Ответ 7

void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    dataGridView1.BeginEdit(true);
    ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
    string Valor= cmbMiCtrl.Text;
    dataGridView1.EndEdit();
}

Ответ 8

Одна из проблем, которые я видел: она не будет работать, если вы выберете: GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

Ответ 9

Я трачу, как два часа на поиск ошибки, потому что я не заметил, что значение ячейки не сохраняется, если оно не расфокусировано, или, лучше сказать, я заметил, что ячейка не расфокусирована, потому что combobox whited out в то время как сохранение (событие btn). Не только то, что режим EditOnEnter преобладает, что большинство других методов, показанных выше, работают. Причина использования EditOnEnter заключается в том, что когда вы используете DataGridViewComboBoxColumn, вам нужно щелкнуть два раза, чтобы открыть выпадающий список, если вы не установите EditMode в EditOnEnter.

this.dataGridView.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2; this.dataGridView.EndEdit(); this.dataGridView.EditMode = DataGridViewEditMode.EditOnEnter;

Надеюсь, это поможет. Это стоило мне около двух часов, задаваясь вопросом, почему значение в объекте не то же самое, что показано в графическом интерфейсе.

Ответ 10

Я добавляю свой ответ в качестве продолжения обсуждения, которое уже произошло. Я пытался создать DataGridView, у которого были разные comboboxes для каждой строки. Они также должны были реагировать на один клик. И, когда был сделан выбор, другая ячейка в строке должна была быть изменена в соответствии с выбором combobox. Изменения должны были произойти, как только был сделан выбор. Моя основная проблема, такая как OP, заключалась в том, что изменение не произойдет до тех пор, пока комбобокс не потеряет фокус.

Итак, вот полный рабочий минимальный пример такого DataGridView. Я должен был свести его к минимуму, потому что все мои требования к работе в то же время были сложными. Несколько SO-сообщений занялись этим, и я позже обновляю свое сообщение. Но сейчас, здесь идет...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace TestDGV
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private Panel panel2;
    private DataGridView TestGrid;

    private void InitializeComponent()
    {
        this.panel2 = new System.Windows.Forms.Panel();
        this.SuspendLayout();
        // 
        // panel2
        // 
        this.panel2.Dock = DockStyle.Fill;
        this.panel2.Name = "panel2";
        this.panel2.TabIndex = 1;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(661, 407);
        this.Controls.Add(this.panel2);
        this.Name = "Form1";
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        this.ResumeLayout(false);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        //basic grid properties
        TestGrid = new DataGridView();
        TestGrid.Dock = DockStyle.Fill;
        TestGrid.AutoGenerateColumns = false;
        TestGrid.Name = "TestGrid";
        TestGrid.ReadOnly = false;
        TestGrid.EditMode = DataGridViewEditMode.EditOnEnter;

        //Event handlers
        TestGrid.DataBindingComplete += TestGrid_DataBindingComplete;
        TestGrid.CurrentCellDirtyStateChanged += TestGrid_CurrentCellDirtyStateChanged;
        TestGrid.CellValueChanged += TestGrid_CellValueChanged;

        //columns
        var textCol = new DataGridViewTextBoxColumn();
        textCol.HeaderText = "Text";
        textCol.Name = "Text";
        textCol.DataPropertyName = "Text";
        textCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
        TestGrid.Columns.Add(textCol);

        var comboCol = new DataGridViewComboBoxColumn();
        comboCol.HeaderText = "ComboBox";
        comboCol.Name = "ComboBox";
        comboCol.AutoComplete = true;
        comboCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
        TestGrid.Columns.Add(comboCol);

        var resultCol = new DataGridViewTextBoxColumn();
        resultCol.HeaderText = "Result";
        resultCol.Name = "Result";
        resultCol.DataPropertyName = "Result";
        resultCol.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
        TestGrid.Columns.Add(resultCol);

        //Bind the data
        Datum.TestLoad();
        TestGrid.DataSource = Datum.Data;

        panel2.Controls.Add(TestGrid);
    }

    void TestGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        if (e.RowIndex < 0 || e.ColumnIndex < 0)
            return;

        var row = TestGrid.Rows[e.RowIndex];
        var cell = row.Cells[e.ColumnIndex];
        if (cell is DataGridViewComboBoxCell)
        {
            var val = cell.Value as string;
            var datum = row.DataBoundItem as Datum;
            datum.Current = val;
            row.Cells["Result"].Value = datum.Result;
            TestGrid.InvalidateRow(e.RowIndex);
        }
    }


    void TestGrid_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        if(TestGrid.CurrentCell is DataGridViewComboBoxCell)
        {
            TestGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
            TestGrid.EndEdit();
        }
    }

    void TestGrid_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
    {
        foreach (DataGridViewRow row in TestGrid.Rows)
        {
            var datum = row.DataBoundItem as Datum;
            if (datum == null)
                return;

            var cell = row.Cells["ComboBox"] as DataGridViewComboBoxCell;
            if (cell.DataSource == null)
            {
                cell.DisplayMember = "KeyDisplayValue";
                cell.ValueMember = "KeyValue";
                cell.DataSource = (row.DataBoundItem as Datum).Combo;
                cell.Value = (row.DataBoundItem as Datum).Current;
            }
        }
        TestGrid.DataBindingComplete -= TestGrid_DataBindingComplete;
    }

    public class Datum
    {
        public static void TestLoad()
        {
            var t1 = new Triplet[] {
                     new Triplet("1", "World", "Everyone" ),
                     new Triplet("2", "Charlie", "Friend of Algernon" ),
                     new Triplet("3", "Lester", "Phenomenal programmer" ),
            };
            var t2 = new Triplet[] {
                     new Triplet("1", "World", "Everyone" ),
                     new Triplet("4", "Mary", "Wife of George Bailey" ),
                     new Triplet("3", "Lester", "Phenomenal programmer" ),
            };
            Data.Add(new Datum("hello, ", t1.ToList()));
            Data.Add(new Datum("g'bye, ", t2.ToList()));
        }
        public static List<Datum> Data = new List<Datum>();

        public Datum(string text, List<Triplet> combo)
        {
            this._text = text;
            this._combo = combo.ToDictionary<Triplet,string>(o => o.KeyValue);
            this.Current = combo[0].KeyValue;
        }

        private string _text;
        public string Text
        {
            get
            {
                return _text;
            }
        }

        private Dictionary<string, Triplet> _combo;
        public List<Triplet> Combo
        {
            get
            {
                return _combo.Values.ToList();
            }
        }

        private string _result;
        public string Result
        {
            get
            {
                return _result;
            }
        }

        private string _current;
        public string Current
        {
            get
            {
                return _current;
            }
            set
            {
                if (value != null && _combo.ContainsKey(value))
                {
                    _current = value;
                    _result = _combo[value].Description;
                }
            }
        }
    }

    public class Triplet
    {
        public string KeyValue { get; set; }
        public string KeyDisplayValue { get; set; }
        public string Description { get; set; }
        public Triplet(string keyValue, string keyDisplayValue, string description)
        {
            KeyValue = keyValue;
            KeyDisplayValue = keyDisplayValue;
            Description = description;
        }
    }
}
}

Ответ 11

Вы должны использовать CellValueChanged, который запускает событие изменения в сетке, и внутри события вы должны зафиксировать изменения и оставить элемент управления, чтобы сохранить элемент после его выбора.

    private void FilterdataGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    FilterdataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);      

    FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}

Надеюсь, что это поможет!

Ответ 12

Спасибо Droj за отзыв о EndCurrentEdit, который мне нужен, чтобы он работал у меня. Это то, что я сделал, чтобы мгновенно передать DataGridViewComboBoxColumns и DataGridViewCheckBoxColumns:

private void dataGridViewEnumerable_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
  var dataGridView = sender as DataGridView;
  if (dataGridView == null || dataGridView.CurrentCell == null)
    return;
  var isComboBox = dataGridView.CurrentCell is DataGridViewComboBoxCell;
  if ((isComboBox || dataGridView.CurrentCell is DataGridViewCheckBoxCell) 
    && dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit) 
    && isComboBox && dataGridView.EndEdit())
    dataGridView.BindingContext[dataGridView.DataSource].EndCurrentEdit();
}