DataGridView组合框列:从下拉列表中进行选择后更改单元格值

本文关键字:选择 行选 单元格 组合 下拉列表 DataGridView | 更新日期: 2023-09-27 18:27:00

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

每当我单击下拉箭头,然后选择其中一个枚举值时,它都会保持在某种"中间"状态,其中CellValueChanged事件不会被触发。我需要专注于另一个单元格或另一个控件,以便事件启动。

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

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

我该怎么绕过这个?有没有一种方法可以让我从下拉列表中选择一个值后,它会自动"设置"该值?

谢谢!

DataGridView组合框列:从下拉列表中进行选择后更改单元格值

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

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

希望它能有所帮助!

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

dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;
void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    if (CurrentCell is DataGridViewComboBoxCell)
    {
        dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
        dataGridView1.EndEdit();
    }
}

在强制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);
        }
    }

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

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

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

我在这里找到了这个提示。

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

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

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

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

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

以下是我如何解决问题

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

我看到的一个问题是:如果您选择:GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

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

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

希望它能有所帮助!