![]() |
Re: VirtualStringTree: Speicherleck
Zitat:
Ich müsste ja für ein Item angeben unter welchen übergeordneten Knoten es angeordnet ist. Könnte ich natürlich tun und das entspräche ja ungefähr der Möglichkeit (3). Noch mal auf das Beispiel von oben eingehend würde in den Beschreibungstexten für ein Item dann stehen: 'Verzeichnisse|Archivdaten' 'Verzeichnisse|Temporäre Daten' usw., wobei der '|' als Separator für die Knotenebenen dienen würde. Beim Einfügen des Knotens müsste ich dann nur nach den entsprechenden übergeordneten Knoten suchen. Da die Collection irgendwo gespeichert wird, würden damit auch die Beschreibungstexte mit gespeichert werden. Das ist nur dumm, sobald z.B. Benutzerdaten gespeichert werden, dann wird für jeden Benutzer der ganze Rattenschwanz gespeichert. |
Re: VirtualStringTree: Speicherleck
Ich glaube, ich habe nicht erfasst, was du eigentlich anzeigen willst.
|
Re: VirtualStringTree: Speicherleck
Ich nehme nochmal Bezug auf das Beispiel von Post #6:
In der Collection gibt es die Items "Archivdaten", "Temporäre Daten", "Neue Daten vorhanden" und "Irgendwas anderes". Im Baum sollen also die zugehörigen Daten in der zu dem Childnode gehörigen zweiten Spalte angezeigt werden. Damit das ganze etwas hübscher und aufgeräumter daherkommt, sollen diese Knoten noch die übergeordneten Knoten "Verzeichnisse" und "Benutzerhinweise" erhalten. Zu diesen beiden Knoten gibt es nun keine eigentlichen Daten (= Items in der Collection). +-Verzeichnisse | | | +------Archivdaten | +------Temporäre Daten | +-Benutzerhinweise | +------Neue Daten vorhanden +------Irgendwas anderes Ich denke die beste Lösung wird es sein die CollectionItems so beizubehalten, wie geplant und in den Knotendaten die ID und einen Zeiger auf das Item zu hinterlegen. Über die Knoten-ID kann ich per Getter-Funktion den passenden GUI-Text ermitteln und über den Zeiger auf die Items die Daten für die zweite Spalte. Um die Erstellung der GUI dennoch einfach und übersichtlich zu halten, kann ich diese ja in einer Stringkonstanten im Quelltext hinterlegen, die von der o.g. GUI-Klasse "aufgedröselt" wird. So in etwa:
Delphi-Quellcode:
const
//Anzeigetexte für die GUI im Format ID|AuthReq|Knotentexte CSettingsTree = '10101|0|Verzeichnisse|Archivdaten,' + '10102|0|Verzeichnisse|Temporäre Daten,' + '10201|0|Benutzerhinweise|Neue Daten vorhanden,' + '10202|0|Benutzerhinweise|Irgendwas anderes'; |
Re: VirtualStringTree: Speicherleck
Ich schlage dir mal etwas vor:
Delphi-Quellcode:
In der Anwendung:
type
TBasicCollection=class; //Grundlegende abstrakte Klasse für einen Knoten (egal welcher) TBasicCollectionItem=class(TCollectionItem) Destructor Destroy; override; private FChildCollection:TBasicCollection; //Für Kindknoten oder nil wenn keine Kinder public function GetVSTBezeichner:string; virtual; abstract; //Text für VST (wird je nach Ableitung anders implementiert) property ChildCollection:TBasicCollection read FChildCollection; procedure Init(const Param:string); virtual; abstract; //Param bekommt einen Teilstring von CSettingsTree end; TBasicCollection=class(TCollection) private function GetBasicCollectionItem(index:Integer): TBasicCollectionItem; public property BasicCollectionItem[index:Integer]:TBasicCollectionItem read GetBasicCollectionItem; procedure Init(const Param:string); end; //CollectionItem für die Knoten (Verzeichnisse und Benutzerhinweise) TCollectionItemA=class(TBasicCollectionItem) private FName: string; //hier nur Name merken procedure SetName(const Value: string); public function getVSTBezeichner:string; override; //result:=FName; property Name:string read FName write SetName; procedure Init(const Param:string); override; end; //CollectionItem für die anderen Knoten TCollectionItemB=class(TBasicCollectionItem) private FAuthReq: Integer; FxID: Integer; FText: string; procedure SetAuthReq(const Value: Integer); procedure SetText(const Value: string); procedure SetxID(const Value: Integer); public function getVSTBezeichner:string; override; //result:=Ftext; property xID:Integer read FxID write SetxID; property AuthReq:Integer read FAuthReq write SetAuthReq; property Text:string read FText write SetText; procedure Init(const Param:string); override; end; implementation {$R *.dfm} //gibt den Pos-ten Teilstring in den spitzen Klammern zurück (ohne Beachtung von inneren Klammern) function GetStringPart(const Str: string; Pos: Integer): String; var aPos,ePos:Integer; len:Integer; countopen:Integer; begin len:=length(str); aPos:=0; countopen:=0; while Pos>0 do begin inc(aPos); while (aPos<=len)and((Str[aPos]<>'<')or(countopen>0)) do begin case Str[aPos] of '<': inc(countopen); '>': dec(countopen); end; inc(aPos); end; inc(countopen); dec(Pos); end; ePos:=aPos+1; countopen:=0; while (ePos<=len)and((Str[ePos]<>'>')or(countopen>0)) do begin case Str[ePos] of '<': inc(countopen); '>': dec(countopen); end; inc(ePos); end; result:=copy(str,aPos+1,ePos-aPos-1); end; { TBasicCollection } function TBasicCollection.GetBasicCollectionItem(index:Integer): TBasicCollectionItem; begin result:=items[index] as TBasicCollectionItem; end; procedure TBasicCollection.Init(const Param: string); var i:Integer; Part:string; BasicCollectionItem:TBasicCollectionItem; begin i:=1; Part:=GetStringPart(Param,i); while Part<>'' do begin BasicCollectionItem:=Add as TBasicCollectionItem; BasicCollectionItem.Init(Part); inc(i); Part:=GetStringPart(Param,i); end; end; { TBasicCollectionItem } destructor TBasicCollectionItem.Destroy; begin FChildCollection.Free; inherited; end; { TCollectionItemA } function TCollectionItemA.getVSTBezeichner: string; begin result:=FName; end; procedure TCollectionItemA.Init(const Param: string); begin Name:=GetStringPart(Param,1); FChildCollection:=TBasicCollection.Create(TCollectionItemB); FChildCollection.Init(GetStringPart(Param,2)); end; procedure TCollectionItemA.SetName(const Value: string); begin FName := Value; end; { TCollectionItemB } function TCollectionItemB.getVSTBezeichner: string; begin result:=FText; end; procedure TCollectionItemB.Init(const Param: string); begin xID:=StrToInt(GetStringPart(Param,1)); AuthReq:=StrToInt(GetStringPart(Param,2)); Text:=GetStringPart(Param,3); end; procedure TCollectionItemB.SetAuthReq(const Value: Integer); begin FAuthReq := Value; end; procedure TCollectionItemB.SetText(const Value: string); begin FText := Value; end; procedure TCollectionItemB.SetxID(const Value: Integer); begin FxID := Value; end;
Delphi-Quellcode:
Aus der Konstante wird folgende Collection aufgebaut:
//habe mal die Konstante etwas geändert; macht das Parsen einfacher;
//wäre aber auch mit deinem String gegangen const CSettingsTree='<'+ ' <Verzeichnisse>'+ ' <'+ ' <'+ ' <10101><0><Archivdaten>'+ ' >'+ ' <'+ ' <10102><0><Tempöräre Daten>'+ ' >'+ ' >'+ '>'+ '<'+ ' <Benutzerhinweise>'+ ' <'+ ' <'+ ' <10201><0><Neue Daten vorhanden>'+ ' >'+ ' <'+ ' <10202><0><Irgendetwas>'+ ' >'+ ' >'+ '>'; type TForm1 = class(TForm) VirtualStringTree1: TVirtualStringTree; procedure FormCreate(Sender: TObject); procedure VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); procedure VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); procedure FormDestroy(Sender: TObject); private { Private-Deklarationen } FDaten:TBasicCollection; procedure vstInit(Node:PVirtualNode;BasicCollection:TBasicCollection); public { Public-Deklarationen } end; implementation procedure TForm1.FormCreate(Sender: TObject); begin FDaten:=TBasicCollection.Create(TCollectionItemA); FDaten.Init(CSettingsTree); //CSettingsTree in die entsprechende Collectionstruktur umformen vstInit(nil,FDaten); //rekursiv den VST erstellen end; procedure TForm1.vstInit(Node: PVirtualNode; BasicCollection: TBasicCollection); var i:Integer; BasicCollectionItem:TBasicCollectionItem; cNode:PVirtualNode; begin for i:=0 to BasicCollection.Count-1 do //für alle items in der Collection begin BasicCollectionItem:=BasicCollection.BasicCollectionItem[i]; //getitem cNode:=VirtualStringTree1.AddChild(Node,BasicCollectionItem); //Knoten erstellen if assigned(BasicCollectionItem.ChildCollection) then //wenn Kinder vorhanden vstInit(cNode,BasicCollectionItem.ChildCollection); //Kindknoten rekursiv anlegen end; end; procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString); begin //egal welcher Knoten, überall befinden sich Ableitungen von TBasicCollectionItem in NodeData //und alle haben die Methode GetVSTBezeichner überschrieben Celltext:= (TObject(Sender.GetNodeData(Node)^)as TBasicCollectionItem).getVSTBezeichner; end; //hier noch ein Beispiel, wie man weitere Informationen je Knoten bekommt procedure TForm1.VirtualStringTree1FocusChanged(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex); var obj:TObject; begin obj:=TObject(Sender.GetNodeData(Node)^); if obj is TCollectionITemA then showmessage('Root') else if obj is TCollectionitemB then showmessage(Format('ID: %d; AuthReq: %d; Text: %s', [TCollectionItemB(obj).xID, TCollectionItemB(obj).AuthReq, TCollectionItemB(obj).Text])); end; procedure TForm1.FormDestroy(Sender: TObject); begin FDaten.Free; end;
Code:
Damit kannst du quasi in BasicCollectionItem alles das implementieren, was du für den VST brauchst (neben dem NodeText kann das durchaus auch noch Checkstate o.ä. sein, was man recht simpel über die Ereignisse des VST mit setzen kann.
TBasicCollection
| +-TCollectionItemA(Verzeichnisse) | | | +-TBasicCollection | | | +--TCollectionItemB(10101|0|Archivdaten) | +--TCollectionItemB(10102|0|Temporäre Daten) | +-TCollectionItemA(Benutzerhinweise) | +-TBasicCollection | +--TCollectionItemB(10201|0|Neue Daten vorhanden) +--TCollectionItemB(10202|0|Irgendetwas) Die Ableitungen von BasicCollectionItem berherbegen dann zusätzliche Informationen zum Knoten. |
Re: VirtualStringTree: Speicherleck
Das Speicherleck kommt von dem String Datentyp.
Ich habe das in dem Stammtisch Video erklärt. 2 Möglichkeiten 1) Shortstrings verwenden für die VST Struktur. 2) Den Huge String freigeben im OnFreeNode Ich empfehle wie in dem Video gezeigt mit Objekten und nicht mit Records zu arbeiten. ![]() |
Re: VirtualStringTree: Speicherleck
Danke Sirius! Das ist mal ne ausführliche Lösung.
Ich werde es mir mal genau anschauen, wenn ich ein paar freie Minuten habe, aber das grundlegende Prinzip habe ich kapiert. Ich muss dann nur schauen, wie ich es mit meiner Datenhaltung in Einklang bringen kann. |
Re: VirtualStringTree: Speicherleck
Zitat:
Zitat:
|
Re: VirtualStringTree: Speicherleck
Zitat:
Delphi-Quellcode:
oder
Finalize(NodeData^.Text);
Delphi-Quellcode:
das Ergebnis ist das selbe.
NodeData^.Text := '';
Aber: VT.OnFreeNode wird nur für die Knoten aufgerufen, die auch zur Laufzeit einmal auf dem Bildschirm sichtbar (also aufgeklappt) waren. Daher wird nur ein Teil der Strings freigegeben. |
Re: VirtualStringTree: Speicherleck
Leute, was habt ihr eigentlich immer mit eurem 'Finalize'?
New/Dispose (bei Records) bzw. Create/Free (bei Klassen) kümmert sich doch um die ganze Chose. Deshalb programmiert man doch in Delphi und nicht in C...? Es wurde doch lang und breit erklärt, das dieses OnFreeNode nicht dazu dient, den Speicher freizugeben. Man muss das also selbst machen. Ich würde mir auch keinen abbrechen, um die Information, die baumartig dargestellt werden soll, baumartig ablegen. Die Zeit hab ich einfach nicht: Wenn ich in einem Baum polymorphe Informationen darstelle, klebe ich an jeden Knoten ein Objekt. Das Freigeben mache ich selbst, indem ich jedes Objekt in eine TObjectlist packe (OwnsObjects := True). Was habe ich an dem Problem nicht verstanden, das es in meinen Augen keins ist? Außer, das ich den Sinn und Zweck dieses Events nicht verstanden habe. |
Re: VirtualStringTree: Speicherleck
@alzaimar:
Hier war der Record schon im Tree eingebaut. New/Dispose reservieren Speicher, geben ihn wieder frei und machen zusätzlich noch die Initialisierung/Finalisierung. Da der Speicher vom Record aber schon vorhanden war, fehlt hier nur noch die Initialisierung/Finalisierung. Und eben eine stelle, wo die Finalisierung auch aufgerufen wird. OnFreeNode scheint ja demnach wohl nicht so gut geeignet, obwohl ich das einfach nicht versteht, warum dieses nur aufgerufen wird, wenn der Node erst angezeigt wurde o.O aber ich hätte da jetzt eine "Lösung":
Delphi-Quellcode:
und schon sollte OnFreeNode aufgerufen werden :angel:
Node := vst.AddChild(ParentNode);
Data := vst.GetNodeData(Node); Include(Node.States, vsInitialUserData); Initialize(Node); ... Data.Text := 'gfdsvcxvy';
Delphi-Quellcode:
procedure TSettingsForm.vstFreeNode(Sender: TBaseVirtualTree; Node: PVirtualNode);
... Finalize(NodeData^.Text); |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:47 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