在Delphi中有效地填充组合框

时间:2017-09-12 15:03:25

标签: delphi combobox delphi-xe3

需要在TComboBox中添加许多项目(超过10k)(我知道TComboBox不应该包含很多项目,但不能由我来改变它)而不添加重复项。 所以我需要在添加之前搜索完整列表。我想避免使用TComboBox.items.indexof,因为我需要二进制搜索但是TStrings中没有二进制查找。

所以我创建了一个临时的Tstringlist,设置为true并使用了find。但现在将临时Tstringlist分配回TComboBox.Items

(myCB.Items.AddStrings(myList)) 

复制整个列表真的很慢。有没有办法移动列表而不是复制它?或者以其他方式高效填充我的TComboBox?

3 个答案:

答案 0 :(得分:6)

无法将列表“移动”到组合框中,因为组合框的存储属于内部Windows控件实现。它不知道直接使用你的Delphi TStringList对象的任何方法。它提供的所有功能都是将一个项目添加到列表中的命令,然后TComboBox用于将字符串列表中的每个项目逐个复制到系统控件中。避免将数千个项目复制到组合框中的唯一方法是完全避免这个问题,例如使用不同类型的控件或减少需要添加的项目数。

列表视图具有“虚拟”模式,您只需告诉它它应该有多少项目,然后当它需要知道有关可见内容的详细信息时,它会回调您的程序。屏幕。不可见的项目不会占用列表视图实现中的任何空间,因此您可以避免复制。但是,system combo boxes don't have a "virtual" mode。您可能能够找到一些提供该功能的第三方控件。

减少您需要放入组合框中的项目数量是您的下一个最佳选择,但只有您和您的同事具备必要的领域知识才能找到最佳方法。

答案 1 :(得分:3)

尽管10k项目在TComboBox中保持疯狂,但这里的一个有效策略是将缓存保存在单独的对象中。例如,声明:

    { use a TDictionary just for storing a hashmap }        
    FComboStringsDict : TDictionary<string, integer>;

其中

procedure TForm1.FormCreate(Sender: TObject);
var
  i : integer;
  spw : TStopwatch;
begin
  FComboStringsDict := TDictionary<string, integer>.Create;
  spw := TStopwatch.StartNew;
  { add 10k random items }
  for i := 0 to 10000 do begin
    AddComboStringIfNotDuplicate(IntToStr(Floor(20000*Random)));
  end;
  spw.Stop;
  ListBox1.Items.Add(IntToStr(spw.ElapsedMilliseconds));
end;

function TForm1.AddComboStringIfNotDuplicate(AEntry: string) : boolean;
begin
  result := false;
  if not FComboStringsDict.ContainsKey(AEntry) then begin
    FComboStringsDict.Add(AEntry, 0);
    ComboBox1.Items.Add(AEntry);
    result := true;
  end;
end;

最初添加10k项目大约需要0.5s。

{ test adding new items }
procedure TForm1.Button1Click(Sender: TObject);
var
  spw : TStopwatch;
begin
  spw := TStopwatch.StartNew;
  if not AddComboString(IntToStr(Floor(20000*Random))) then
    ListBox1.Items.Add('Did not add duplicate');
  spw.Stop;
  ListBox1.Items.Add(IntToStr(spw.ElapsedMilliseconds));
end;

但是添加每个后续项目非常快<1ms。这是一个笨拙的实现,但您可以轻松地将此行为包装到自定义类中。我们的想法是让您的数据模型尽可能与可视组件分开 - 在添加或删除项目时保持它们同步,但在查找速度快的字典上进行大量搜索。删除项目仍将依赖.IndexOf

答案 2 :(得分:3)

正如Rudy Velthuis已在评论中提及并假设您使用的是SendMessage(myCB, CB_INITSTORAGE, myList.Count, 20 * myList.Count*sizeof(Integer)); CB_INITSTORAGE消息可能是一个选项:

20

其中CB_INITSTORAGE是您的平均字符串长度。

结果(在i5-7200U和20K项目上,随机长度在1到50个字符之间):

  • 没有CB_INITSTORAGE:~265ms
  • {{1}}:~215ms

因此,虽然您可以通过预先分配内存来加快速度,但更大的问题似乎是糟糕的用户体验。用户如何在具有如此多项目的组合框中找到正确的元素?