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;
