Страницы

Поиск по вопросам

вторник, 16 октября 2018 г.

Что делать, когда при обработке события WinForms контрола нужные данные еще не обновились?

Конечно, напрашивается ответ: "используй другое событие", но, к сожалению, я не нашёл подходящий 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; }

Комментариев нет:

Отправить комментарий