Страницы

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

вторник, 10 декабря 2019 г.

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

#c_sharp #winforms #datagridview #события


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

    


Ответы

Ответ 1



Если я не ошибаюсь, вам способ прекрасно работает, даже если из обработчика CellClick выкинуть создание тасков. То есть достаточно оставить оператор if. Тем не менее, событие, которое вам нужно - CurrentCellDirtyStateChanged. gridSettings.CurrentCellDirtyStateChanged += GridSettings_CurrentCellDirtyStateChanged; private void GridSettings_CurrentCellDirtyStateChanged(object sender, EventArgs e) { buttonSave.Enabled = true; }

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

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