首页 文章

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

提问于
浏览
16

我为我的DataGridView设置了一个ComboBoxColumn,并从枚举中设置了它的可选值 . 它主要按照我想要的方式运行,但有以下异常 .

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

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

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

我怎么能绕过这个?有没有办法在我从下拉列表中选择一个值后自动“设置”该值?

谢谢!

12 回答

  • 0

    我花了两个小时搜索错误,因为我没有注意到如果没有散焦,单元格值不会被保存,或者更好地说我只是注意到单元格没有散焦,因为组合框在保存时变白了( btn事件) . 不仅如此,EditOnEnter-Mode还可以解决上面显示的大多数其他方法的问题 . 使用EditOnEnter的原因是,当您使用DataGridViewComboBoxColumn时,如果未将EditMode设置为EditOnEnter,则必须单击两次才能打开下拉列表 .

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

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

  • 21

    您应该使用 CurrentCellDirtyStateChanged 事件并强制在网格上进行提交编辑:

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

    希望能帮助到你!

  • 0

    我会通过在强制 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);
            }
        }
    
  • 12

    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

    这就是我解决这个问题的方法

    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
    
  • 0
    void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        dataGridView1.BeginEdit(true);
        ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
        string Valor= cmbMiCtrl.Text;
        dataGridView1.EndEdit();
    }
    
  • 0

    我会通过检查单元格类型而不是列类型来扩展Moop的答案 .

    dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;
    
    void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
    {
        if (CurrentCell is DataGridViewComboBoxCell)
        {
            dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
            dataGridView1.EndEdit();
        }
    }
    
  • 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;
            }
        }
    }
    }
    
  • 3

    感谢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

    您应该使用CellValueChanged来触发网格上的更改事件,并在事件内部提交更改并保留控件以便在选中项目后保存该项目 .

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

    希望能帮助到你!

  • 4

    我看到的一个问题:如果你选择它将无法工作: GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

  • 0

    在某些情况下,在焦点完全离开行之前,该值不会粘住 . 在这种情况下,强制当前编辑结束的唯一方法是在整个绑定上下文中结束它:

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

    我找到了这个提示here .

相关问题