我为我的DataGridView设置了一个ComboBoxColumn,并从枚举中设置了它的可选值。它主要按照我的意愿工作,但有以下例外。
每当我单击下拉箭头然后选择其中一个枚举值时,它仍然处于“中间”状态,其中未触发CellValueChanged事件。我需要专注于另一个单元格或其他控件才能触发事件。
我还有一个DataGridView的Leaving事件的事件处理程序,它通过确保没有单元格为空来“验证”内容。
因此,如果我创建一行并填充所有单元格并进入(当前为空白)ComboBox列,请将其更改为值,然后单击“运行”按钮;弹出我的错误对话框,因为ComboBox选择未“保存”。
我该如何解决这个问题?有没有办法在我从下拉列表中选择一个值后自动“设置”值?
谢谢!
答案 0 :(得分:22)
您应该使用CurrentCellDirtyStateChanged
事件并强制在网格上进行提交编辑:
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
希望它有所帮助!
答案 1 :(得分:12)
我会通过检查单元格类型而不是列类型来扩展Moop的答案。
dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
if (CurrentCell is DataGridViewComboBoxCell)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
dataGridView1.EndEdit();
}
}
答案 2 :(得分:4)
在强制DataGridViewColumn
之前,我会通过检查DataGridViewComboBoxColumn
是CommitEdit
的类型来扩展ionden的答案。这样可以防止其他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 :(得分:3)
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();
}
}
答案 4 :(得分:1)
在某些情况下,在焦点完全离开行之前,该值不会粘住。在这种情况下,强制当前编辑结束的唯一方法是在整个绑定上下文中结束它:
mGridView.CommitEdit(DataGridViewDataErrorContexts.Commit);
mGridView.BindingContext[mGridView.DataSource].EndCurrentEdit(); // <<===
我找到了这个提示here。
答案 5 :(得分:0)
以下是我解决问题的方法
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
答案 6 :(得分:0)
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.BeginEdit(true);
ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
string Valor= cmbMiCtrl.Text;
dataGridView1.EndEdit();
}
答案 7 :(得分:0)
我看到的一个问题:如果您选择以下内容将无效:
GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;
答案 8 :(得分:0)
我花了两个小时搜索错误,因为我没注意到如果没有散焦,单元格值不会被保存,或者更好地说我刚注意到单元格没有散焦,因为组合框出了问题保存时(btn事件)。 不仅如此,EditOnEnter-Mode优先于上面显示的大多数其他方法都有效。使用EditOnEnter的原因是当您使用DataGridViewComboBoxColumn时,如果未将EditMode设置为EditOnEnter,则必须单击两次才能打开下拉列表。
this.dataGridView.EditMode = DataGridViewEditMode.EditOnKeystrokeOrF2;
this.dataGridView.EndEdit();
this.dataGridView.EditMode = DataGridViewEditMode.EditOnEnter;
我希望这会有所帮助。我花了大约两个小时想知道为什么对象中的值与GUI上显示的不一样。
答案 9 :(得分:0)
我正在添加我的答案,作为已经发生的讨论的后续行动。我试图构建一个每行具有不同组合框的DataGridView。他们还必须对单击进行响应。并且,当进行选择时,需要根据组合框选择来改变行中的另一个单元。一旦做出选择,就需要进行更改。我的主要问题,如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;
}
}
}
}
答案 10 :(得分:0)
您应该使用CellValueChanged来触发网格上的更改事件,并在事件内部提交更改并保留控件以便在选中项目后保存该项目。
private void FilterdataGrid_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
FilterdataGrid.CommitEdit(DataGridViewDataErrorContexts.Commit);
FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}
希望它有所帮助!
答案 11 :(得分:0)
感谢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();
}