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();
}