DataGridView ComboBox列:从下拉列表中选择后更改单元格值?

时间:2012-03-07 20:10:36

标签: c# datagridview

我为我的DataGridView设置了一个ComboBoxColumn,并从枚举中设置了它的可选值。它主要按照我的意愿工作,但有以下例外。

每当我单击下拉箭头然后选择其中一个枚举值时,它仍然处于“中间”状态,其中未触发CellValueChanged事件。我需要专注于另一个单元格或其他控件才能触发事件。

我还有一个DataGridView的Leaving事件的事件处理程序,它通过确保没有单元格为空来“验证”内容。

因此,如果我创建一行并填充所有单元格并进入(当前为空白)ComboBox列,请将其更改为值,然后单击“运行”按钮;弹出我的错误对话框,因为ComboBox选择未“保存”。

我该如何解决这个问题?有没有办法在我从下拉列表中选择一个值后自动“设置”值?

谢谢!

12 个答案:

答案 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之前,我会通过检查DataGridViewComboBoxColumnCommitEdit的类型来扩展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();
}