【问题标题】:DataGridView ComboBox Column: Change cell value after selection from dropdown is made?DataGridView ComboBox Column:从下拉列表中选择后更改单元格值?
【发布时间】:2012-03-07 20:10:35
【问题描述】:

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

每当我单击下拉箭头,然后选择其中一个枚举值时,它都会保持某种“中间”状态,其中不会触发 CellValueChanged 事件。我需要关注另一个单元格或另一个控件才能触发事件。

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

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

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

谢谢!

【问题讨论】:

    标签: c# datagridview


    【解决方案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();
        }
    }
    

    【讨论】:

    • EndEdit() 救我一命。谢谢 Bioukh
    • @Bioukh 为了接收对DataGridViewComboBoxColumn 字段的更改,我创建了一个事件处理程序:private void Dgv_CurrentCellDirtyStateChanged (object sender, EventArgs e) {dgv.CommitEdit (DataGridViewDataErrorContexts.Commit); dgv.EndEdit (); iNumerator ++; textBox1.Text = textBox1.Text.Insert (0, "*** Event_CurrentCellDirtyStateChanged_4 -" + iNumerator + "\ r \ n"); // ++ Add a line to the beginning }
    • @Bioukh In textBox1.Text 我得到结果:*** Event_CurrentCellDirtyStateChanged_4 - 2 *** Event_CurrentCellDirtyStateChanged_4 - 1 换句话说,事件触发了两次。 问题: 事件触发两次是否正确?事件可以触发一次吗?或者这不是一个根本问题?
    • 通过提交当前cell版本,该函数会改变当前cell的脏状态(即IsCurrentCellDirty),所以确实会再次触发该事件。
    【解决方案3】:

    我会通过在强制CommitEdit 之前检查DataGridViewColumn 是否是DataGridViewComboBoxColumn 的类型来扩展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);
            }
        }
    

    【讨论】:

      【解决方案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(); // <<===
        

        我发现了这个提示here。

        【讨论】:

          【解决方案6】:

          我花了大约两个小时来寻找错误,因为我没有注意到如果单元格值没有散焦,则不会保存它,或者更好地说我只是注意到单元格没有散焦,因为组合框变白了保存时(btn 事件)。 不仅如此,EditOnEnter-Mode 比上面显示的大多数其他方法更有效。使用 EditOnEnter 的原因是,当你使用 DataGridViewComboBoxColumn 时,如果没有将 EditMode 设置为 EditOnEnter,则必须单击两次才能打开下拉菜单。

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

          我希望这会有所帮助。我花了大约两个小时想知道为什么对象中的值与 GUI 上显示的值不同。

          【讨论】:

            【解决方案7】:

            我正在添加我的答案作为已经发生的讨论的后续行动。我试图构建一个每行具有不同组合框的 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;
                    }
                }
            }
            }
            

            【讨论】:

              【解决方案8】:

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

              【讨论】:

                【解决方案9】:

                这是我解决问题的方法

                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
                

                【讨论】:

                • 请看c#标签。
                【解决方案10】:
                void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
                {
                    dataGridView1.BeginEdit(true);
                    ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
                    string Valor= cmbMiCtrl.Text;
                    dataGridView1.EndEdit();
                }
                

                【讨论】:

                  【解决方案11】:

                  我看到的一个问题:如果您选择,它将不起作用: GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

                  【讨论】:

                    【解决方案12】:

                    您应该使用 CellValueChanged 来触发网格上的更改事件,并且在事件内部您应该提交更改并离开控件以便在选择项目后保存它。

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

                    希望对你有帮助!

                    【讨论】:

                    • 您能否尝试在您的答案中添加一些上下文和解释,以便未来的用户可以从中受益?用纯代码回答不太可能成为未来用户更通用的答案。请查看How to Answer 了解更多信息。
                    • 这会导致 CommitEdit 上的堆栈溢出(对于未绑定的数据)。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-03-08
                    • 2015-11-14
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多