![]() |
VirtualTreeView und icons
Liste der Anhänge anzeigen (Anzahl: 1)
Hi.
ist es möglich in der virtual-treeview icons zu zeichnen ohne die "TImageList" zu verwenden? ich habe eine große DB und jeder eintrag in der DB hat seinen eigenen icon. die der benutzer ändern/löschen oder neue hinzufügen kann; zurzeit lade ich eine externe Datei mit ~300 Icons in die "TImageList" und in der DB wird nur der index zum icon Gespeichert. was sehr limitiertend ist. thx. |
AW: VirtualTreeView und icons
Kenne mich mit VTV nicht wirklich aus, aber du kannst doch zur Laufzeit immer neue Images zu einer ImageList hinzufügen und dann ist es egal wann und von wo das eigentliche Icon her kommt...
|
AW: VirtualTreeView und icons
du hast recht, das problem ist allerdings das die ImageList kann nur eine limitierte anzahl von icons halten
wenn ich mich korrekt erinnere sind das ~500 Icons. ps. ich habe auch nicht so viel erfahrung mit der VTV! so wenn jemand eine code schnipsel hat, ich wäre euch dankbar. |
AW: VirtualTreeView und icons
Du kannst in den
Delphi-Quellcode:
und
OnAfterCellPaint
Delphi-Quellcode:
selbst Inhalte über den Parameter
OnBeforeCellPaint
Delphi-Quellcode:
in die Zelle(n) zeichnen.
TargetCanvas
Statt einer
Delphi-Quellcode:
kannst du zB eine
TImageList
Delphi-Quellcode:
nehmen, da die Icons als
TList
Delphi-Quellcode:
ablegen und dann über einen der genannten Events selbst zeichnen, je nachdem, "wann" du das Icon haben willst.
TBitmaps
|
AW: VirtualTreeView und icons
wie schon gesagt ich habe auch nicht so viel erfahrung mit der VTV.
und korrigiere mich wenn ich falsch liege aber ist es nicht sinnvoll/möglich die icons direkt in den Node Record abzuspeichern, genau wie den text. ich denke dann braucht man auch keine listen für die icons.
Delphi-Quellcode:
PRNodeData = ^TRNodeData;
TRNodeData = record ID : Int64; // pointer Title : Widestring; Value : Widestring; Hiden : Boolean; Image : TBitmap; // icon end; ich denke mein problem ist mehr das ich nicht genau weiß wie ich das zeichnen soll. ich meine den text nach rechts verschieben und vor dem Text die box für das icon zeichnen soll. ich verstehe die theorie dahinter. nur die umsetzung ins praktische, da hakt es noch. ich glaube ich brauche ein Red Bull :wink: |
AW: VirtualTreeView und icons
Liste der Anhänge anzeigen (Anzahl: 1)
Hab ich zwar noch nie versucht, kann mir aber gut vorstellen, daß dann das Handling des VirtualTreeViews um etliches langsamer wird, weil ja bei jedem Zugriff auf NodeData zusätzlich das Bitmap kopiert werden muß. Mit TImageList müssen die Bitmaps dagegen nur einmal kopiert werden, nämlich zwischen BeginUpdate und EndUpdate.
Auch hatte ich noch nie einen Fall, der es nötig machte, mehr als 500 Bitmaps bzw. Icons in einem VTV einzusetzen. |
AW: VirtualTreeView und icons
OK! ich habe es geschaft (Nach dem Red Bull :wink:) so ich hatte recht mit der idee die icon direkt in den NodeRecord zu schreiben!
so jetzt ist der limit nur der arbeitsschpeicher. so this is my solution! Have fun with it.
Delphi-Quellcode:
ganz vergessen zu sagen, ich benutze eine leere TImageList um den text nach rechts zu bewegen
type
PRNodeData = ^TRNodeData; TRNodeData = record ID : Int64; // pointer Title : Widestring; Value : Widestring; Hiden : Boolean; Image : TBitmap; // icon end; procedure TMain_form.RecordTreeBeforeCellPaint(Sender: TBaseVirtualTree; TargetCanvas: TCanvas; Node: PVirtualNode; Column: TColumnIndex; CellPaintMode: TVTCellPaintMode; CellRect: TRect; var ContentRect: TRect); var Data : PNodeData; R : TRect; begin if Assigned(Node)then begin Data:= FolderTree.GetNodeData(Node); if Data^.Colored then begin case Column of 0 : begin TargetCanvas.Brush.Color := $00FFFAF4; TargetCanvas.FillRect(CellRect); // Draw Icon Test R.Left:= ContentRect.Left-16; R.Top:= ContentRect.Top -1; R.Right:= ContentRect.Bottom + R.Left; R.Bottom:= ContentRect.Bottom -1; //TargetCanvas.Brush.Color:= clred; TargetCanvas.FillRect(R); TargetCanvas.Draw(R.Left, 0, Data^.Image); // Icon end; 1 : begin (* Draw Gradient *) GradientFillHorizontal(TargetCanvas, CellRect, $00FFFAF4, clWhite, 100) end; 2 : begin // comment todo end; end; end; end; end; in meinem test lade ich die icons direkt von der festplatte später wird das direkt von der DB/Stream geladen!
Delphi-Quellcode:
nochmal danke an alle.
Data:= RecordTree.GetNodeData(Parent);
... Data^.Image:= TBitmap.Create; Data^.Image.LoadFromFile('c:\...\...\folder_open.bmp'); Data^.Image.Transparent := True; Data^.Image.TransparentMode := tmAuto; |
AW: VirtualTreeView und icons
@Perlsau
normalerweise würde ich dir zustimmen. in meinem fall ist es ein wenig anders, bei meinem Programm erstellt der benutzer die DB/File und die felder Dynamisch. so was ist wenn der benutzer 20000 einträge hat? und in jedem eintrag einen anders icon haben will. wenn ich die Icons vorab in die TImageList Lade und nur den index in die DB speicher, kann es später zum problem werden. sagen wir mal ich muss ein icon löschen/ändern (aus rechtlichen grunden,...) :wink: so beim nächsten programm update stimmen die icons nicht mit dem eintrag überein da der index zu den icon sich geändert hat. oder man macht die DB/Datei komplett mit einem anderen Programm auf. ein anderer fall, was ist wenn du den folder mit deinen icons in die Virtual TreeView einlesen wilst zum beispiel ich habe 1,148,532 icons auf meinem Computer das sind ~10 GB ich glaube da versagen die meisten listen. Datenbankgröße 32 TB (auch durch Dateisystem und Betriebssystem begrenzt) und ich glaube der speed unterschied ist nicht so groß. thx. |
AW: VirtualTreeView und icons
Daß in deinem Fall die Kapazität der TImageList nicht ausreicht, ist nachvollziehbar. Dennoch würde ich im NodeDataRecord nicht das eigentliche Bitmap speichern, sondern eine Id auf eine Liste bzw. auf die entsprechenden der DB-Bildtabelle. Ob du jetzt im Node selbst das Icon austauschst oder in der Liste macht im Grunde keinen Unterschied – abgesehen davon, daß nicht bei jedem Zugriff auf den Data-Record das Bitmap aus dem Speicher gekramt werden muß, denn das wird ja nur einmal beim Aufbau des Baums benötigt.
Das mit der Geschwindigkeit hab ich jetzt nicht mit einem TreeView ausprobiert, aber mit Datenbanken. Wenn ich z.B. eine Tabelle, die zwei Blob-Felder (ein Bitmap und ein RTF-Text) beinhaltet, sortiere, ist das ohne diese beiden Blob-Felder entsprechend schneller: Je mehr Datensätze in der Tabelle, desto größer der Geschwindigkeitsvorteil. Die benötigten Felder lasse ich dann aus einer zweiten Tabelle nach Bedarf selektieren (z.B. im AfterScroll-Ereignis). Mit anderen Worten: Da ich die beiden Blob-Felder nur zur Anzeige, nicht aber zur Anwendung von Such- und Sortierkriterien benötige, muß ich die nicht mitschleppen. Aus diesem Grund gibt es bei vielen Komponenten wie z.B. auch beim VTV die beiden Schalter BeginUpdate und EndUpdate. Meine Bilder im dargestellten VTV kommen ebenfalls aus einer Datenbank und sind zudem ziemlich groß. Beim Baumaufbau werden diese Bitmaps, die an anderer Stelle in Originalgröße benötigt werden, mit StretchDraw in die Bitmaps der TImageList kopiert, wonach letztere dann dem VTV zugewiesen wird. |
AW: VirtualTreeView und icons
Zitat:
Delphi-Quellcode:
bzw.
VTV.Invalidate
Delphi-Quellcode:
neu zeichnen lassen und die Bilder sind direkt auf dem neuen Stand.
VTV.InvalidateNode(ANode)
Zitat:
Dann kannst du ganz einfach im PaintText Event per Index auf die Liste der Bilder zugreifen und dann um die Breite des Bildes verschieben. Das mal so aus meiner Sicht. Vielleicht hilft es dir ja weiter. |
AW: VirtualTreeView und icons
ok nochmal ich habe eine DB, und jeder eintrag in der DB hat sein eigenes Icon das ich dann nach bedarf in der VTV anzeigen möchtebei.
so jetzt muss ich das icon doch irgendwie aus der DB rausholen damit ich das anzeigen kann. richtig? so ob ich das in eine NodeRecord, ImageList oder in irgend einer anderen liste zwischenspeicher und dan per index daraf zugreife. (und wie schon gesagt eine ImageList kommt nicht in fragen wegen limit) der speicher (Ram) wird immer gleich ausgelastet! richtig? und ein icon wiegt ~800 bytes. ehrlich gesagt ich sehe kein unterschied. abgesehen von ein wenig mehr arbeitsspeicher verbrauch. aber ich möchte es richtig machen, und ich würde mich echt freuen wenn ihr mir eine kleine demo schreiben konntet. es gibt bestimmt jemanden der mehr plan von db's un vtv's hat den ich. und zeit um mir ne demo zu schreiben. ich freue mich über jede hilfe. So thanks in advance by the way ich benutze die ![]() |
AW: VirtualTreeView und icons
Selbstverständlich mußt du das Icon erst mal aus der Datenbank einlesen. Mit deiner Methode, das Icon in NodeData zu speichern, verursachst du unnötige CPU-Last beim Handling mit dem VTV, was natürlich bedeutet, daß der Aufbau eines riesigen Trees wie in deinem Fall unnötig verzögert wird. So müssen z.B. die Bitmaps in NodeData beim Umsortieren ständig mitkopiert werden, was bei einer externen Liste (Generics TObjectList) nicht nötig wäre, denn die hättest du einfach nur nach dem Index sortiert, damit das gesuchte Bitmap schnell gefunden wird.
Dazu erzeugst du dir erstmal eine Klasse:
Delphi-Quellcode:
Dann deklariest du in der Formular-Unit, die den VTV aufnehmen soll, eine Objektliste:
UNIT Startbilder;
INTERFACE USES ExtCtrls, StdCtrls, Classes, Graphics; TYPE TStartBild = Class PRIVATE { Private-Deklarationen } Var fBild : TImage; fIndex : Integer; Function GetfBild : TImage; Procedure SetfBild(Const Value : TImage); Function GetfIndex : TImage; Procedure SetfIndex(Const Value : TImage); PUBLIC { Public-Deklarationen } Constructor Create(); Destructor Destroy; override; Property Bild : TImage read GetfBild write SetfBild; Property Index : Integer read GetfIndex write SetfIndex; END; IMPLEMENTATION { TStartBild } Function TStartBild.GetfBild: TImage; begin Result := fBild; end; Procedure TStartBild.SetfBild(Const Value: TImage); begin fBild.Assign(Value); end; Function TStartBild.GetfIndex: Integer; begin Result := fBild; end; Procedure TStartBild.SetfIndex(Const Value: Integer); begin fBild.Assign(Value); end; Constructor TStartBild.Create; begin inherited; fBild := TImage.Create(nil); fBild.Visible := False; fBild.AutoSize := False; fBild.Stretch := False; fBild.Proportional := False; fBild.Center := False; fBild.Width := 30; // oder wie die Größe deiner Icons eben ist fBild.Height := 30; end; Destructor TStartBild.Destroy; begin fBild.Free; inherited; end; end.
Delphi-Quellcode:
In FormCreate und FormDestroy deines VTV-Formulars schreibst du:
...
PRIVATE { Private-Deklarationen } Var BList : Generics.Collections.TObjectList<TStartBild>; ...
Delphi-Quellcode:
Den VTV baust du in einer privaten Methode auf, die z.B. bei Programmstart aufgerufen wird. Dort gehst du deine DB-Tabelle durch und liest die entsprechenden Spalten aus. In NodeData.Index schreibst du den Index deiner Tabelle, der mit dem Index in BList korrespondiert (nicht der Listen-Index, sondern der Eintrag Index, der in der Klasse festgelegt wurde).
Procedure TFormMain.FormCreate(Sender: TObject);
begin BList := TObjectList<TStartBild>.Create; ... end; Procedure TFormMain.FormDestroy(Sender: TObject); begin If BList.Count > 0 Then BList.DeleteRange(0,BList.Count-1); BList.Free; ... end; Nachtrag: Probier's doch einfach mal aus: Erstelle eine Exe mit den Icons in NodeData und eine mit den Icons in einer Objektliste, wobei du jedesmal die Zeit mißt, die vergeht, wenn du
|
AW: VirtualTreeView und icons
[QUOTE=Perlsau;1268418]Mit deiner Methode, das Icon in NodeData zu speichern, verursachst du unnötige CPU-Last beim Handling mit dem VTV, was natürlich bedeutet, daß der Aufbau eines riesigen Trees wie in deinem Fall unnötig verzögert wird. So müssen z.B. die Bitmaps in NodeData beim Umsortieren ständig mitkopiert werden,...[QUOTE] Der Speicherverbrauch wächst minimal, d.h. um 4 Byte pro Record. Deine Argumention ist daher nicht schlüssig. Einzig die eventuell redundante Instantiierung von identischen Icons könnte man verbessern. Hier würde ich einen einfachen Cache vorschlagen. Zu klären wäre nur eine Funktion, die zu einem Icon einen eindeutigen Schlüssel generiert.
Zitat:
Diese Implementierung ist somit skalierbar, d.h. sie funktioniert auch bei sehr großen Datenmengen. Hier wäre natürlich die Visualisierung anzupassen, d.h. nur die Daten zu laden und im Speicher zu halten, die gerade benötigt werden. |
VirtualTreeView und TImageList
Liste der Anhänge anzeigen (Anzahl: 1)
Ein Testlauf mit einer TImageList bzw. ein TjvImageList ergab Folgendes:
Knapp eine Million (genau: 923.168) Icon-Bitmaps der Größe 30x30 mit 24 Bit Farbtiefe wurden problemlos in eine ImageList geladen – allerdings nur mit einem 64-Bit-Programm unter einem Win64. Da würden auch noch etliche mehr reinpassen. Bei einem 32-Bit-Programm paßten in die Imagelist lediglich 771.725 Bitmaps dieser Größe (hängt wohl auch davon ab, was sich sonst noch so im Speicher tummelt). Eine Testanwendung habe ich nun so gestaltet, daß immer nur die Bitmaps in die Imagelist geladen werden, die auch aktuell ausgewählt wurden. Ich glaube kaum, daß ein Anwender sich in einer einzigen Sitzung durch alle Baumknoten klickt, um auch nur die Grenze der Imagelist bei einer 32-Bit-Anwendung, geschweige denn bei einer 64-Bit-Anwendung zu erreichen. Wer sich für die Testanwendung incl. zugrundeliegender Testdatenbank (gepacktes Firebird-Backup ca. 300 MB) und Source interessiert, möge mir das via PM mitteilen. Die Datenbank enthält jene 923.168 Einträge (Geodaten von OpenGeoDB oder OpenStreetMap, weiß ich gerade nicht auswendig), denen jeweils ein oben beschriebenes Bitmap (in der DB gespeichert als Jpg-Grafik) zugeordnet ist. Das Program stellt diese Einträge als Baumstruktur dar, und zwar geordnet. Zum Kompilieren werden die ![]() |
AW: VirtualTreeView und icons
:thumb:
Ich glaube auch nicht, das hier die Grenze jemals erreicht wird, aber wenn man es -rein theoretisch- optimieren will (weil es regnet, einem langweilig oder nur um mal zu sehen, ob es geht), dann würde man einen Cache einbauen, der die Icon-ID mit dem ImageListIndex mappt. Und wenn nun die ImageList mehr als z.B. 1000 Einträge hätte, würde man den ältesten Eintrag aus dem Cache, und das Icon aus der ImageList rausschmeißen und im Cache alle Indexe aktualisieren. |
AW: VirtualTreeView und icons
Hi.
entschuldigung das ich mich nicht früher gemeldet hatte, war mit arbeit beschäftigt! und danke an alle für eure hilfe. @Perlsau ich hatte an so etwas ähnliches selbst gedacht wie dein code schnipsel! das problem war, ich muss nicht nur den index zum Icon managen sondern auch den Index zur (TStartBild = Class). so wenn ich auf die Daten im (TStartBild) zugreifen will mus ich ja den index zu der (TStartBild = Class) Wissen.
Delphi-Quellcode:
// Delete
procedure TMain_form.RecordTreeBeforeCellPaint(Sender: TBaseVirtualTree;...)
begin TargetCanvas.Draw(R.Left, 0, BList .Items[index].Image); //index? end;
Delphi-Quellcode:
BList.Delete(index); um ein bild freizugeben
thx. |
AW: VirtualTreeView und icons
Da es wohl kaum soweit kommt, daß der Anwender eine Million Icons gleichzeitig darstellen will, würde ich wie oben bereits beschrieben dennoch eine TImageList verwenden, die einfach immer nur die Icons enthält, die angezeigt werden bzw. in der aktuellen Sitzung bereits angezeigt wurden. Dafür reicht die Kapazität der TImageList auf jeden Fall aus, was ich mit Hilfe des oben vorgeführten Testprogramms herausfinden konnte.
|
AW: VirtualTreeView und icons
Hi
an diesem punkt spielt es keine rolle ob ich eine ImageList oder dein Code benutze, solange ich die icons klein halte 16x16 (und da muss ich ich mir noch etwas ausdenken) ich meine, ich hatte zum test eine 4 MB image/Icon in die DB gespeichert und anschließend in die VTV geladen. nur um die auslastung zu sehen. und da ich den benutzer die möglichkeit geben will Icons von der festplatte zu laden. muss ich das verhindern dass er absichtlich oder aus versehen riesige bilder als icons für die VTV speichert normalerweise hat die listen ~ 20 einträge E-Email einstellungen logins für skype, Steam, Online Bank ... das programm hat aber auch die möglichkeit Bookmarks von firefox zu parsen und in die DB speichern. und in meine fall sind das eine ganze menge. |
AW: VirtualTreeView und icons
Du kannst den Anwender im Programm doch darauf hinweisen, daß nur Icon-Bilder bis zu einer gewissen Größe erlaubt sind:
Delphi-Quellcode:
Und dann erzeugst du aus dem zu großen Bild mit StretchDraw einfach ein kleineres.
ShowMessage('Das Bild ist zu groß für ein Icon und wird automatisch auf die Größe 16x16 verkleinert';
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:04 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz