Конечно, напрашивается ответ: "используй другое событие", но, к сожалению, я не нашёл подходящий event.
Живой пример из живой программы: есть таблица настроек, у которой есть колонка типа checkbox. Требуется, чтобы при изменении состояния чекбокса, кнопка Save на форме Enabled = true;
Проблема заключается в том, что когда вы нажимаете мышкой на checkbox, событие DataGridView.CellValueChanged не вызывается, пока ячейка не потеряет фокус. Cell.Value ещё хранит прежнее значение и никаких событий обновления данных не возникает. Новое значение хранится в свойстве Cell.EditedFormattedValue, но оно появляется там только после выполнения всех событий, которые я тестировал.
Напросилось такое решение:
private void gridSettings_CellClick(object sender, DataGridViewCellEventArgs e)
{
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
Thread.Sleep(200);
})
.ContinueWith(result =>
{
if (gridSettings.Columns[e.ColumnIndex].Name ==
"ColumnIsMandatory")
{
var clickedCell = gridSettings.Rows[e.RowIndex].Cells[e.ColumnIndex];
object editedValue = clickedCell.EditedFormattedValue;
object oldValue = clickedCell.Value;
if (oldValue != null && editedValue != null && (bool) editedValue != (bool) oldValue)
{
clickedCell.Value = (bool) editedValue;
// Событие изменения данных вызвано принудительно
}
}
}, uiScheduler);
}
Но оно обладает недостатками в виде запуска Task и отфонарного Thread.Sleep(200); который не гарантирует, что данные уже обновились. Да и решение топорное, совсем не изящное.
Как бы вы решали такую проблему?
UPDATE:
Работающий пример:
public partial class Form1 : Form
{
private DataGridView gridSettings;
private Button buttonSave;
public Form1()
{
InitializeComponent();
gridSettings = new DataGridView()
{
Location = new Point(0,0),
Size = new Size(200, 200)
};
var column = new DataGridViewCheckBoxColumn()
{
Name = "ColumnIsMandatory"
};
gridSettings.Columns.Add(column);
gridSettings.Rows.Add(1);
buttonSave = new Button {Text = "Save", Location = new Point(210, 210), Enabled = false};
gridSettings.CellValueChanged += (sender, args) => buttonSave.Enabled = true;
gridSettings.CellClick += gridSettings_CellClick;
this.Controls.AddRange(new Control[]{gridSettings, buttonSave});
}
private void gridSettings_CellClick(object sender, DataGridViewCellEventArgs e)
{
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() =>
{
Thread.Sleep(200);
})
.ContinueWith(result =>
{
if (gridSettings.Columns[e.ColumnIndex].Name ==
"ColumnIsMandatory")
{
var clickedCell = gridSettings.Rows[e.RowIndex].Cells[e.ColumnIndex];
object editedValue = clickedCell.EditedFormattedValue;
object oldValue = clickedCell.Value;
if (oldValue != null && editedValue != null && (bool)editedValue != (bool)oldValue)
{
clickedCell.Value = (bool)editedValue;
// Событие изменения данных вызвано принудительно
}
else if (oldValue == null && editedValue != null)
{
clickedCell.Value = (bool)editedValue;
}
}
}, uiScheduler);
}
}
Ответ
Если я не ошибаюсь, вам способ прекрасно работает, даже если из обработчика CellClick выкинуть создание тасков. То есть достаточно оставить оператор if
Тем не менее, событие, которое вам нужно - CurrentCellDirtyStateChanged
gridSettings.CurrentCellDirtyStateChanged += GridSettings_CurrentCellDirtyStateChanged;
private void GridSettings_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
buttonSave.Enabled = true;
}
Комментариев нет:
Отправить комментарий