批量更改被设计用于通过消除过度更新 (可视更新、重设顺序、选择更新等) 来提升网格控件的性能。 主要目的是只更新视图一次 —— 在所有必需的更改已经作出之后。
防止过度的可视更新
如果执行一系列会导致视图更新的更改,可能要注意性能下降,特别是当这些操作很耗时的时候。 例如,通过 ColumnView.DeleteRow 方法删除视图中的几百行就可能需要一段时间,因为在每次删除之后视图都被更新。 但是,通过防止个别操作之间不必要的更新,可以提升这种操作的性能。 网格控件提供了多个方法来支持这种批量更改。
在对视图应用一系列更改之前,可以调用 BaseView.BeginUpdate 方法。 此方法锁定视图,并阻止后续的可视更新。 在对视图执行所有必需的操作之后,调用 BaseView.EndUpdate 方法。 此方法立即更新视图以反映所有最近作出的更改,也重新允许将来的更新。
如果需要同时对几个视图作出更改,则可以为每个视图使用 BaseView.BeginUpdate 和 BaseView.EndUpdate 方法。 但是,最省事的解决方案是使用 GridControl.BeginUpdate 方法来代替,此方法锁定网格控件中所有已打开的视图。 类似地,要立即解锁所有视图,则调用 GridControl.EndUpdate 方法。
BeginUpdate 和 EndUpdate 方法使用一个内部计数器来实现适当的功能。 此计数器的初值是 0。 每次调用 BeginUpdate 方法,此计数器都被递增 1。 每次调用 EndUpdate 方法,此计数器都被递减 1; 如果它的新值为 0,则视图被更新。 注意,每次调用 BeginUpdate,都必须与调用 EndUpdate 配对。 如果调用了 BeginUpdate,但是后面没有调用 EndUpdate,或者 EndUpdate 未被调用的原因是产生了异常,则视图将不再被刷新。 要确保 EndUpdate 始终被调用,即使是产生了异常,则使用 try...finally 语句。
这些要点适用于在本章节中描述的所有批量更改方法。
下面的示例展示了当删除符合特定条件的记录时,如何使用 BaseView.BeginUpdate 和 BaseView.EndUpdate 方法来防止产生多余的更新。C# | 复制代码 |
---|---|
using DevExpress.XtraGrid.Views.Grid; using DevExpress.XtraGrid.Columns; //... GridView currentView = gridView1; GridColumn compColumn = gridView1.Columns["moduleid"]; int compValue = 3; int rowCount = currentView.DataRowCount; int rowHandle; currentView.BeginUpdate(); try { for (rowHandle = rowCount - 1; rowHandle >= 0; rowHandle--) { if ((int)currentView.GetRowCellValue(rowHandle, compColumn) == compValue) currentView.DeleteRow(rowHandle); } } finally { currentView.EndUpdate(); } |
Visual Basic | 复制代码 |
---|---|
Imports DevExpress.XtraGrid.Views.Grid Imports DevExpress.XtraGrid.Columns '... Dim currentView As GridView = GridView1 Dim compColumn As GridColumn = GridView1.Columns("moduleid") Dim compValue As Integer = 3 Dim rowCount As Integer = currentView.DataRowCount Dim rowHandle As Integer currentView.BeginUpdate() Try For rowHandle = rowCount - 1 To 0 Step -1 If CType(currentView.GetRowCellValue(rowHandle, compColumn), Integer) = compValue Then currentView.DeleteRow(rowHandle) End If Next Finally currentView.EndUpdate() End Try |
防止过度的内部数据更新
BeginUpdate 和 EndUpdate 方法只阻止可视更新。 当在 BeginUpdate 和 EndUpdate 配对内修改属性或调用方法时,相应的操作会立即生效。 但是,仅当调用 EndUpdate 之后,结果才被显示。
可能执行某些操作来强制网格控件重新加载、重设顺序、重新分组数据。 这些操作中的每一个都会导致内部数据更新,当这些操作被连续执行时,则产生多次数据更新。 不能通过使用 BeginUpdate 和 EndUpdate 方法来避免过度的数据更新。 而是必须使用 BaseView.BeginDataUpdate 和 BaseView.EndDataUpdate 方法。 当连续执行下列任何操作时,这些方法能提升控件的性能:
- 按照列进行排序或分组;
- 当数据已被排序时,修改视图的记录;
- 在数据源级别添加、删除或修改记录。
如果连续执行这些操作的代码被封闭在 BeginDataUpdate 和 EndDataUpdate 方法中,则在调用 EndDataUpdate 方法之后,视图仅执行一次数据更新,以反映所有作出的更改。
使用 BeginDataUpdate 和 EndDataUpdate 方法与使用 BeginUpdate 和 EndUpdate 方法类似。 每次调用 BeginDataUpdate 方法都必须与调用 EndDataUpdate 方法相对应。 要确保这样执行 (即使是产生了异常),则使用 try...finally 块。
BeginDataUpdate 和 EndDataUpdate 方法会分别调用 BeginUpdate 和 EndUpdate 方法。 因此,当使用 BeginDataUpdate 和 EndDataUpdate 方法时,您不需要调用这些方法来阻止多余的可视更新。
注意 |
---|
在主/从模式中,如果当前已打开任何 detail,则不要调用 BeginDataUpdate 和 EndDataUpdate 方法,因为这样会导致某些绘制 artifacts。 |
ColumnView.BeginSort 和 ColumnView.EndSort 方法与 BeginDataUpdate 和 EndDataUpdate 方法等效。
示例
下面的示例在网格视图中按照两个列对数据排序。 代码被封闭在 ColumnView.BeginDataUpdate 和 BaseView.EndDataUpdate 方法调用内。 这样在调用 BaseView.EndDataUpdate 方法之后,仅对数据排序一次,从而能提升性能。
C# | 复制代码 |
---|---|
gridView1.BeginDataUpdate(); try { gridView1.ClearSorting(); gridView1.Columns["Trademark"].SortOrder = DevExpress.Data.ColumnSortOrder.Ascending; gridView1.Columns["Model"].SortOrder = DevExpress.Data.ColumnSortOrder.Ascending; } finally { gridView1.EndDataUpdate(); } |
Visual Basic | 复制代码 |
---|---|
gridView1.BeginDataUpdate() Try gridView1.ClearSorting() gridView1.Columns("Trademark").SortOrder = DevExpress.Data.ColumnSortOrder.Ascending gridView1.Columns("Model").SortOrder = DevExpress.Data.ColumnSortOrder.Ascending Finally gridView1.EndDataUpdate() End Try |
防止过度的选择更新
每当选择被更改时,ColumnView.SelectionChanged 事件都发生。 例如,可以为事件编写代码,使用选中记录的取值填充列表框。 每当把记录加入选择或从选择中移除记录时,此事件都发生,因此列表框被自动维护。
假设需要通过代码执行多个连续的选择操作 (例如,清除选择然后调用 ColumnView.SelectRow 或 ColumnView.SelectRange 方法)。 在这种情况下,取决于调用方法的次数,此事件会多次发生,并且多次更新视图。 要避免这种多余的更新,则使用 BaseView.BeginSelection 和 BaseView.EndSelection 方法。 这两个方法分别锁定和解锁选择更新。在调用 BaseView.BeginSelection 方法之后,当通过代码更改选择时,ColumnView.SelectionChanged 事件不发生,视图不被更新。 BaseView.EndSelection 允许选择更新,引发 ColumnView.SelectionChanged 事件,并更新视图以反映所有最近的选择更改。
因此,当通过代码选中多个行时,以及通过 ColumnView.SelectionChanged 事件执行耗时操作、或者操作需要屏幕更新时,可以使用这些方法防止屏幕闪烁。
使用 BaseView.BeginSelection 和 BaseView.EndSelection 方法与使用 BeginUpdate 和 EndUpdate 方法类似。 必须始终确保每次调用 BaseView.BeginSelection 都跟随一个相应的 BaseView.EndSelection 方法调用。 要达到此目的,我们建议您使用 try...finally 语句。 也支持嵌套的方法调用。BaseView.BeginSelection 方法只锁定与选择相关的更新,不锁定其他任何更新。 因此,如果需要阻止可视更新,则仍然需要使用 BaseView.BeginUpdate 方法。
下列代码演示了当选择符合特定条件的行时,如何防止选择更新:C# | 复制代码 |
---|---|
using DevExpress.XtraGrid.Views.Grid; using DevExpress.XtraGrid.Columns; //... GridView currentView = advBandedGridView1; GridColumn compColumn = currentView.Columns["Discontinued"]; currentView.OptionsSelection.MultiSelect = true; currentView.ExpandAllGroups(); bool compValue = true; currentView.BeginSelection(); try { currentView.ClearSelection(); int rowHandle; for (rowHandle = 0; rowHandle < currentView.DataRowCount; rowHandle ++) if ((bool)currentView.GetRowCellValue(rowHandle, compColumn) == compValue) currentView.SelectRow(rowHandle); } finally { currentView.EndSelection(); } |
Visual Basic | 复制代码 |
---|---|
Imports DevExpress.XtraGrid.Views.Grid Imports DevExpress.XtraGrid.Columns '... Dim currentView As GridView = advBandedGridView1 Dim compColumn As GridColumn = currentView.Columns("Discontinued") currentView.OptionsSelection.MultiSelect = True currentView.ExpandAllGroups() Dim compValue As Boolean = True currentView.BeginSelection() Try currentView.ClearSelection() Dim rowHandle As Integer For rowHandle = 0 To currentView.DataRowCount - 1 If CType(currentView.GetRowCellValue(rowHandle, compColumn), Boolean) = compValue Then currentView.SelectRow(rowHandle) End If Next Finally currentView.EndSelection() End Try |
在某些情况下,可以调用 BaseView.CancelSelection 方法来代替 BaseView.EndSelection 方法。 此方法允许选择更新,但是不引发 ColumnView.SelectionChanged 事件,因此不重绘视图。 例如,如果调用 BaseView.BeginSelection,但不更改选择,则可以使用 BaseView.CancelSelection,而不是 BaseView.EndSelection,来避免不必要的最终更新。
防止过度的集合更新
当操作存储在相应集合中的汇总项和样式条件时,网格控件提供了方法来防止过度更新。
由 GridSummaryItemCollection 类支持汇总项。 GridSummaryItemCollection.BeginUpdate 和 GridSummaryItemCollection.EndUpdate 方法允许执行批量更改。 使用这些方法与使用其他的 BeginUpdate 和 EndUpdate 方法类似。 只需要把更改汇总集合 (添加或删除个别汇总,修改汇总设置) 的代码封闭在 GridSummaryItemCollection.BeginUpdate 和 GridSummaryItemCollection.EndUpdate 方法内,这就能防止多余的汇总重新计算和视图更新。 汇总只被重新计算一次 —— 在 GridSummaryItemCollection.EndUpdate 方法被调用之后。
下列代码演示了当添加两个 总体汇总 时,如何防止不必要的汇总重新计算。C# | 复制代码 |
---|---|
using DevExpress.XtraGrid; using DevExpress.XtraGrid.Views.Grid; //... GridView currentView = advBandedGridView1; GridSummaryItemCollection coll = currentView.Columns[0].SummaryItem.Collection; coll.BeginUpdate(); try { GridSummaryItem sumItem = currentView.Columns["Price"].SummaryItem; sumItem.SummaryType = DevExpress.Data.SummaryItemType.Max; sumItem.FieldName = "Price"; sumItem.DisplayFormat = "Max: {0:c0}"; sumItem = currentView.Columns["Model"].SummaryItem; sumItem.SummaryType = DevExpress.Data.SummaryItemType.Count; sumItem.FieldName = "Model"; sumItem.DisplayFormat = "Records: {0}"; } finally { coll.EndUpdate(); } |
Visual Basic | 复制代码 |
---|---|
Imports DevExpress.XtraGrid Imports DevExpress.XtraGrid.Views.Grid '... Dim currentView As GridView = advBandedGridView1 Dim coll As GridSummaryItemCollection = currentView.Columns(0).SummaryItem.Collection coll.BeginUpdate() Try Dim sumItem As GridSummaryItem = currentView.Columns("Price").SummaryItem sumItem.SummaryType = DevExpress.Data.SummaryItemType.Max sumItem.FieldName = "Price" sumItem.DisplayFormat = "Max: {0:c0}" sumItem = currentView.Columns("Model").SummaryItem sumItem.SummaryType = DevExpress.Data.SummaryItemType.Count sumItem.FieldName = "Model" sumItem.DisplayFormat = "Records: {0}" Finally coll.EndUpdate() End Try |
GridSummaryItemCollection 类提供了 GridSummaryItemCollection.CancelUpdate 方法,在某些情况下可以用于代替 GridSummaryItemCollection.EndUpdate 方法。 此方法允许汇总集合更新,但不强制立即重新计算汇总,也不更新视图。
要获得更多关于汇总的信息,请参阅 总体汇总 和 分组汇总 文档。样式条件由 StyleFormatConditionCollection 类表示。 该类也提供了 FormatConditionCollectionBase.BeginUpdate 和 FormatConditionCollectionBase.EndUpdate 方法,用于封闭修改集合的代码,因此防止不必要的视图更新。 使用这些方法与使用其他批量更改方法类似。 请参阅 样式的格式化条件 文档,以获得更多信息。