Избегайте состояния гонки при удалении строки в пользовательском DataGridView

Я знаю, что код ниже C++-CLI, но он относится к C# таким же образом

Когда я хотел "объединить" несколько строк в DataGridViewЯ сначала попробовал создать кастомDataGridViewRow, но это имело тот недостаток, что было практически невозможно реализовать другие типы ячеек, чем DataGridViewTextBoxCell,

Моим следующим решением было создание кастома DataGridView, который работает очень хорошо сейчас.
Переопределяет функции

OnCellFormatting ()
OnCellPainting ()
OnRowsRemoved ()
OnSelectionChanged ()

Проблема в том, что часто во время отладки, а иногда и во время нормального выполнения программы, я получаю исключение из разных мест внутреннего кода.net, который вызывается для отрисовки DataGridView или его частей. Пример:

Stack Trace:  
 bei System.Windows.Forms.DataGridViewTextBoxCell.PaintPrivate(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, Int32 rowIndex, DataGridViewElementStates cellState, Object formattedValue, String errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts, Boolean computeContentBounds, Boolean computeErrorIconBounds, Boolean paint)
 bei System.Windows.Forms.DataGridViewTextBoxCell.Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, Int32 rowIndex, DataGridViewElementStates cellState, Object value, Object formattedValue, String errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
 bei System.Windows.Forms.DataGridViewCell.PaintWork(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, Int32 rowIndex, DataGridViewElementStates cellState, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
 bei System.Windows.Forms.DataGridViewRow.PaintCells(Graphics graphics, Rectangle clipBounds, Rectangle rowBounds, Int32 rowIndex, DataGridViewElementStates rowState, Boolean isFirstDisplayedRow, Boolean isLastVisibleRow, DataGridViewPaintParts paintParts)
 bei System.Windows.Forms.DataGridViewRow.Paint(Graphics graphics, Rectangle clipBounds, Rectangle rowBounds, Int32 rowIndex, DataGridViewElementStates rowState, Boolean isFirstDisplayedRow, Boolean isLastVisibleRow)
 bei System.Windows.Forms.DataGridView.PaintRows(Graphics g, Rectangle boundingRect, Rectangle clipRect, Boolean singleHorizontalBorderAdded)
 bei System.Windows.Forms.DataGridView.PaintGrid(Graphics g, Rectangle gridBounds, Rectangle clipRect, Boolean singleVerticalBorderAdded, Boolean singleHorizontalBorderAdded)
 bei System.Windows.Forms.DataGridView.OnPaint(PaintEventArgs e)
 bei System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer)
 bei System.Windows.Forms.Control.WmPaint(Message& m)
 bei System.Windows.Forms.Control.WndProc(Message& m)
 bei System.Windows.Forms.DataGridView.WndProc(Message& m)
 bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
 bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
 bei System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

Это вызвано удалением нескольких строк одновременно из потока, не являющегося пользовательским интерфейсом. На самом деле, удаление их сразу было бы хорошо и решило бы мою проблему, но это невозможно, потому что события вызываются после удаления каждой отдельной строки.:-(
Исключение возникает, когда строка удаляется во время рисования, что, конечно, не может работать. Это случается иногда и не всегда, так что это определенно условие гонки между удалением и просмотром.

Мой вопрос: как я могу избежать этого состояния гонки, поскольку у меня нет доступа к коду, который его вызывает?

Что я сделал до этого: перед началом удаления нескольких строк я установил

m_bUpdateControl = true

Во всех 4 переопределенных функциях у меня есть

if (m_bUpdateControl)
  return;

но это еще не решило это.

Что я сделал дальше:

  • в OnCellFormatting() я использую

    if (m_bUpdateControl) { i_oEventArgs->FormattingApplied = true; return; }

  • в OnCellPainting() я использую

    if (m_bUpdateControl) { i_oEventArgs->Handled = true; return; }

Но этого все же недостаточно, чтобы безопасно избежать состояния гонки.
Что еще я мог сделать?
Произойдет ли это и на обычном DGV?

Единственная оставшаяся идея - вызвать функцию удаления строк в потоке пользовательского интерфейса, поэтому на DGV будет работать только один поток, и, как мы надеемся, условие гонки исчезнет.

Редактировать:
Я тестировал с обычным DGV и всегда получал InvalidOperationException при удалении строк из потока, не являющегося пользовательским интерфейсом. Конечно, это имеет смысл, поскольку операции с несколькими потоками над объектами пользовательского интерфейса невозможны. Поэтому я удивился, почему это было возможно в моем приложении.
Я создал небольшой тестовый проект и наконец нашел причину: основное приложение реализует DLL. Как продавец сказал мне в телефонном звонке, процедура Init основного класса этой DLL-звонков

Control.CheckForIllegalCrossThreadCalls = false;

который включает последующие операции с несколькими потоками. Поэтому, если бы эта проверка не была отключена, я бы все равно был вынужден удалить строку в потоке пользовательского интерфейса, что, скорее всего, позволит избежать первоначальной проблемы.

1 ответ

Решение

Сочетание

  • вызов удаления строки в потоке пользовательского интерфейса
  • OnCellPainting() с
    if (m_bUpdateControl) { i_oEventArgs->Handled = true; return; }

похоже на работу.

В

void OnCellFormatting (...)
{
  DataGridView::OnCellFormatting (i_oEventArgs);

  if (IsConsecutiveCell (i_oEventArgs->RowIndex, i_oEventArgs->ColumnIndex))
  {
    i_oEventArgs->Value = String::Empty;
    i_oEventArgs->FormattingApplied = true;
  }
}

Мне пришлось удалить всю защиту, чтобы DGV не заканчивал ячейками с разными типами контента в одном столбце, что вызывало другое сообщение об ошибке и исключение.

Я все еще хотел бы знать другие решения, если они существуют.

Другие вопросы по тегам