![]() |
Arbeiten mit TTreeView
Hi,
frohes neues Jahr :cheer: Folgende Problemstellung: Ich habe eine Klassse TTestObjects:
Delphi-Quellcode:
Diese Klasse ist über
type TTestObjects = class(TObject)
public // z.B. "Kamel" oder "Auto" oder "Haus" ItemCaption: string; // Semikolon getrennte Liste, z.B. "Objekt;Mensch;Maschine" Tags: string; end; var FObjects: TList<TTestObjects>;
Delphi-Quellcode:
ansprechbar.
FObjects
Ich möchte nun alle Objekte, gruppiert nach den einzelnen Tags, in einem TTreeView anzeigen lassen. Hat also ein Objekt 3 Tags, erscheint es unter 3 Nodes im TTreeView.
Delphi-Quellcode:
Der Code selbst funktioniert. Sobald die Anzahl der Objekte jedoch größer werden, gibt es Probleme mit der Performance. Wie kann man den Code optimieren?
procedure UpdateList;
var i, k, l: integer; varItemAdded: boolean; varStringArray: TStringDynArray; varTreeNodeItem: TTreeNode; begin TreeView.Items.BeginUpdate; TreeView.Items.Clear; for i := 0 to FObjects.Count-1 do begin // Wurde ein Tag gesetzt? if (trim(FObjects[i].tags) <> '') then begin varStringArray := SplitString(FObjects[i].tags, ';'); end else begin setlength(varStringArray, 1); varStringArray[0] := 'Untagged Objects'; end; for k := 0 to length(varStringArray)-1 do begin // Leere Tags ignorieren if varStringArray[k] = '' then Continue; varItemAdded := False; for l := 0 to TreeView.Items.Count -1 do begin if TreeView.Items[l].Text = varStringArray[k] then begin // Objekt zu bereits existierendem Tag hinzufügen TreeView.Items.AddChild(TreeView.Items[l], FObjects[i].ItemCaption); varItemAdded := True; Break; end; end; if not varItemAdded then begin // Neues Tag anlegen und Objekt hinzufügen varTreeNodeItem := TreeView.Items.Add(nil, varStringArray[k]); TreeView.Items.AddChild(varTreeNodeItem, FObjects[i].ItemCaption); end; end; end; TreeView.Items.EndUpdate; end; Weitere Frage: Wie kann man verhindern, dass sich das TTreeView nach jeder Änderung zusammenklappt? Danke & Viele Grüße |
AW: Arbeiten mit TTreeView
Zitat:
Das geht auch viel schneller. Für eine bessere Performance würde ich die VirtualStringTree empfehlen: ![]() |
AW: Arbeiten mit TTreeView
Baum aufklappen geht mit
Delphi-Quellcode:
eventuell auch
treeview.FullExpand;
Delphi-Quellcode:
TreeView.AutoExpand := True;
Bei jedem neuen Objekt durchläufst Du den ganzen Baum, das erhöht mit wachsendem Baum automatisch die Laufzeit, weil ja die zu durchlaufende Menge ständig wächst und neue Zweige "irgendwo" in den Baum eingefügt werden müssen. 'ne andere Suchmöglichkeit für den TreeView ist mir allerdings nicht bekannt :-( Kannst Du Dir eventuell hieraus etwas schnelleres bauen? ![]() |
AW: Arbeiten mit TTreeView
hallo,
mit diesem Codeschnipsel
Delphi-Quellcode:
überprüfst du also, ob ein Tag bereits vorhanden ist. Wenn Ja, wird das Objekt an der Stelle angefügt, wenn nein hinten drangehängt. Dazu muss du aber Worst-Case die gesamte Treeview durchlaufen, bevor du weisst ob ein Tag bereits vorhanden ist oder nicht. Wenn ich aber eine Liste nicht komplett durchlaufen möchte, um zu wissen ob ein Element bereits vorhanden ist, nimmt man für gewöhnlich (zusätzlich) ein Dictonary (Hashmap oder wie auch immer). Ich würde also parallel zur Treeview, ein Dictonary mitpflegen, indem ich jeweils den Tag plus die Position in der Treeview speichere. Wenn ich ein Tag nun prüfe, schaue ich im Dictonary nach, ob das Tag vorhanden ist, wenn ja, erhalte ich die Position an der ich das Objekt einfüge, wenn das Tag noch nicht in meinem Dictonary vorhanden ist, weiss ich, dass ich das neue Tag am Ende in die Treeview einfügen muss.
varItemAdded := False;
for l := 0 to TreeView.Items.Count -1 do begin if TreeView.Items[l].Text = varStringArray[k] then begin // Objekt zu bereits existierendem Tag hinzufügen TreeView.Items.AddChild(TreeView.Items[l], FObjects[i].ItemCaption); varItemAdded := True; Break; end; end; if not varItemAdded then begin // Neues Tag anlegen und Objekt hinzufügen varTreeNodeItem := TreeView.Items.Add(nil, varStringArray[k]); TreeView.Items.AddChild(varTreeNodeItem, FObjects[i].ItemCaption); end; Zum Codieren hatte ich gerade keine Lust, aber vielleicht hilft es zumindestens als Denkanregung. mfg |
AW: Arbeiten mit TTreeView
Hmm..
oder man nutzt die möglichkeiten des TreeNodes:
Delphi-Quellcode:
Damit wird nur noch durch die HauptNodes durchgegangen, ohne alle Unternodes zu Prüfen!
for k := 0 to length(varStringArray)-1 do
begin // Leere Tags ignorieren if varStringArray[k] = '' then Continue; // erstes Node holen varTreeNodeItem := TreeView.TopItem; // Vergleichen, ob Tag vorhanden while Assigned(varTreeNodeItem) do begin if varTreeNodeItem.Text = varStringArray[k] then begin // Objekt zu bereits existierendem Tag hinzufügen TreeView.Items.AddChild(varTreeNodeItem, FObjects[i].ItemCaption); Break; end; // Nächstes Node der 'gleich' Ebene varTreeNodeItem := TreeView.TopItem.getNextSibling; end; // Wenn kein existierender Zweig gefunden, dannn anhängen if not Assigned(tmpTreeNode) then begin varTreeNodeItem := TreeView.Items.Add(nil, varStringArray[k]); TreeView.Items.AddChild(varTreeNodeItem, FObjects[i].ItemCaption); end; end; Vor allem wird nun wirklich nur nach den Tags gesucht, da beim Durchgehen der ganzen Liste auch TreeNode.Text von den untergeordneten FObjects[i].ItemCaption mit verglichen wird und somit bei einem ItemCaption = Tag dort ein Unter-Node eingefügt wird... |
AW: Arbeiten mit TTreeView
Hi,
hab folgendes TDictionary ergänzt:
Delphi-Quellcode:
:-D
FTagDict: TDictionary<string, TTreeNode>;
Daraus ergibt sich folgender Code:
Delphi-Quellcode:
Es wird besser :bouncing4:
procedure UpdateList;
var i, k: integer; varStringArray: TStringDynArray; varTreeNodeItem: TTreeNode; begin FTagDict.Clear; TreeView.Items.BeginUpdate; TreeView.Items.Clear; for i := 0 to FObjects.Count-1 do begin // Wurde ein Tag gesetzt? if (trim(FObjects[i].tags) <> '') then begin varStringArray := SplitString(FObjects[i].tags, ';'); end else begin setlength(varStringArray, 1); varStringArray[0] := 'Untagged Objects'; end; for k := 0 to length(varStringArray)-1 do begin // Leere Tags ignorieren if varStringArray[k] = '' then Continue; if FTagDict.ContainsKey(varStringArray[k]) then varTreeNodeItem := FTagDict.Items[varStringArray[k]] else varTreeNodeItem := nil; if Assigned(varTreeNodeItem) then begin TreeView.Items.AddChild(varTreeNodeItem, FObjects[i].ItemCaption); end else begin varTreeNodeItem := TreeViewBusinessObjects.Items.Add(nil, varStringArray[k]); FTagDict.Add(varStringArray[k], varTreeNodeItem); TreeView.Items.AddChild(varTreeNodeItem, FObjects[i].ItemCaption); end; end; end; TreeView.Items.EndUpdate; end; Danke & Viele Grüße |
AW: Arbeiten mit TTreeView
Wenn wir schon dabei sind... Habe ich mal eine Funktion geschrieben, um die Duplikate rauszubekommen aus dem TStringDynArray. Funktioniert zwar, wirkt aber doch arg zweckentfremdet :duck: Geht das eleganter bei gleicher Performance?
Delphi-Quellcode:
Mal abgesehen von den klassischen zwei Schleifen-Lösung des Problems...
function TfoPersistentModeller.RemoveDuplicateTags(
const aTags: TStringDynArray): TStringDynArray; var i,k: integer; varTagDict: TDictionary<string, string>; begin k := 0; varTagDict := TDictionary<string, string>.Create(Length(aTags)); for i := 0 to length(aTags)-1 do begin if varTagDict.ContainsKey(aTags[i]) then Continue; varTagDict.Add(aTags[i], ''); inc(k); setlength(Result, k); Result[k-1] := aTags[i]; end; varTagDict.Free; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:16 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