批量更新数据引起 DataGrid 的绘制错误及解决方法
2006-07-15 18:13
441 查看
背景:
在我们的应用中,经常使用 Windows.Forms.DataGrid 控件用于显示大批量的数据。
然而,当我们在批量修改 DataGrid 所绑定数据源时,极大可能引发一个 DataGrid 的绘制异常,异常信息如下所示:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.DataColumnPropertyDescriptor.GetValue(Object component)
at System.Windows.Forms.DataGridColumnStyle.GetColumnValueAtRow(CurrencyManager source, Int32 rowNum)
at System.Windows.Forms.DataGridTextBoxColumn.Paint(Graphics g, Rectangle bounds, CurrencyManager source, Int32 rowNum, Brush backBrush, Brush foreBrush, Boolean alignToRight)
at System.Windows.Forms.DataGridRelationshipRow.PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column, Brush backBr, Brush foreBrush, Boolean alignToRight)
at System.Windows.Forms.DataGridRow.PaintData(Graphics g, Rectangle bounds, Int32 firstVisibleColumn, Int32 columnCount, Boolean alignToRight)
at System.Windows.Forms.DataGridRelationshipRow.Paint(Graphics g, Rectangle bounds, Rectangle trueRowBounds, Int32 firstVisibleColumn, Int32 numVisibleColumns, Boolean alignToRight)
at System.Windows.Forms.DataGrid.PaintRows(Graphics g, Rectangle& boundingRect)
at System.Windows.Forms.DataGrid.PaintGrid(Graphics g, Rectangle gridBounds)
at System.Windows.Forms.DataGrid.OnPaint(PaintEventArgs pe)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
问题分析:
经过各种解决方案的测试,最后发现是因为在批量更新 DataGrid 所绑定的数据源时,每次更新都会导致 Grid 发生重绘;而当数据更新速度非常快或者其它原因,导致 UI 线程来不及绘制完毕 DataGrid 时,Grid 就会发生绘制错误。
请注意异常信息中加亮显示的部分。
解决方法:根据上面分析推测出来的原因,基本上就是尽量减少因为数据源的变更而导致 DataGrid 重新发生绘制,从而避免因绘制过程中出错。因此,使用下面的步骤,从而使问题得以解决。
从原来的数据源中Clone一个数据源出来,用来做 Save 操作的数据源,这样就使在Save 操作时,对数据源的更新与 DataGrid 所绑定的数据源分离开来,而不会导致 Grid 被刷新;
在Save 完毕后,断开 DataGrid 与数据源之间的绑定;
把 Clone 出来保存了 Save 的结果的数据源 Merge 回原来的数据源中,并更新相应的状态;
重新将 DataGrid 绑定到数据源,这将使 Grid 只发生一次绘制行为。
总结:
我们在大量刷新控件绑定的数据源时,可能通过临时断开与数据源之间的绑定,抑制控件发生频繁重绘。在更新数据源完成后,再恢复控件与数据之间的绑定,从而使控件仅需绘制一次即可显示数据。
由于 DataGrid 不提供与数据源断开的功能,例如 C1FlexGrid 可以使用 BeginInit() / EndInit() 方法对来提供暂时与数据源断开绑定/恢复绑定的功能,因此,需要手工处理其与数据源的断开/恢复功能,那就是移除 DataSource 属性的置,在数据源更新完毕后,再重新设置DataSource的值。
事实上,Control基类提供了一个 public virtual 的方法对 BeginInit/EndInit,来提供这方面的支持,仅管 MSDN 文档中并没有如此说明,但在 DataGrid 中完全可以重写这两个方法提供这个功能。但遗憾的是,微软并没有这样做,这又增加了一个 DataGrid 被人诟病的理由,因此,在 .NET 2.0 中,微软新提供了一个替代控件 DataGridView,不过,本人还未仔细地去研究这个控件所提供的功能与改变。
在我们的应用中,经常使用 Windows.Forms.DataGrid 控件用于显示大批量的数据。
然而,当我们在批量修改 DataGrid 所绑定数据源时,极大可能引发一个 DataGrid 的绘制异常,异常信息如下所示:
System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.DataColumnPropertyDescriptor.GetValue(Object component)
at System.Windows.Forms.DataGridColumnStyle.GetColumnValueAtRow(CurrencyManager source, Int32 rowNum)
at System.Windows.Forms.DataGridTextBoxColumn.Paint(Graphics g, Rectangle bounds, CurrencyManager source, Int32 rowNum, Brush backBrush, Brush foreBrush, Boolean alignToRight)
at System.Windows.Forms.DataGridRelationshipRow.PaintCellContents(Graphics g, Rectangle cellBounds, DataGridColumnStyle column, Brush backBr, Brush foreBrush, Boolean alignToRight)
at System.Windows.Forms.DataGridRow.PaintData(Graphics g, Rectangle bounds, Int32 firstVisibleColumn, Int32 columnCount, Boolean alignToRight)
at System.Windows.Forms.DataGridRelationshipRow.Paint(Graphics g, Rectangle bounds, Rectangle trueRowBounds, Int32 firstVisibleColumn, Int32 numVisibleColumns, Boolean alignToRight)
at System.Windows.Forms.DataGrid.PaintRows(Graphics g, Rectangle& boundingRect)
at System.Windows.Forms.DataGrid.PaintGrid(Graphics g, Rectangle gridBounds)
at System.Windows.Forms.DataGrid.OnPaint(PaintEventArgs pe)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
问题分析:
经过各种解决方案的测试,最后发现是因为在批量更新 DataGrid 所绑定的数据源时,每次更新都会导致 Grid 发生重绘;而当数据更新速度非常快或者其它原因,导致 UI 线程来不及绘制完毕 DataGrid 时,Grid 就会发生绘制错误。
请注意异常信息中加亮显示的部分。
解决方法:根据上面分析推测出来的原因,基本上就是尽量减少因为数据源的变更而导致 DataGrid 重新发生绘制,从而避免因绘制过程中出错。因此,使用下面的步骤,从而使问题得以解决。
从原来的数据源中Clone一个数据源出来,用来做 Save 操作的数据源,这样就使在Save 操作时,对数据源的更新与 DataGrid 所绑定的数据源分离开来,而不会导致 Grid 被刷新;
在Save 完毕后,断开 DataGrid 与数据源之间的绑定;
把 Clone 出来保存了 Save 的结果的数据源 Merge 回原来的数据源中,并更新相应的状态;
重新将 DataGrid 绑定到数据源,这将使 Grid 只发生一次绘制行为。
总结:
我们在大量刷新控件绑定的数据源时,可能通过临时断开与数据源之间的绑定,抑制控件发生频繁重绘。在更新数据源完成后,再恢复控件与数据之间的绑定,从而使控件仅需绘制一次即可显示数据。
由于 DataGrid 不提供与数据源断开的功能,例如 C1FlexGrid 可以使用 BeginInit() / EndInit() 方法对来提供暂时与数据源断开绑定/恢复绑定的功能,因此,需要手工处理其与数据源的断开/恢复功能,那就是移除 DataSource 属性的置,在数据源更新完毕后,再重新设置DataSource的值。
事实上,Control基类提供了一个 public virtual 的方法对 BeginInit/EndInit,来提供这方面的支持,仅管 MSDN 文档中并没有如此说明,但在 DataGrid 中完全可以重写这两个方法提供这个功能。但遗憾的是,微软并没有这样做,这又增加了一个 DataGrid 被人诟病的理由,因此,在 .NET 2.0 中,微软新提供了一个替代控件 DataGridView,不过,本人还未仔细地去研究这个控件所提供的功能与改变。
相关文章推荐
- 批量更新数据引起 DataGrid 的绘制错误及解决方法
- 关于ADO直连数据库报无法更新错误的解决方法
- silverlight DataGrid 自定义控件 随鼠标滚动,数据发生更改的错误解决方法
- android studio编程时出现的错误:ListView重新载入后,更新数据或者再次进入该ListView加载数据,数据不显示 解决方法
- linq to sql统一更新方法,直接返回更新的对象(解决更新后再刷新数据错误显示问题)
- mvc数据表机构修改引起运行错误的解决方法
- var dataObj=eval("("+data+")");//转换为json对象(解决在ajax返回json格式数据的时候明明正确的获取了返回值但是却就是进不去success方法的问题。格式错误)
- 使用AFNetworking 2.0 请求数据时出现错误 Request failed: unacceptable content-type: XXXX 解决方法
- MySQL 数据批量恢复时 [Err] 2006 - MySQL server has gone away 错误的解决
- android studio 更新 Gradle错误解决方法
- 安卓探究ListView+Adapter数据在工作线程中更新引发的crash以及解决方法(二)
- Hadoop本地库与系统版本不一致引起的错误解决方法
- 大数据学习系列之八----- Hadoop、Spark、HBase、Hive搭建环境遇到的错误以及解决方法
- sql 更新date字段报“格式代码出现两次”错误解决方法.
- 数据错误(循环冗余检查) 各种解决方法
- SpringMVC中出现" 400 Bad Request "错误(用@ResponseBody处理ajax传过来的json数据转成bean)的解决方法
- 解决在MySQL使用PetaPoco T4生成数据的实体时得到当前MySQL数据库下所有表的错误方法
- 关于mysql版本升级后,原有的数据无法访问的解决方法,mysql错误代码:1558
- thinkphp读写分离坑爹!mysql进行update更新数据时报错[HY000]:General error,原因以及解决方法
- mysql数据导入/出产生Errcode: 13 - Permission denied错误解决方法