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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9608343/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 08:08:04  来源:igfitidea点击:

DataGridView ComboBox Column: Change cell value after selection from dropdown is made?

c#datagridview

提问by john

I have setup a ComboBoxColumn for my DataGridView and set its selectable values from an enumeration. It mostly works as I would like with the following exception.

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

Whenever I click the dropdown arrow and then select one of the enum values, it remains in sort of a "intermediate" state where the CellValueChanged event isn't triggered. I need to focus on another cell or another control for the event to fire.

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

I also have an event handler for the DataGridView's Leaving event which "validates" the contents by making sure that no cell is empty.

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

So, if I create a row and fill all the cells and come to the (currently blank) ComboBox column, change it to a value, and then click a Run button; my error dialog pops up because the ComboBox selection wasn't "saved".

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

How can I get around this? Is there a way that after I select a value from the drop down it automatically "sets" the value?

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

Thanks!

谢谢!

采纳答案by ionden

You should use CurrentCellDirtyStateChangedevent and force a commit edit on the grid:

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

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

Hope it helps!

希望能帮助到你!

回答by Moop

I would extend ionden's answer by checking if the DataGridViewColumnis the type of DataGridViewComboBoxColumnbefore forcing the CommitEdit. This will prevent other DataGridViewColumnobjects from committing too early.

如果我会通过检查扩展ionden的回答DataGridViewColumn是类型DataGridViewComboBoxColumn迫使前CommitEdit。这将防止其他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);
        }
    }

回答by LANHA

Here's how I solved the issue

这是我解决问题的方法

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

回答by Droj

In some cases, the value won't stick until the focus has left the row entirely. In that case, the only way to force the current edit to end is to end it on the whole binding context:

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

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

I found this tip here.

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

回答by Bioukh

I would extend Moop's answer by checking the cell type instead of the column type.

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

dataGridView1.CurrentCellDirtyStateChanged += dataGridView1_CurrentCellDirtyStateChanged;

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

回答by JxDarkAngel

void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
    dataGridView1.BeginEdit(true);
    ComboBox cmbMiCtrl=(ComboBox)dataGridView1.EditingControl;
    string Valor= cmbMiCtrl.Text;
    dataGridView1.EndEdit();
}

回答by SeyoS

One problem that I saw : It won't work if you choose : GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

我看到的一个问题:如果你选择它就行不通: GridView.EditMode = System.Windows.Forms.DataGridViewEditMode.EditOnEnter;

回答by Dennis E

I spend like two hours searching for an error because I did not notice that the cell value does not get saved if it′s not defocused, or better to say I just noticed that the cell is not defocused because the combobox whited out while saving(btn event). Not only that, the EditOnEnter-Mode prevails that most other methods shown above work. The reason to use EditOnEnter is that when you use a DataGridViewComboBoxColumn, you have to click two times to open the dropdown if you do not set EditMode to EditOnEnter.

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

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

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

I hope this helps. It cost me around two hours wondering why the value in the object is not the same then shown on the GUI.

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

回答by Les

I am adding my answer as a follow-up to the discussion that has already occurred. I was trying to build a DataGridView that had different comboboxes per row. They also had to be responsive to a single click. And, when the selection was made, another cell in the row needed to be changed according to the combobox selection. The change needed to happen as soon as the selection was made. My main problem, like the OP's, was the change wouldn't happen until the combobox lost focus.

我正在添加我的答案,作为已经发生的讨论的后续行动。我试图构建一个每行具有不同组合框的 DataGridView。他们还必须对单击做出响应。并且,在进行选择时,需要根据组合框选择更改行中的另一个单元格。做出选择后需要立即进行更改。我的主要问题,就像 OP 一样,是在组合框失去焦点之前不会发生变化。

So, here is a full working minimal example of such a DataGridView. I had to bring it down to a minimum because getting all my requirements to work at the same time was tricky. Several SO posts went into making this, and I will update my post with references later. But for now, here goes...

因此,这里是此类 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;
        }
    }
}
}

回答by Shyqyri Rama

You should use CellValueChanged which fires the change event on grid and inside the event you should commit changes and leave the control in order to save the item after it is selected.

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

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

    FilterdataGrid.EndEdit(DataGridViewDataErrorContexts.LeaveControl);
}

Hope it helps!

希望能帮助到你!