DataGridViewComboBoxColumn 不保存到数据源,除非按下输入

DataGridViewComboBoxColumn not saving to datasource unless enter is pressed

我有一个 DataGridView,其数据源 属性 绑定到一个数据表。所述 DataTable 是手动填充的(不是来自数据库)。我不会详细说明这是如何发生的,因为除了我将要描述的内容之外,一切都完美无缺。构建 DataTable 后,使用以下代码将其绑定到 DataGridView :

DgvResults.DataSource = _data.ResultsData;

此 DataGridView 的最后一列是使用以下代码创建的 DataGridViewComboBoxColumn:

var col = new DataGridViewComboBoxColumn
        {
            Name = "newMedId",
            HeaderText = @"Med ID (NEW PIS)",
            DataPropertyName = "newMedId",
            DisplayMember = "ItemId",
            ValueMember = "ItemId",
            ReadOnly = false,
            Resizable = DataGridViewTriState.False,
            SortMode = DataGridViewColumnSortMode.Programmatic
        };

        col.Items.Add("");
        col.Items.Add("DELETE");
        _data.NewFormularyData.AsEnumerable().Select(r => r.Field<string>("ItemId")).ToList()
            .ForEach(m => col.Items.Add(m));

        DgvResults.Columns.Add(col);

这实际上是用另一个 DataTable 中的所有 ID 填充此列中的每个 ComboBox,并在顶部添加一个空白值和一个“DELETE”值。

我还对此 DataGridView 进行了自定义编程排序。我不会 post 代码,因为我认为它不相关,但本质上它是通过 ColumnHeaderMouseClick 事件触发的,并根据您单击的任何列简单地对基础 DataTable 进行排序,使 DataGridView 的数据源无效 属性 和重新绑定它。

这一切都很好,除了这个:假设 DataGridViewComboBoxColumn 中的一个下拉列表是空白的,我手动将值更新为其他值。如果我在选择所述值后立即对 DataGridView 进行排序,则该值不会更新基础 DataTable 并且会丢失。如果我做同样的事情但是我在选择值后按下回车键,它会正确更新。

我的应用程序具有将所述 DataTable 导出到 Excel 电子表格的功能。单击该按钮会生成包含我更新的值的电子表格,即使我在选择它后没有按回车键也是如此。它的行为就好像 DataGridViewComboBoxCell 在更新它绑定到的 DataTable 之前需要失去焦点。立即单击列 header 对其进行排序似乎不会提供这种丢失的焦点,并且值不会更新。

如何在 DataTable 中立即更新这个值?

这是重现问题的MRE

using System;
using System.ComponentModel;
using System.Data;
using System.Windows.Forms;

namespace MRE
{
    public partial class Form1 : Form
    {
        private DataTable dt;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        SetupDgv();
        CreateDT();

        Dgv.DataSource = dt;
    }

    private void CreateDT()
    {
        dt = new DataTable();

        dt.Columns.Add("test1", typeof(string));
        dt.Columns[0].ColumnName = "test1";
        dt.Columns[0].Caption = "test1";

        dt.Columns.Add("test2", typeof(string));
        dt.Columns[1].ColumnName = "test2";
        dt.Columns[1].Caption = "test2";

        var row = dt.NewRow();
        row[0] = 1;
        row[1] = 1;

        dt.Rows.Add(row);

        row = dt.NewRow();
        row[0] = 2;
        row[1] = 2;

        dt.Rows.Add(row);

        row = dt.NewRow();
        row[0] = 3;
        row[1] = 3;

        dt.Rows.Add(row);
    }

    private void SetupDgv()
    {
        Dgv.Columns.Add("test1", "test1");
        Dgv.Columns[0].DataPropertyName = "test1";
        Dgv.Columns[0].ReadOnly = true;
        Dgv.Columns[0].Resizable = DataGridViewTriState.False;
        Dgv.Columns[0].SortMode = DataGridViewColumnSortMode.Programmatic;

        var col = new DataGridViewComboBoxColumn
        {
            Name = "test2",
            HeaderText = "test2",
            DataPropertyName = "test2",
            ReadOnly = false,
            Resizable = DataGridViewTriState.False,
            SortMode = DataGridViewColumnSortMode.Programmatic
        };

        col.Items.Add("");
        col.Items.Add("DELETE");
        col.Items.Add("1");
        col.Items.Add("2");
        col.Items.Add("3");

        Dgv.Columns.Add(col);
    }

    private void Dgv_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        var newColumn = Dgv.Columns[e.ColumnIndex];
        var oldColumn = Dgv.GetSortedColumnFromDataTable(dt);

        ListSortDirection direction;

        if (oldColumn != null)
        {
            if (oldColumn == newColumn && dt.GetDataTableSortOrder() == "ASC")
                direction = ListSortDirection.Descending;
            else
            {
                direction = ListSortDirection.Ascending;
                oldColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
            }
        }
        else
            direction = ListSortDirection.Ascending;

        Dgv.ClearSortGlyphInAllColumnsExcept(e.ColumnIndex);

        dt.DefaultView.Sort = direction == ListSortDirection.Ascending ?
            Dgv.Columns[e.ColumnIndex].Name + " ASC" : Dgv.Columns[e.ColumnIndex].Name + " DESC";

        newColumn.HeaderCell.SortGlyphDirection =
            direction == ListSortDirection.Ascending ? SortOrder.Ascending : SortOrder.Descending;
    }
}

public static class DGVExtensions
{
    public static DataGridViewColumn GetSortedColumnFromDataTable(this DataGridView dgv, DataTable dt)
    {
        var dtSort = dt.DefaultView.Sort;

        string colName;
        if (dtSort.IndexOf(" ") != -1)
            colName = dtSort.Substring(0, dtSort.IndexOf(" "));
        else
            colName = dtSort;

        return dgv.Columns[colName];
    }

    public static void ClearSortGlyphInAllColumnsExcept(this DataGridView dgv, int index)
    {
        foreach (DataGridViewColumn col in dgv.Columns)
        {
            if (col.Index != index)
                col.HeaderCell.SortGlyphDirection = SortOrder.None;
        }
    }
}

public static class DTExtensions
{
    public static string GetDataTableSortOrder(this DataTable dt)
    {
        if (dt.DefaultView.Sort.IndexOf(" ") == -1)
            return "ASC";
        else
        {
            var startIndex = dt.DefaultView.Sort.IndexOf(" ") + 1;

            return dt.DefaultView.Sort.Substring(startIndex, dt.DefaultView.Sort.Length - startIndex).ToUpper();
        }
    }
}
}

Dgv_ColumnHeaderMouseClick事件中添加以下代码行作为事件中代码的第一行...

Dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);