DataGridView ComboBox Column: Изменить значение ячейки после выбора из выпадающего списка? - PullRequest
17 голосов
/ 08 марта 2012

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

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

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

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

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

Спасибо!

Ответы [ 12 ]

23 голосов
/ 08 марта 2012

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

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

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

12 голосов
/ 11 марта 2014

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

dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;

void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (CurrentCell is DataGridViewComboBoxCell)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        dataGridView1.EndEdit();
    }
}
4 голосов
/ 04 августа 2012

Я бы расширил ответ Иондена, проверив, является ли 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);
        }
    }
3 голосов
/ 26 октября 2016

Событие 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();
        }
    }
1 голос
/ 07 августа 2013

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

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

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

0 голосов
/ 15 июля 2016

Спасибо 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();
}
0 голосов
/ 14 апреля 2016

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

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

    FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}

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

0 голосов
/ 05 марта 2016

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

Итак, вот полный рабочий минимальный пример такого 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;
        }
    }
}
}
0 голосов
/ 17 января 2015

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

0 голосов
/ 22 октября 2014

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...