У меня есть 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);
По сути, это заполняет каждый ComboBox в этом столбце всеми идентификаторами из другого DataTable и добавляет пустое значение и значение DELETE сверху.
У меня также есть пользовательская программная сортировка в этом DataGridView. Я не буду публиковать код, так как не думаю, что он уместен, но, по сути, он запускается через событие ColumnHeaderMouseClick и просто сортирует базовый DataTable по любому столбцу, на который вы нажали, аннулирует свойство DataSource DataGridView и повторно привязывает его.
Все это отлично работает, ЗА ИСКЛЮЧЕНИЕМ этого: скажем, одно из раскрывающихся списков в DataGridViewComboBoxColumn пусто, и я обновляю значение на что-то другое вручную. Если я сортирую DataGridView сразу после выбора указанного значения, значение не обновляет базовую таблицу данных и теряется. Если я делаю то же самое, НО нажимаю клавишу ввода после выбора значения, оно обновляется правильно.
В моем приложении есть функция, которая экспортирует указанную таблицу данных в электронную таблицу 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",
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();
}
}
}
}
EndEdit
сработает и обновит базовый источник данных еще до того, как будет вызван код сортировки. Обновляется ли базовый источник данных, если для сортировки задано значение, отличное от программного? Если это так, то может показаться, что проблема связана с сортировкой. Я хочу сказать… я уверен, что ячейка обновит базовый источник данных, как только фокус покинет ячейку. - person JohnG   schedule 07.05.2021