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;