Discussion:
[Delphi 10.3] Was ist nur mit dem TListView los?
(zu alt für eine Antwort)
Michael Landenberger
2019-12-06 09:03:04 UTC
Permalink
Hallo,

seit einiger Zeit schlage ich mich damit herum, ein mit D3 bzw. D4 erstelltes
Programm auf D10 zu portieren. Besondere Probleme machen ListViews.

Im alten Programm gibt es u. a. eine ListView-Komponente. Wegen einiger
Spezialfunktionen wurde dafür nicht TListView aus der VCL benutzt, sondern
eine neu entwickelte, von TWinControl abgeleitete Komponente. Diese Komponente
fungiert als virtuelles ListView im Report-Stil und funktioniert gut.

In neueren Delphi-Versionen unterstützt die VCL-TListView-Komponente die
benötigten Funktionen (insbesondere spezielle CustomDraw-Routinen). Also habe
ich die alte Komponente rausgeschmissen und durch ein Delphi-TListView
ersetzt. Von der Funktionalität her hat das auch geklappt (wenn man mal von
diversen Kunstgriffen absieht, die nötig waren, um bei einem TListView mit
OwnerData = True und Checkboxes = True die Checkboxen tatsächlich sichtbar zu
machen). Allerdings ist die Performance unter aller Sau. Auch auf schnellen
Rechnern ist flüssiges Scrollen kaum möglich. Mit der alten Komponente war
dagegen (bei gleicher Funktionalität) auch auf älterer Hardware flüssiges
Scrollen kein Problem.

Weiteres Problem, das allerdings nur unter 10.2 auftrat und unter 10.3 behoben
zu sein scheint: bestimmte Font-Änderungen (z. B. normal/fett) innerhalb eines
CustomDraw-Handlers funktionierten nicht. In 10.3 scheint wenigstens das zu
klappen.

Frage: was hat Embarcadero da in der TListView-Komponente verbockt und gibt es
Workarounds? Oder muss ich wieder zur selbst erstellten ListView-Komponente
zurückkehren?

Gruß

Michael
Achim Kalwa
2019-12-07 10:43:20 UTC
Permalink
Hallo Michael,
[...] sondern
eine neu entwickelte, von TWinControl abgeleitete Komponente. Diese Komponente
fungiert als virtuelles ListView im Report-Stil und funktioniert gut.
[...] CustomDraw[...] Also habe
ich die alte Komponente rausgeschmissen und durch ein Delphi-TListView
ersetzt. Von der Funktionalität her hat das auch geklappt (wenn man mal von
diversen Kunstgriffen absieht, die nötig waren, um bei einem TListView mit
OwnerData = True und Checkboxes = True die Checkboxen tatsächlich sichtbar zu
machen).
Deine Lösung hierzu würde mich interessieren! Aber das nur am Rande.
Allerdings ist die Performance unter aller Sau. Auch auf schnellen
Rechnern ist flüssiges Scrollen kaum möglich. Mit der alten Komponente war
dagegen (bei gleicher Funktionalität) auch auf älterer Hardware flüssiges
Scrollen kein Problem.
Wieviele Items enthält Dein ListView denn?
Ohne den Code zu kennen: Evtl. wird zu oft/zuviel gezeichnet. Zeig mal
den Code. Oder setze einen Haltepunkt in Deiner Paint-Methode und prüfe,
wie oft das aufgerufen wird. Oder zähle dort eine Variable hoch.

Ansonsten nimm TVirtualTreeView:
https://github.com/Virtual-TreeView/Virtual-TreeView

Feature Overview und Screenshots:
http://www.soft-gems.net/index.php/controls/virtual-treeview

Am Anfang ist die Lernkurve etwas steil, aber sobald man das mit dem
NodeData-Record verinnerlicht hat, möchte man dieses Control nicht mehr
missen. Schau Dir die Beispiele an; ggf. wieder nachfragen.

HTH
Achim
Michael Landenberger
2019-12-10 12:39:31 UTC
Permalink
"Achim Kalwa" schrieb am 07.12.2019 um 11:43:20:

Danke an dich und Björn für deine Antworten.
Post by Achim Kalwa
[...] CustomDraw[...] Also habe
ich die alte Komponente rausgeschmissen und durch ein Delphi-TListView
ersetzt. Von der Funktionalität her hat das auch geklappt (wenn man mal von
diversen Kunstgriffen absieht, die nötig waren, um bei einem TListView mit
OwnerData = True und Checkboxes = True die Checkboxen tatsächlich sichtbar
zu machen).
Deine Lösung hierzu würde mich interessieren! Aber das nur am Rande.
Ich habe durch Überschreiben des CNNotify-Handlers die Verarbeitung von
LVM_GETDISPINFO-Botschaften modifiziert. Im Code sieht das ungefähr so aus:

procedure TFSTListView.CNNotify (var Msg : TMessage);

begin
case PNMHdr (Msg.lParam)^.code of
LVN_GETDISPINFO : if not OwnerDraw then
with PNMLVDispInfo (Msg.lParam)^.item do
if Checkboxes and (iSubItem = 0) and
(mask and LVIF_IMAGE = LVIF_IMAGE) then begin
mask := mask or LVIF_STATE;
stateMask := LVIS_STATEIMAGEMASK;
//Erläuterung von GetChecked siehe unten
if GetChecked (iItem) then
state := INDEXTOSTATEIMAGEMASK (2)
else
state := INDEXTOSTATEIMAGEMASK (1)
end;
(Bearbeitung anderer Botschaften...)
end;
inherited
end;

Zur Erläuterung: Die Funktion GetChecked () ist eine Methode, die den
Checked-Status der Checkbox aus den Daten ermittelt, mit der das Listview
gefüttert wurde. Bei diesen Daten handelt es sich um Objekte, die in einer
TList gespeichert sind und die eine Eigenschaft enthalten, die den Status der
Checkboxen bestimmt. Der Rest ist Windows API.
Post by Achim Kalwa
Wieviele Items enthält Dein ListView denn?
Verschieden. Von wenigen Dutzend bis mehreren Tausend. Die Performance ist
aber schon bei wenigen Items grottig.

Inzwischen habe ich aber die Bremse gefunden: es ist der OnData-Handler. Der
muss bei jedem Aufruf dem übergebenen Item die Subitems hinzufügen, und aus
unerfindlichen Gründen dauert das lange (obwohl TItem.Subitems eigentlich nur
eine Stringlist ist, bei der das Hinzufügen von Strings normalerweise recht
schnell geht). Abhilfe: ich habe das Listview jetzt auf OwnerDraw umgestellt.
Es kostete zwar etwas Mühe, den OnItemDraw-Handler so zu programmieren, dass
das Listview wieder wie vorher (also mit OwnerDraw = False) aussieht. Aber
jetzt klappt das und die Performance ist auch wieder so, wie sie sein sollte.
Die Routinen, mit denen die Texte der Items und Subitems aus den Daten
ausgelesen werden, sind übrigens dieselben wie die, die ich vorher zum
Befüllen von Item.Caption und den SubItems genutzt habe. Trotzdem geht jetzt
alles viel schneller. Diese Routinen waren also nicht schuld an der schlechten
Performance.

Der o. g. CNNotify-Handler ist jetzt allerdings unnötig, denn die Checkboxen
werden jetzt im OnItemDraw-Handler gezeichnet. Auch dafür nutze ich Windows
API-Routinen, z. B. ListView_GetImageList (<Listview-Handle>,LVSIL_STATE) und
ImageList_Draw ().

Gruß

Michael

Björn Schreiber
2019-12-10 10:43:55 UTC
Permalink
Post by Michael Landenberger
machen). Allerdings ist die Performance unter aller Sau. Auch auf schnellen
Rechnern ist flüssiges Scrollen kaum möglich. Mit der alten Komponente war
dagegen (bei gleicher Funktionalität) auch auf älterer Hardware flüssiges
Scrollen kein Problem.
Ähnliche Erfahrungen haben wir beim Umstellen unserer Anwendungen von
D5 auf D10.3 gemacht. Allerdings stört weniger das Scrollen im gefüllten
TListView, als das Füllen selber - es dauert in Kombination mit einer
Sortierung einfach sehr viel länger als unter D5 und ist nicht zumutbar.
Wo es einfach ging haben wir auf einen virtuellen TListView
umgestellt (OwnerData = TRUE), ansonsten flog die Komponente raus und
wurde durch einen TVirtualStringTree ersetzt.

Gruß,
Björn.
--
Björn Schreiber
DRIGUS Systeme GmbH
Loading...