在自定义 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;
但这还没有解决。
接下来我做了什么:
在OnCellFormatting()
我用
if (m_bUpdateControl)
{
i_oEventArgs->FormattingApplied = true;
return;
}
在OnCellPainting()
我用
if (m_bUpdateControl)
{
i_oEventArgs->Handled = true;
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 就不会在同一列中出现具有不同类型内容的单元格,这会导致另一个错误消息和异常。
如果存在其他解决方案,我仍然想知道。
我知道下面的代码是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;
但这还没有解决。
接下来我做了什么:
在
OnCellFormatting()
我用if (m_bUpdateControl) { i_oEventArgs->FormattingApplied = true; return; }
在
OnCellPainting()
我用if (m_bUpdateControl) { i_oEventArgs->Handled = true; 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 就不会在同一列中出现具有不同类型内容的单元格,这会导致另一个错误消息和异常。
如果存在其他解决方案,我仍然想知道。