除非按下 Enter,否则 DataGridViewComboBoxColumn 不会保存到数据源

问题描述

我有一个 DataGridView,它的 DataSource 属性绑定到一个 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 上也有一个自定义的程序化排序。我不会发布代码,因为我认为它不相关,但本质上它是通过 ColumnHeaderMouseClick 事件触发的,只需按您单击的任何列对底层 DataTable 进行排序,取消 DataGridView 的 DataSource 属性并重新绑定它。

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

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

如何在 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",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);