在自定义 DataGridView 中避免行删除时的竞争条件

Avoid race condition at row deletion in custom DataGridView

我知道下面的代码是C++-CLI,但同样引用C#

当我想在 DataGridView 中 "combine" 多行时,我首先尝试 ,但缺点是几乎不可能实现其他单元格类型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)

这是由于从非UI 线程中一次删除多行造成的。实际上,一次删除它们会很好并且可以解决我的问题,但这是不可能的,因为在删除每一行之后都会调用事件。 :-(
当正在绘制的行被删除时会引发异常,这当然是行不通的。这种情况有时会发生,但并非总是如此,因此这绝对是删除和粘贴之间的竞争条件。

我的问题:由于无法访问导致竞争的代码,我该如何避免这种竞争?

到目前为止我做了什么:在开始删除多行之前,我设置了

m_bUpdateControl = true

在所有 4 个重写函数中,我有

if (m_bUpdateControl)
  return;

但这还没有解决。

接下来我做了什么:

但这仍然不足以安全地避免竞争条件。
我还能做什么?
这也会发生在普通的 DGV 上吗?

我唯一剩下的想法是在 UI 线程上调用一个行删除函数,因此只有一个线程在 DGV 上运行,竞争条件有望消失。

编辑:
我已经使用常规 DGV 进行了测试,并且在从非 UI 线程中删除行时总是得到 InvalidOperationException。当然这是有道理的,因为对 UI 对象的跨线程操作是不可能的。所以我想知道为什么它在我的应用程序中是可能的。
我创建了一个小测试项目,终于找到了原因:主应用程序实现了一个DLL。正如供应商在 phone 调用中告诉我的那样,此 DLL 的主要 class 的 Init 例程调用

Control.CheckForIllegalCrossThreadCalls = false;

可以实现后续的跨线程操作。所以如果这个检查没有被禁用,我将不得不在 UI 线程上进行行删除,这很可能避免了最初的问题。

组合

  • 在 UI 线程上调用行删除
  • 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 就不会在同一列中出现具有不同类型内容的单元格,这会导致另一个错误消息和异常。

如果存在其他解决方案,我仍然想知道。