Performanceproblem bei TImageList

Performanceproblem bei der TImageList

Update: Dieses Problem wurde mit Delphi 10.1 Berlin gelöst.

Problembeschreibung:

Das Hinzufügen von Bildern zu einer Imageliste in einer VCL Anwendung dauert nach der Umstellung auf Delphi 10 Seattle plötzlich sehr lange.

Normalerweise geht das hinzufügen von Bildern sehr schnell – vorausgesetzt, man befolgt in etwa diesen Ablauf:

  ...
  iml: TImageList;
  ...
  iml.BeginUpdate;
  try
    for x in BigList do
      begin
        iml.Add(x);
      end;
  finally
    iml.EndUpdate;
  end;

Wenn dieser Vorgang nun plötzlich sehr lange dauert kann es folgenden Grund haben:

Elemente, die diese Imageliste verwenden, werden über Änderungen an der Liste informiert.

Damit bei vielen Änderungen diese Benachrichtigung nur und erst am Ende der Aktualisierungen versendet wird, wird ein BeginUpdate/EndUpdate verwendet.

Dieses BeginUpdate und EndUpdate funktioniert in Delphi 10 Seattle jedoch nicht wie gewünscht.

Erkenntnisse liefert hier ein Blick auf den Quellcode:

 

Die Vererbungshierarchie von TImageList:

Basis ist TBaseImageList in System.ImageList.

Für FMX dann in Unit FMX.ImgList: TCustomImageList >> TImageList

Für VCL entsrpechend in Unit VCL.ImgList: TCustomImageList >> TImageList

 

Das BeginUpdate und EndUpdate in der Basisklasse sieht wie folgt aus:

  procedure TBaseImageList.BeginUpdate;
  begin
    if FUpdateCount = 0 then
      Updating;
    Inc(FUpdateCount);
  end;
  
  procedure TBaseImageList.EndUpdate;
  begin
    if FUpdateCount >
      0 then begin Dec(FUpdateCount);
    if FUpdateCount = 0 then
      Updated;
  end;

Diese beiden Methoden machen exakt was sie sollen.

Unglücklicherweise werden beide Methoden jedoch von TCustomImageList für die VCL überschrieben (nicht vererbt!):

  procedure TCustomImageList.BeginUpdate;
  begin
    Inc(FUpdateCount);
  end;

  procedure TCustomImageList.EndUpdate;
  begin
    if FUpdateCount >
      0 then Dec(FUpdateCount);
    if FChanged then
      begin
        FChanged := False;
        Change;
      end;
  end;

Hier wird das Updating und Updated nicht mehr gesetzt – und der UpdateCount, der noch dazu lokal in der Klasse deklariert ist und das geerbte UpdateCount überschreibt – wird nicht verwendet.

Die Methode TBaseImageList.Change der Basisklasse wird immer alle eingetragenen Klassen über die geänderte Imageliste informieren.

 

Ein Vergleich mit älteren Delphi VCL Quellen zeigt, dass hier etwas falsch läuft und so nicht funktioniert.

 

Die Lösung für das Problem ist als Workaroud jedoch recht einfach: Es wird einfach die „gute“ Methode der Basisklasse gerufen.

Einziger Problempunkt: Es müsste bei kommenden Delphi-Updates kontrolliert werden, ob sich zusätzliche Änderungen in der Ableitung ergeben haben, welche aktuell natürlich nicht ausgeführt werden.

Es ist anzunehmen, dass dieser Bug in Zukunft behoben wird. Dann kann diese Stelle wieder auf das Orginal geändert werden.

 

Durchgeführte Änderung: Aufruf der Methoden der Basisklasse:

  ...
  iml: TImageList;
  ...
  TBaseImageList(iml).BeginUpdate;
  try
    for x in BigList do
      begin
        iml.Add(x);
      end;
  finally
    TBaseImageList(iml).EndUpdate;
  end;

Schreibe einen Kommentar