一种改善控件性能的方法是,当需要对许多单元格进行变动时,可以先保持或挂起重画,直到所有的变动都完成时再进行。通过在对单元格修改和重算时保持重画(挂起布局),然后再恢复布局并重画所有单元格控件能够节省很多时间,并且仍然能为用户展现一个全新的界面。
布局是一个对象,它保存了计算后的值(像单元格的宽度和高度,合并,以及视图),用来绘制控件的当前状态。这些值可能包括到底有多少视图,每一个视图左上方的单元格是什么,每一行及每一列有多大以及每一个视图有多少单元格是当前可见的,等等。使用布局对象的目的是,通过保存绘制控件过程所使用已计算好的布局值,每次控件重画时重新使用它们而不是每次都进行重算,来优化控件的绘制。当跟踪到一个需要重新生成布局对象的改变发生时,绘制代码就会丢弃现有的布局对象,并计算出一个新的对象。布局对象不属于公共API,但是它们缓存了绘制表单所需的所有信息,像列宽,行高,单元格合并,单元格溢出以及总是可见的长方形单元格标注(Cell.NoteStyle = NoteStyle.StickyNote)。
为了改善性能,你可以暂停布局,这样可以暂停布局对象的更新,因此控件不会在重画的计算上花费时间,直到恢复布局。两个方法可以完成这些操作,FpSpread类中的SuspendLayout 和ResumeLayout方法。一定要在一个特定操作的范围内同时使用这两个方法,否则暂停布局后就会出现问题,不能恢复。
当对表单进行修改时,SuspendLayout 方法能够阻止控件重新计算列、行和单元格的布局。如果你在一个代码块中对表单做了大量的变动,使用SuspendLayout方法可以避免控件在每一次变动发生时对布局对象所做的多余的中间计算,在所有变动完成之后使用ResumeLayout(true)方法重新计算布局对象。这样可以极大的提高控件的性能;另外基于表单所需要的特性,还有许多其他方法可以提高控件的性能。
System.DateTime st = System.DateTime.Now;
FpSpread1.SuspendLayout();
FpSpread1.Sheets(0).ActiveSkin = FarPoint.Win.Spread.SheetSkin.Load("d:\\temp\\skin.skn");
FpSpread1.ResumeLayout();
MsgBox("Duration (ticks)="& System.DateTime.Now.Ticks - st.Ticks)
privatevoid PerformInitialSetup(){
// (0) used for property labels._propertyLabelOrdinal =0;
// Set up the spread
spread.SuspendLayout();
SheetView sheet = spread.ActiveSheet;
sheet.Models.ColumnHeaderData =new HeaderDataModel(spread, _orientation);
sheet.Models.Style =new SheetStyleModel(_orientation);
spread.NamedStyles = _config.Styles.NamedStyles;
// Insert initial data
sheet.Columns.Count =2;
foreach (Block block in _config.Blocks)
{
// Insert any leading blank rows
if (block.SpaceBefore >0) sheet.Rows.Add(sheet.Rows.Count, block.SpaceBefore);
if (block.DealProperties !=null)
{
int rowIndex = sheet.Rows.Count;
sheet.Rows.Add(rowIndex, block.DealProperties.Count);
foreach (DealProperty property in block.DealProperties) {
sheet.Cells[rowIndex, _propertyLabelOrdinal].Value = property;
sheet.Rows[rowIndex].StyleName = block.StyleName;
rowIndex++;
}
}
// Insert any trailing blank rows
if (block.SpaceAfter >0) sheet.Rows.Add(sheet.Rows.Count, block.SpaceAfter);
}
// Set initial styles
sheet.Columns[_propertyLabelOrdinal].StyleName ="dealPropertyLabels";
spread.ResumeLayout();
}
当布局被暂停后,如果没有在同一个代码块中有相应的恢复方法就会出现异常,控件会显示一个通知“布局处于暂停状态”。如果控件的状态变成这样,说明布局对象包含了非法的数据(大多数情况下为错误的数值),当控件使用非法的布局数据绘制时就会导致异常发生。在绘制控件过程中,如果发生未被捕捉的异常,通知就会出现,并且在异常发生时布局也会被暂停。
这些只会在使用SuspendLayout方法暂停布局时才会发生,然后对控件状态所做的改变也会生效,控件也会以某种方式使用非法的布局对象进行再次绘制。也可能存在这样的异常,它导致上述消息的显示,但却与暂停布局无关;例如,IRenderer.PaintCell方法调用过程中由自定义单元格类型对象抛出的异常。对控件状态所做的任何修改都会触发布局的重计算,但并非所有的改动都是这样。对行或列进行重新排列时,如排序和过滤,肯定需要重计算,但设置文本只有在某些情况下才需要重计算,例如,当你将AllowCellOverflow属性打开时。即使布局被暂停,Spread控件仍然可以使用之前的正确布局信息来绘制控件;但之后Spread控件可能会产生不可预知的情况,例如,当你想要滚动页面而控件却没有反应,也没有显示异常通知。
如果你不使用手写便笺,那么可以将AutoUpdateNotes属性设置为false,阻止控件对必须被设置为可见或隐藏或可移动的手写便笺进行检查。如果你使用了AllowCellOverflow属性,将其关闭可以提高布局计算的性能,因为每次对单元格中的数据进行修改时,这个特性需要许多对文本宽度的计算。如果你使用了公式,在更新之前将AutoCalculation属性设置为false,然后再将其设回true,并调用Recalculate方法,这样可减少对公式的多余中间计算。
你还可以做一些其他的操作提高性能,如减小控件的大小,或减少一次性显示的列数和行数(布局对象只计算表单的可见部分),或实现你自己的表单模型对象(就像实现了ISheetDataModel接口的数据模型对象),删除不需要的功能特性(例如,当你不需要数据绑定时,就不用实现数据绑定相关的接口)。
大概的程序结构如下:
SuspendLayout
在这里插入你的代码
ResumeLayout
这两个方法用来暂时忽略对布局所做的修改,这样可以进行许多修改操作,而不用在每次修改时进行多余的布局重计算。当布局计算被暂停时,跟踪控件修改的事件处理器不能对布局进行重计算,并且绘制代码不会访问新的布局。在使用嵌套循环修改每一个单元格时,像修改每一个单元格的值,这种情况肯定可以从先暂停布局,然后再恢复布局的方式中获益。
请记住,如果在对控件进行修改时不能从暂时停止布局中获得性能提升,就不要使用这些方法。一定要在同一个代码块中同时使用这两个方法; 否则,如果调用 SuspendLayout方法时没有在同一个代码块中相应的调用 ResumeLayout方法,控件可能无法正确的绘制。
在下面的示例代码中,我们在修改单元格的代码附近的代码块中同时使用了这两个方法。在修改单元格的颜色时,代码暂停了Spread控件的重画,并在之后恢复了重画。
fpSpread1.SuspendLayout();
fpSpread1.Sheets[0].Cells[0, 0, 499, 499].BackColor = Color.White;
fpSpread1.ResumeLayout(true);
Spread for Windows Forms 5.0 中文版下载地址
附:Spread for Windows Forms高级主题系列文章
Spread for Windows Forms高级主题(1)---底层模型
Spread for Windows Forms高级主题(2)---理解单元格类型
Spread for Windows Forms高级主题(3)---理解单元格的编辑模式
Spread for Windows Forms高级主题(4)---自定义用户交互
Spread for Windows Forms高级主题(5)---数据处理