![]() |
AW: VirtualStringTree - Nodes hinzufügen
kleiner zwischenbericht:
- checkbox ist nun korrekt positioniert, dank dem property "MainColumn". - die items werden nun dank dieses threads korrekt angezeigt und verschwinden auch nicht mehr: ![]() das andere problem mit den images in den anderen columns habe ich auch bereits gelöst. wenn ich alle dateien und verzeichnisse aus C:\Windows\System32 in eine stringliste lade und die VST dann mit nodes fülle (listview-artig mit 6 spalten) dauert das ganze nun 4 sekunden. mit einer listview dauert das mehr als 20. ich füge nicht nur hinzu ich ändere auch schrift- und hintergrundfarben manchmal + es sind checkboxen vorhanden. bei biden varianten dauert die erstellung der stringliste rund 400ms. sind 4 sekunden für alles in ordnung oder ist das für 13000 einträge mit schöner darstellung zu lange? |
AW: VirtualStringTree - Nodes hinzufügen
Das Problem warum es bei dir so lange dauert ist das Hinzufügen. Eigentlich ist genau das schöne an der VST, dass du die Knoten hinzufügen kannst und diese nicht direkt abgerufen werden, erst wenn du sie anzeigst. Du rufst aber unnötigerweise ValidateNode auf und führst das damit ad absurdum. ;-)
Eine Möglichkeit
Delphi-Quellcode:
Das ist immer noch nicht optimal, da jede Datei aus dem Thread einzeln synchronisiert und angezeigt wird. Da könnte man auch z.B. alle Dateien in einem Verzeichnis in eine Liste packen und die ganze Liste synchronisiert hinzufügen oder ähnliches. Aber das sollte als beispiel erst einmal reichen. ;-)
type
TTreeData = class private FFilename: string; FIconIndex: Integer; public constructor Create(const AFilename: string); property IconIndex: Integer read FIconIndex write FIconIndex; property Filename: string read FFilename write FFilename; end; TOnAddFile = procedure(const AFilename: string) of object; TFileSearchTree = class(TThread) private FPath: string; FOnAddFile: TOnAddFile; FCurrentFile: string; procedure DoAddFile; protected procedure Execute; override; public constructor Create(const APath: string; const AOnAddFile: TOnAddFile); property OnAddFile: TOnAddFile read FOnAddFile write FOnAddFile; end; TForm67 = class(TForm) VST: TVirtualStringTree; ImageList1: TImageList; procedure FormCreate(Sender: TObject); procedure VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); procedure VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); private procedure AddFile(const AFilename: string); procedure AddFileEvent(const AFilename: string); public end; var Form67: TForm67; implementation {$R *.dfm} procedure TForm67.AddFileEvent(const AFilename: string); begin AddFile(AFilename); end; procedure TForm67.FormCreate(Sender: TObject); begin VST.NodeDataSize := SizeOf(TTreeData); TFileSearchTree.Create('c:\windows', AddFileEvent); end; procedure TForm67.VSTGetImageIndex(Sender: TBaseVirtualTree; Node: PVirtualNode; Kind: TVTImageKind; Column: TColumnIndex; var Ghosted: Boolean; var ImageIndex: Integer); function GetIconIndex(const AFilename: string): Integer; var Icon: TIcon; FileIcon: TSHFileInfo; begin Icon := TIcon.Create; try ZeroMemory(@FileIcon, SizeOf(FileIcon)); SHGetFileInfo(PChar(AFilename), 0, FileIcon, SizeOf(FileIcon), SHGFI_SYSICONINDEX or SHGFI_ICON); Icon.Handle := FileIcon.hIcon; Result := ImageList1.AddIcon(Icon); finally Icon.Free; end; end; var TreeData: TTreeData; begin if (Kind <> ikOverlay) and (Column = 0) then begin TreeData := TTreeData(Sender.GetNodeData(Node)^); if TreeData.IconIndex < 0 then TreeData.IconIndex := GetIconIndex(TreeData.Filename); ImageIndex := TreeData.IconIndex; end; end; procedure TForm67.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string); var TreeData: TTreeData; begin TreeData := TTreeData(Sender.GetNodeData(Node)^); case Column of 0: CellText := TreeData.Filename; end; end; procedure TForm67.AddFile(const AFilename: string); var NewNode: PVirtualNode; begin NewNode := VST.AddChild(nil, TTreeData.Create(AFilename)); NewNode.CheckType := ctCheckBox; NewNode.CheckState := csCheckedNormal; end; { TTreeData } constructor TTreeData.Create(const AFilename: string); begin FFilename := AFilename; FIconIndex := -1; end; { TFileSearchTree } constructor TFileSearchTree.Create(const APath: string; const AOnAddFile: TOnAddFile); begin inherited Create(False); FPath := APath; FOnAddFile := AOnAddFile; end; procedure TFileSearchTree.DoAddFile; begin FOnAddFile(FCurrentFile); end; procedure TFileSearchTree.Execute; procedure FindAllFiles(ARootFolder: string; AMask: string = '*.*'; ARecurse: Boolean = True); // von: http://www.delphipraxis.net/2235-verzeichnisse-nach-dateien-durchsuchen.html var SR: TSearchRec; begin // Implementation bis einschließlich Delphi 4 if ARootFolder = '' then Exit; if AnsiLastChar(ARootFolder)^ <> '\' then ARootFolder := ARootFolder + '\'; // Implementation ab Delphi 5 ARootFolder := IncludeTrailingPathDelimiter(ARootFolder); if FindFirst(ARootFolder + AMask, faAnyFile, SR) = 0 then try repeat if SR.Attr and faDirectory <> faDirectory then begin FCurrentFile := ARootFolder + SR.Name; Synchronize(DoAddFile); end; until FindNext(SR) <> 0; finally FindClose(SR); end; if ARecurse then if FindFirst(ARootFolder + '*.*', faAnyFile, SR) = 0 then try repeat if SR.Attr and faDirectory = faDirectory then // --> ein Verzeichnis wurde gefunden // der Verzeichnisname steht in SR.Name // der vollständige Verzeichnisname (inkl. darüberliegender Pfade) ist // ARootFolder + SR.Name if (SR.Name <> '.') and (SR.Name <> '..') then FindAllFiles(ARootFolder + SR.Name, AMask, ARecurse); until FindNext(SR) <> 0; finally FindClose(SR); end; end; begin if Assigned(FOnAddFile) then FindAllFiles(FPath, '*.*', True); end; |
AW: VirtualStringTree - Nodes hinzufügen
@ d7user1
Alles dauert seine Zeit. Das Lesen der 13000 Dateien dauert nur ein Augenblick, das Erstellen der passenden Objekte und sogar sortieren geht auch ganz schnell, selbst das Erstellen der Items. Aber kaum packst du die ListView an (vermutlich auch Virtual TreeView) um Daten anzulegen, dauert es Zeit. Das zuweisen aller Caption's ungleich "" dauert fast 1,5 Sekunden. Das Zuweisen der ImageIndex's ungleich 0 dauert etwa 2,5 Sekunden. Womit wir bei 4 Sekunden wären. Und die SubItems, selbst wenn leer, dauert auch seine Sekunden. Das bedeutet, die 13000 Dateien lesen und die gleiche Anzahl Objekte und Items anzulegen (plus sortieren) dauert nur paar Millisekunden, der Rest kostet Zeit. Ich hab gerade mein Beispiel insoweit geändert, dass zuerst alle Items angelegt werden, ohne eine Zuweisung. Der Rest wird beim Anzeigen der Items gefüllt. Also nicht wie im ersten Beispiel nur die Subitems, sondern alles. Das Ergebnis bei 13000 Dateien sind 265 Millisekunden. Ob das alles noch so seinen Sinn macht sein dahingestellt, ist einfach nur ein Test. |
AW: VirtualStringTree - Nodes hinzufügen
Zitat:
Wie der Name schon sagt ist der "Virtual Tree View" virtuell. Also es gibt da keinen direkten Zugriff auf Knoten oder Unterknoten. ItemIndex-Images werden auch nicht direkt zum Baum gespeichert, sondern "On Demand" mittels GetNodexIndex geholt. Das hat den Vorteil, dass dadurch der VST nicht an eine Struktur gebunden ist, sondern du alle möglichen Daten mit dem Baum verknüpfen und je nach Bedarf nach deinen eigenen Vorstellungen darstellen kannst. Auf einzelne Knoten (und deren Daten) kannst du somit auch nicht direkt über VST.Node[X] zugreifen, sondern müsstest erst durch den Baum iterieren. Entweder per Hand mit
Delphi-Quellcode:
, bzw.
VST.GetFirst ... while ... VST.GetNodeData ... Daten mit Suchparametern vergleichen ... end VST.GetNext
Delphi-Quellcode:
,
GetFirstSibling
Delphi-Quellcode:
, usw. Oder per
GetNextSibling
Delphi-Quellcode:
-Event und dann mit VST.IterateSubTree.
OnIncrementalSearch
|
AW: VirtualStringTree - Nodes hinzufügen
hallo, popov, fügst du die daten aus C:\Windows\System32 in deine liste hinzu? und wenn ja, ist es noch immer das beispiel aus deinem anderen thread? denn das war nicht rekursiv meine ich und es waren glaube ich etwas mehr als 2000 daten.
ich habe gerade mal geguckt. daten sammeln (C:\Windows\System32 rekursiv) und diese in records zu speichern ohne daten in der VST anzuzeigen dauert bei mir etwas mehr als 700ms. man darf nicht vergessen dass ich noch OnPaintText und OnAfterItemErase verwende und dort mit Font.Color und Brush rumgespielt wird. zu deinem beispiel jaenike: ich mache alles genau so wie du außer dass ich das nicht auf einen thread ausgelagert habe und dass ich die nodes so erzeuge:
Delphi-Quellcode:
laut dem post von Popov sind 4 sekunden also normal für das alles was ich mache?
Node := aVST.AddChild(nil);
Node.CheckType := ctCheckBox; Node.CheckState := csCheckedNormal; Data := aVST.GetNodeData(Node); Data^ := aRecord; p.s.: selbst wenn ich die Icon in der VST weglasse und die Zellentexte nicht zuweise duaert es bei mir 700ms. ich glaube der lädt die Icons und die Zellentexte von haus aus erst wenn sie gebracht werden. hier eine kleine zeitmessung für C:\Windows\System32 NICHT rekursiv: - das lesen der daten dauert bei mir ungefähr 60ms. - das erstellen der records ohne nodes zu erzeugen etwa 600ms - das erstellen der records mit nodes zu erzeugen etwa 650ms - und alles zusammen mit erstellen und freigeben der dateiliste, BeginUpdate, EndUpdate etwa 700ms. ist das so in ordnung? großes edit: ich weiß jetzt warum das 4 sekunden dauert. beim erstellen meiner records nutze ich einmal IntToStr(getFileSizeA()) und einmal DateTimeToStr(getFileLastModified()). ohne IntToStr(getFileSizeA()) dauert es nur 1.5 sekunden und ohne DateTimeToStr(getFileLastModified()) dauert es nur 3.3 sekunden. ohne beides dauert alles zusammen plus anzeigen der 13.000 dateien 400ms. meine zwei funktionen sehen so aus:
Delphi-Quellcode:
function getFileSizeA(const FileName: string): Int64;
var SR: TSearchRec; begin Result := 0; if not FileExists(FileName) then Exit; if FindFirst(FileName, faAnyFile and not faDirectory, SR) = 0 then try Result := SR.Size; finally SysUtils.FindClose(SR) end; end; function getFileLastModified(const FileName: string): TDateTime; var SR: TSearchRec; SystemTime: TSystemTime; NewWriteTime: TFileTime; begin Result := 0; if FindFirst(FileName, faAnyFile, SR) = 0 then try if (Windows.FileTimeToLocalFiletime(SR.FindData.ftLastWriteTime, NewWriteTime) and Windows.FileTimeToSystemTime(NewWriteTime, SystemTime)) then Result := Encodedate(SystemTime.wYear, SystemTime.wMonth, SystemTime.wDay) + Encodetime(SystemTime.wHour, SystemTime.wMinute, SystemTime.wSecond, SystemTime.wMilliseconds); finally SysUtils.FindClose(SR) end; end; |
AW: VirtualStringTree - Nodes hinzufügen
Wobei das Zeichnen usw. egal ist, da das ja nur die tatsächlich angezeigten Knoten betrifft.
Was bei dir aber noch fehlt ist ein BeginUpdate...EndUpdate, denn dadurch dauert das Hinzufügen der Knoten sehr viel länger. Wenn man den Thread noch ändert und die Dateien häppchenweise statt einzeln übergibt plus BeginUpdate...EndUpdate sollte das ganze quasi sofort initial angezeigt werden (und dann natürlich noch kurz nachladen). Der übliche Weg sieht übrigens so aus, dass man immer nur die Dateien der aktuellen Ebene anzeigt und beim Aufklappen die Ebene darunter einliest. So sollte es keinerlei Verzögerungen geben, es sei denn du hast Verzeichnisse mit enorm vielen Dateien. |
AW: VirtualStringTree - Nodes hinzufügen
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Und ja, es ist nicht rekursiv, denn es sollte nur ein Ordner gelesen werden. Ich denke mir aber, dass es keinen besonderen Unterschied macht wenn man den gesamten Inhalt liest, inkl. Unterordner. Trotzdem, auch wenn ich das so hin gekriegt habe, es ist eher nur just4fun. Ich hab noch nie mit virtuellem ListView gearbeitet, evtl. ist es für die Aufgabe besser geeignet. Was das Andere angeht, Total Commander hatte bis (mindestens) Version 5.5 die Möglichkeit einen Verzeichnis-Baum auszugeben. Um das zu laden hat er zum Schluss fast immer eine Minute gebraucht. Aber man läd nun mal nicht alles auf einmal, vor allem bei TreeView. Das dauert zu lange. |
AW: VirtualStringTree - Nodes hinzufügen
Zitat:
FindFirst sucht ja bereits nach dem Namen der Datei, findet es nicht, geht sie nicht in der Block rein (hier try finally), sondern übergeht ihn. Somit kann man sich die FileExists Abfrage sparen, sie kostet nur Zeit. SR.Size gibt u. U. die falsche Dateigröße zurück, denn es ist Integer. Solange du unter 2 GByte bleibst, merkst du nichts. Die richtige Größe steckt in FindData. Hier die Formel:
Delphi-Quellcode:
Ansonsten wird hier jede Datei anscheinend zwei mal angepackt - zuerst um den Namen zu lesen, später noch mal um die Größe zu erfahren. Dabei enthält TSearchRec schon beim ersten Zugriff alle Infos.
x := Int64(SR.FindData.nFileSizeLow) or (Int64(SR.FindData.nFileSizeHigh) shl 32);
// |
AW: VirtualStringTree - Nodes hinzufügen
Zitat:
Heute ist es ein Int64 Wert. |
AW: VirtualStringTree - Nodes hinzufügen
Gibt SR.Size (bei aktuellen Delphis) auch den korrekten Wert an (> 2GB)?
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:24 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-2025 by Thomas Breitkreuz