Hinzufügen von vielen Einträger in einer Listbox beschleunigen


Zwei kleine Tipps, die das Füllen einer Listbox oder Combobox beschleunigen, wenn man viele - und damit meine ich wirklich viele Einträge, so um die hundert oder so - aufeinmal hinzufügen will. Inwiefern eine Listbox oder Combobox mit so vielen Einträgen noch sinnvoll ist, sei mal dahingestellt. Ich gehe einfach davon aus, dass der Programmierer schon seine Gründe haben wird.

Als erstes kann man verhindern, dass das Kontroll nach jedem Hinzufügen neu gezeichnet wird. Dazu schickt man die Nachricht WM_SETREDRAW an das Fenster. Ist der WPARAM False, wird verhindert, dass das Fenster jedes mal neugezeichnet wird. (Das funktioniert übrigens mit jedem Fenster.) Hat man die Listbox gefüllt, ruft man die selbe Nachricht noch mal auf, aber diesmal mit dem WPARAM True und danach lässt man das Fenster mit InvalidateRect neuzeichnen.

Eine weitere, wohl nicht so bekanntere, Möglichkeit besteht darin, schon vorher den nötigen Speicher für die Einträge zu reservieren und zwar geschieht das mit der Nachricht LB_INITSTORAGE für Listboxen bzw. CB_INITSTORAGE für Comboboxen.

Eine kleine Gegenüberstellung meiner Versuche mit einer Listbox, die mit 1.000.000 Einträgen in einer Schleife gefüllt wird:

procedure TForm1.Button1Click(Sender: TObject);
const
  MAX = 1000000;
var
  i: Integer;
  Start: Integer;
begin
  Listbox1.Clear;
  if chkSetRedraw.Checked then
    SendMessage(Listbox1.Handle, WM_SETREDRAW, 0, 0);
  if chkLBInitStorage.Checked then
    SendMessage(Listbox1.Handle, LB_INITSTORAGE, MAX, MAX*sizeof(Integer));
  Start := GetTickCount;
  for i := 0 to MAX - 1 do
    Listbox1.Items.Add(IntToStr(i));
  if chkSetRedraw.Checked then
    SendMessage(Listbox1.Handle, WM_SETREDRAW, 1, 0);
  Invalidaterect(Listbox1.Handle, nil, True);
  ShowMessage(IntToStr(GetTickCount - Start));
end;

Die Messung mit GetTickCount ist nicht sehr genau, ist aber in diesem Fall ausreichend.


Redraw/InitStorageZeit [ms]Zeit [ms]Zeit [ms]
- WM_SETREDRAW
- LB_INITSTORAGE
156364154983151047
+ WM_SETREDRAW
- LB_INITSTORAGE
156425158438153381
- WM_SETREDRAW
+ LB_INITSTORAGE
846028232083640
+ WM_SETREDRAW
+ LB_INITSTORAGE
780837819381817

Wie man sieht, scheint das Alloziieren von Speicher die meiste Zeit in Anspruch zu nehmen, während das Verhindern des Neuzeichnens kaum nennens werte Vorteile bringt. Man muss allerdings dazu sagen, dass ich den standard Speichermanager von Delphi benutzt haben. Man könnte noch mal ausprobieren, ob nicht andere Speichermanager etwas performanter beim Alloziieren des Speichers sind.

2010-12-29T23:44:50 +0100, mail+homepage[at]michael-puff.de