Delphi XE7填充22个TComboBox耗时超4秒,求性能优化方案
优化Delphi XE7中TComboBox批量填充的性能问题
我们的应用基于Windows 10系统,采用Delphi XE7开发,窗体上包含26个TComboBox。从文件读取数据填充这些ComboBox列表时,耗时超过4秒,列表最大条目数约为800项。通过Debuginfo过程记录的各阶段耗时日志如下:
- 22:11:10.179,开始填充数字列表
- 22:11:10.180,开始清空数字列表
- 22:11:10.182,结束清空数字列表
- 22:11:10.183,开始解析数字列表
- 22:11:10.536,结束解析数字列表
- 22:11:10.536,开始复制数字列表
- 22:11:15.667,结束复制数字列表
- 22:11:15.668,结束填充数字列表
相关代码如下:
procedure TForm1.fill_digital_lists(whserch:string; dg:byte); var xdr:integer; filltxt:string; begin with work_cfg do begin debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', Start Fill Digital List'); if totdig>0 then begin debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', Start Clear Digital List'); brk1.Items.Clear; brk2.Items.Clear; brk3.Items.Clear; brk4.Items.Clear; evt1.Items.Clear; evt2.Items.Clear; evt3.Items.Clear; evt4.Items.Clear; evt5.Items.Clear; evt6.Items.Clear; evt7.Items.Clear; evt8.Items.Clear; evt9.Items.Clear; evt10.Items.Clear; evt11.Items.Clear; evt12.Items.Clear; evt13.Items.Clear; evt14.Items.Clear; evt15.Items.Clear; evt16.Items.Clear; evt17.Items.Clear; evt18.Items.Clear; evt19.Items.Clear; evt20.Items.Clear; evt21.Items.Clear; evt22.Items.Clear; debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', End Clear Digital List'); debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', Start Parse Digital List'); for xdr:=1 to totdig do begin if ansipos('SPARE',ansiuppercase(digitl_chans[xdr]))=0 then begin filltxt:=''; if (dg=1) and (length(whserch)>0) then begin if (ansipos(ansiuppercase(whserch),ansiuppercase(digitl_chans[xdr]))>0) then filltxt:=digitl_chans[xdr] end else filltxt:=digitl_chans[xdr]; if length(filltxt)>0 then brk1.Items.Add(filltxt); end; end; debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', End Parse Digital List'); debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', Start Copy Digital List'); brk2.Items:=brk1.Items; brk3.Items:=brk1.Items; brk4.Items:=brk1.Items; evt1.Items:=brk1.Items; evt2.Items:=brk1.Items; evt3.Items:=brk1.Items; evt4.Items:=brk1.Items; evt5.Items:=brk1.Items; evt6.Items:=brk1.Items; evt7.Items:=brk1.Items; evt8.Items:=brk1.Items; evt9.Items:=brk1.Items; evt10.Items:=brk1.Items; evt11.Items:=brk1.Items; evt12.Items:=brk1.Items; evt13.Items:=brk1.Items; evt14.Items:=brk1.Items; evt15.Items:=brk1.Items; evt16.Items:=brk1.Items; evt17.Items:=brk1.Items; evt18.Items:=brk1.Items; evt19.Items:=brk1.Items; evt20.Items:=brk1.Items; evt21.Items:=brk1.Items; evt22.Items:=brk1.Items; debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', End Copy Digital List'); end; debuginfo(wavepath,formatdatetime('hh:nn:ss.zzz',now)+', End Fill Digital List'); end; end;
优化建议
1. 解决核心耗时:优化列表复制逻辑
从日志可见,复制数字列表阶段耗时超过5秒,是性能瓶颈。当前直接赋值Items属性会触发每个ComboBox的多次重绘和冗余内存操作,可替换为:
- 使用
Assign方法批量赋值:brk2.Items.Assign(brk1.Items);,该方法内部做了批量处理,减少事件触发次数。 - 提前创建共享
TStringList,填充后统一分配给所有ComboBox(若后续需单独修改某ComboBox列表,需先复制实例避免互相影响):var SharedList: TStringList; begin SharedList := TStringList.Create; try // 将解析后的内容先存入SharedList for xdr:=1 to totdig do begin // ...原有解析逻辑,将符合条件的内容Add到SharedList end; // 统一分配给所有ComboBox brk1.Items.Assign(SharedList); brk2.Items.Assign(SharedList); // ...其他ComboBox依次处理 finally SharedList.Free; end; end;
2. 减少UI重绘开销
在清空、填充ComboBox时,临时禁用控件重绘,避免每次操作都触发界面刷新:
// 批量禁用重绘 brk1.Perform(WM_SETREDRAW, 0, 0); brk2.Perform(WM_SETREDRAW, 0, 0); // ...其他ComboBox依次执行 // 执行清空或填充操作 brk1.Items.Clear; brk2.Items.Clear; // ... // 恢复重绘并刷新界面 brk1.Perform(WM_SETREDRAW, 1, 0); brk2.Perform(WM_SETREDRAW, 1, 0); brk1.Invalidate; brk2.Invalidate; // ...其他ComboBox依次执行
3. 优化字符串解析效率
解析阶段的字符串处理可减少重复计算:
- 提前转换搜索关键词为大写,避免循环内重复调用
AnsiUpperCase:var UpperWhSerch: string; begin UpperWhSerch := AnsiUpperCase(whserch); brk1.Items.BeginUpdate; try for xdr:=1 to totdig do begin var UpperChan := AnsiUpperCase(digitl_chans[xdr]); if AnsiPos('SPARE', UpperChan) = 0 then begin if (dg=1) and (Length(whserch)>0) then begin if AnsiPos(UpperWhSerch, UpperChan) > 0 then brk1.Items.Add(digitl_chans[xdr]); end else brk1.Items.Add(digitl_chans[xdr]); end; end; finally brk1.Items.EndUpdate; end; end; - 用
BeginUpdate和EndUpdate包裹Add操作,避免每次添加条目都触发重绘。
4. 简化清空操作
将所有ComboBox存入数组,通过循环批量处理清空和重绘控制,减少重复代码:
var Combos: array of TComboBox; begin SetLength(Combos, 22); Combos[0] := brk1; Combos[1] := brk2; Combos[2] := brk3; // ...依次添加其他ComboBox到数组 // 禁用重绘 for var Combo in Combos do Combo.Perform(WM_SETREDRAW, 0, 0); // 批量清空 for var Combo in Combos do Combo.Items.Clear; // 恢复重绘 for var Combo in Combos do begin Combo.Perform(WM_SETREDRAW, 1, 0); Combo.Invalidate; end; end;
内容的提问来源于stack exchange,提问作者MRoth
相关产品推荐
相关产品推荐

