Autor |
Beitrag |
JanHH
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Fr 28.02.03 04:48
Hallo Forum,
treeview und kein Ende.. Einen solchen zu Laden und zu Speichern ist ja relativ einfach (TTreeView.SaveToFile bzw. -.LoafFromFile), aber was ist mit den damit verbundenen Daten (TTreeNode.Data)? Auch diese (ist bei mir jedeweils ein record bzw. ein ^record) zu laden und zu speichern ist kein Problem, nur werden doch die TreeNodes sequentiell in der Reihenfolge ihres Entstehens im Items-Array abgelegt, und die Data-Pointer deshalb logischerweise beim sequentiellen Speichern auch in dieser Reihenfolge.. wenn der Tree nun aber wieder geladen wird, geht die ursprüngliche Reihenfolge verloren, und das Items-Array wird neu angelegt, dabei halt umsortiert, und wenn ich die damit verbundenen Daten-Objekte dann einlese, ist die zuordnung nicht mehr korrekt.. und ich bin da ziemlich ratlos wie man das "einfach" lösen kann. Die Delphi-Hilfe erzählt da irgendwas von BLOB-Streams, was ist das??
Weiss irgend jemand Rat?
Danke und Gruß,
Jan
|
|
maximus
      
Beiträge: 896
Win XP, Suse 8.1
Delphi 4/7/8 alles prof
|
Verfasst: Fr 28.02.03 15:28
Hi.
Ich sehe zwei Möglichkeiten:
1. die property 'ItemID' des nodes in deinem record speicher und nachher wieder zuordnen. Musst aber testen ob sie auch gleich bleibt.
2. Main favorit: Eine klasse von TTreeNode ableiten und ein zusätzliches published property definieren. zB. 'RecordIndex'. Vor dem speichern allen nodes das record index geben. Nach dem laden alle nodes durchgehen und das richtige record linken!
mfg maximus.
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Fr 28.02.03 15:50
2. klingt gut, danke!
Gruß
Jan
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Fr 28.02.03 23:37
Hmm.. funktioniert doch nicht, glaube ich, ohne es ausprobiert zu haben. DENN: Wenn ich die treeview mittels SaveToFile/-Stream speichere, wird er ja nur als text gespeichert.. und die ID wird nicht mitgespeichert, also vermutlich auch durchaus geändert manchmal, und wenn ich zum Baum-Zusammenbauen ursprünglic nicht TTreeNode, sondern was davon abgeleitetes benutzt haben, dann wird das ja beim lesen nicht wieder so gemacht, sondern es werden nur die normalen TreeNode-Objekte genommen, und die Bezüge sind wieder alle weg.. Ich müsste also eher gleich die ganze TTreeView-Klasse ableiten und sowohl die TreeNodes durch eigene ersetzten, als auch die LoadFromFile-Methode durch ne eigenen ersetzen. oder? *ächz*
Die einfachste, wenn auch unelegante, Methode, das Problem zu lösen, ist meines erachtens nach, den Bezug zwischen Data-Record und TreeNode über die TreeNode-Caption herzustellen, welche in meinem Anwendungsfall eindeutig identifizierend ist.
Hat jemand spontan Inspriation zu meinen Ausführungen? Oder hab ich irgendwas übersehen oder falsch verstanden?
Danke und Gruß,
Jan
|
|
wulfskin
      
Beiträge: 1349
Erhaltene Danke: 1
Win XP
D5 Pers (SSL), D2005 Pro, C, C#
|
Verfasst: Sa 01.03.03 15:18
Hallo JanHH!
Es hat ne Weile gedauert, aber ich glaube es klappt jetzt: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90:
| type TNodeType = (ntNew, ntChild, ntNormal);
procedure SaveTree(const FileName: String; const Nodes: TTreeNodes); procedure WriteString(const FS: TFileStream; const S: String); var L: Word; begin L := Length(S); FS.Write(L, SizeOf(L)); FS.Write(S[1], L); end;
var I: Integer; Node, Parent: TTreeNode; FS: TFileStream; NodeType: TNodeType; Rec: PRecord; begin FS := TFileStream.Create(FileName, fmCreate or fmShareExclusive); with FS do begin try Parent := nil; I := Nodes.Count; Write(I, SizeOf(I)); for I := 0 to Nodes.Count - 1 do begin Node := Nodes[I]; if Node.Parent = nil then NodeType := ntNew else if Node.Parent <> Parent then NodeType := ntChild else NodeType := ntNormal; WriteString(FS, Node.Text); Rec := PRecord(Node.Data); Write(Rec, SizeOf(PRecord)); Write(NodeType, SizeOf(NodeType)); Parent := Node.Parent; end; finally Free; end; end; end;
procedure LoadTree(const FileName: String; const Nodes: TTreeNodes); function ReadString(const FS: TFileStream): String; var L: Word; S: String; begin S := ''; FS.Read(L, SizeOf(L)); SetLength(S, L); FS.Read(PChar(S)^, L); Result := S; end;
var I: Integer; FS: TFileStream; Rec: PRecord; S: String; NodeType: TNodeType; Node: TTreeNode; begin FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); with FS do begin try Node := nil; Read(I, SizeOf(I)); for I := (I - 1) downto 0 do begin S := ReadString(FS); Read(Rec, SizeOf(PRecord)); Read(NodeType, SizeOf(NodeType)); case NodeType of ntNew: Node := Nodes.AddObject(nil, S, Rec); ntChild: Node := Nodes.AddChildObject(Node, S, Rec); ntNormal: Node := Nodes.AddObject(Node, S, Rec); end; end; finally Free; end; end; end; | Den Typ PRecord musst du halt auf deinen Record anpassen. Ich muss gestehen, dass er bei mir noch die Daten falsch läd.
Ich konnte den Fehler bis jetzt nocht nicht finden, vielleicht findest du ihn.
Probier es einfach mal bei dir aus, vielleicht klappt es ja bei dir!
Gruß Hape!
Moderiert von Klabautermann: Code durch Delphi-Tags ersetzt.
_________________ Manche antworten um ihren Beitragszähler zu erhöhen, andere um zu Helfen.
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Sa 01.03.03 20:05
Hallo,
vielen Dank für die Mühe, aber funktioniert das wirklich!? Kann mir gar nicht vorstellen, das der Baum auf diese Weise korrekt gespeichert wird. Du speicherst ja nur die Information, OB ein Node Child- oder Parentnode ist, nicht, von WEM, wenn ich das richtig sehe.. also, die Bezüge der nodes aufeinander gehen verloren!? Würde ich zumindest vermuten.. Dein Ansatz ist ja, den Baum (bzw. die einzelnen Knoten) abzuklappern und quasi Binär zu speichern.. Hmm, sollte ich vielleicht auch mal ausprobieren. Danke also für die Mühe und Inspiration! Zumindest das zuordnen des Data-Records über die Caption ist als "Notlösung" ja auf jeden Fall möglich.. gar nicht so tirival, so ein TreeView, sehe ich das richtig?
Grüße
Jan
|
|
wulfskin
      
Beiträge: 1349
Erhaltene Danke: 1
Win XP
D5 Pers (SSL), D2005 Pro, C, C#
|
Verfasst: Sa 01.03.03 20:15
Hallo JanHH!
So funktioniert es tatsächlich. Das (oder der?) Node muss gar nicht wissen wer das Parent ist, denn dass ergibt sich zwangsweise aus der Reihenfolge!
Deshalb probier es erstmal aus, bevor du urteilst, denn es funktioniert  !
Das einzige, was beim mir nicht geklappt hat, war das Laden der Records. Das dürfte aber für dich kein großes Problem sein das zu beheben. Die Reihenfolge hat immer gestimmt und wird auch immer stimmen!
Gruß wulfskin!
_________________ Manche antworten um ihren Beitragszähler zu erhöhen, andere um zu Helfen.
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Sa 01.03.03 20:47
Hallo,
meine Vermutung ist halt, das die Reihenfolge zwar anfangs "OK" ist, aber wenn man Knoten hin- und herverschiebt (property Owner) sich die Parent-Child-Beziehungen verändern, aber ja nicht der Listenplatz, den ein Node im Items-Array hat, und alles durcheinandergerät.. Oder?
Gruß
Jan
|
|
wulfskin
      
Beiträge: 1349
Erhaltene Danke: 1
Win XP
D5 Pers (SSL), D2005 Pro, C, C#
|
Verfasst: Sa 01.03.03 20:55
JanHH hat folgendes geschrieben: | Hallo,
meine Vermutung ist halt, das die Reihenfolge zwar anfangs "OK" ist, aber wenn man Knoten hin- und herverschiebt (property Owner) sich die Parent-Child-Beziehungen verändern, aber ja nicht der Listenplatz, den ein Node im Items-Array hat, und alles durcheinandergerät.. Oder?
Gruß
Jan |
Sorry, blick ich net! Der Aufbau wird so geladen, wie er davor war. Versuch es doch einfach mal, dann kannst du ja immer noch meckern!
Gruß wulfskin!
_________________ Manche antworten um ihren Beitragszähler zu erhöhen, andere um zu Helfen.
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Sa 01.03.03 21:05
Naja die Reihenfolge der Knoten im Items-Array enstpricht nicht unbedingt der, wie sie in der Baumdarstellung sind (also von oben nach unten, so ganz visuell *g*), da man sie ja mit drag&drop hin-und-herschieben kann (in meiner Applikation). Und dabei ändert sich ja soweit ich weiss nicht die Reihenfolge im Items-Array, sonder nur die Owner-Property. Daher bezweifle ich das das so geht. Bei einem Baum, der nicht "editierbar" ist, sicher, aber wenn man darin was verschoben hat.. und ich meckere auch gar nicht, sondern denke lediglich drüber nach, mittlerweile reichlich frustriert  .
Gruß und Danke,
Jan
|
|
wulfskin
      
Beiträge: 1349
Erhaltene Danke: 1
Win XP
D5 Pers (SSL), D2005 Pro, C, C#
|
Verfasst: Sa 01.03.03 21:19
JanHH hat folgendes geschrieben: | Naja die Reihenfolge der Knoten im Items-Array enstpricht nicht unbedingt der, wie sie in der Baumdarstellung sind (also von oben nach unten, so ganz visuell *g*), da man sie ja mit drag&drop hin-und-herschieben kann (in meiner Applikation). Und dabei ändert sich ja soweit ich weiss nicht die Reihenfolge im Items-Array, sonder nur die Owner-Property. Daher bezweifle ich das das so geht. Bei einem Baum, der nicht "editierbar" ist, sicher, aber wenn man darin was verschoben hat.. und ich meckere auch gar nicht, sondern denke lediglich drüber nach, mittlerweile reichlich frustriert .
Gruß und Danke,
Jan |
Ahhh, jetzt versteh ich!
Wenn das so ist, wie du sagst (habe es noch getestet), dann geht es natürlich nicht so, wie ich das gemacht habe.
Dann müsste man den Baum durchgehen. Mit GetFirstNode hat man ja ein Anfang und man kann sich durch die Childs durchhangeln. Das einzige Problem das ich dann noch sehe ist das nächste "oberste" Item zu bekommen! Wenn du weisst, wie man an das kommt, dürfte das ganze kein Problem sein!
Gruß wulfskin!
_________________ Manche antworten um ihren Beitragszähler zu erhöhen, andere um zu Helfen.
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Sa 01.03.03 21:35
Tja ist wohl so.. ich werd das mal machen und den code dann posten, können sicher viele leute gebrauchen  Den Baum rekursiv durchzugehen ist ja prinzipiell kein problem, und in die datei müssen dann halt push- und pop-markierungen geschrieben werden.. nervig, aber möglich.
Naja und die andere Möglichkeit ist halt die Verknüpfung über die Caption des Nodes.. doof aber funzt sofern diese eindeutig ist.
Ich werds mal testen, melde mich dann wieder..
Gruß
Jan
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: So 02.03.03 04:31
es funktioniert  .. habe ein neues thema draus gemacht.
Moderiert von Tino: Beitrag aus dem neuen Topic hier eingefügt.
Hallo,
der folgende Code speichert und lädt einen TTreeView INKLUSIVE der Data-Records, die mit den Nodes verknüpft sind.. vielleicht interessiert das jemanden  .
WriteInt, WriteString, ReadInt und ReadString sind Hilfsfunktionen zum Laden und Speichern selbiger Daten, habe die wegen der Trivialität weggelassen, und WriteNodeData bzw. ReadNodeData speichern und laden den Data-Record (bzw. pRecord).
Sorry für die Formatierung, wie fügt man sourcecode hier "richtig" ein? Bin irgendwie planlos was das angeht..
Grüße
Jan
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91:
| procedure TForm1.WriteTreeView(f: TFileStream); begin WriteInt(f, 0); WriteTreeNode(TreeView1.Items[0], f); WriteInt(f, 99); end;
procedure TForm1.WriteTreeNode(node: TTreeNode; f: TFileStream); var myNode: TTreeNode; begin WriteString(f, node.Text); WriteNodeData(f, node.Data);
myNode := node.getFirstChild; if assigned(myNode) then begin WriteInt(f, 1); WriteTreeNode(myNode, f); end;
myNode := node.GetNextSibling; if assigned(myNode) then begin WriteInt(f, 0); WriteTreeNode(myNode, f); end else begin WriteInt(f, 2); end; end;
procedure TForm1.LoadTreeView(f: TFileStream; tree: TTreeView); var node: TTreeNode; begin node := nil; tree.Items.Clear; LoadTreeNode(f, tree, node); end;
procedure TForm1.LoadTreeNode(f: TFileStream; tree: TTreeView; node: TTreeNode); var s: String; nodeType: Integer; myNode, myParentNode: TTreeNode; begin myParentNode := node; nodeType := -1; while not(nodeType=99) do begin case nodeType of 0: begin ReadString(f, s); myNode := tree.Items.AddChild(myParentNode, s); myNode.Data := ReadNodeData(f); end; 1: begin ReadString(f, s); myParentNode := myNode; myNode := tree.Items.AddChild(myParentNode, s); myNode.Data := ReadNodeData(f); end; 2: begin if assigned(myParentNode) then myParentNode := myParentNode.Parent; end; end; ReadInt(f, nodeType); end; end; |
Moderiert von Tino: Code-Tags hinzugefügt.
Moderiert von Klabautermann: Code durch Delphi-Tags ersetzt.
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Mo 03.03.03 17:03
Hmm, das laden funktioniert doch noch nicht, die Data-Records und die Knoten werden nicht richtig zugeordnet.. seltsam *frust*.
Gibts nicht doch irgendwo sowas wie DIE trivial-Lösung dafür?
Jan
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Mo 03.03.03 17:24
Es geht doch, der Bug war woanders in meinem Programm, der obige Code ist also RICHTIG.. Sorry. und sorry für mein konfuses geposte..
Jan
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Do 26.08.04 00:11
Hallo!
Sorry, dass ich so ein altes Thema wieder hier rauf hole, aber von Streams hab ich herzlich wenig Ahnung.
Ich würde gern wissen wie die Hilfsfunktionen WriteInt, WriteString, ReadInt und ReadString ausschauen könnten, ich finde die garnicht so trivial
Danke im Voraus!
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
Zuletzt bearbeitet von Dr. Phil am Do 26.08.04 14:33, insgesamt 1-mal bearbeitet
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Do 26.08.04 02:34
So hab ich das gelöst.
Gruß
Jan
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72:
| unit MyFiles;
interface
uses Classes, SysUtils;
procedure WriteInt(var f: TFileStream; i: Integer); procedure ReadInt(f: TFileStream; var i: Integer);
procedure WriteReal(var f: TFileStream; r: real); procedure ReadReal(f: TFileStream; var r: real);
procedure WriteString(var f: TFileStream; s: String); procedure ReadString(f: TFileStream; var s: String);
procedure WriteBoolean(var f: TFileStream; b: Boolean); procedure ReadBoolean(f: TFileStream; var b: Boolean);
implementation
procedure WriteInt(var f: TFileStream; i: Integer); begin f.WriteBuffer(i, SizeOf(Integer)); end;
procedure WriteReal(var f: TFileStream; r: real); begin f.WriteBuffer(r, SizeOf(real)); end;
procedure WriteString(var f: TFileStream; s: String); var l: Integer; begin l := Length(s); f.WriteBuffer(l,SizeOf(Integer)); f.WriteBuffer(PChar(s)^,l); end;
procedure WriteBoolean(var f: TFileStream; b: Boolean); begin f.WriteBuffer(b, SizeOf(Boolean)); end;
procedure ReadInt(f: TFileStream; var i: Integer); begin f.ReadBuffer(i, SizeOf(Integer)); end;
procedure ReadReal(f: TFileStream; var r: real); begin f.ReadBuffer(r, SizeOf(real)); end;
procedure ReadString(f: TFileStream; var s: String); var p: PChar; l: Integer; begin f.ReadBuffer(l,SizeOf(Integer)); p := AllocMem(l*2); f.ReadBuffer(p^,l); s := String(p); FreeMem(p); end;
procedure ReadBoolean(f: TFileStream; var b: Boolean); begin f.ReadBuffer(b, SizeOf(Boolean)); end;
end. |
Moderiert von Klabautermann: Code durch Delphi-Tags ersetzt.
|
|
Dr. Phil
      
Beiträge: 66
Win XP
Delphi 7 Prof.
|
Verfasst: Do 26.08.04 10:19
Danke!
Und was ist mit WriteNodeData und ReadNodeData?
Ich möchte das Beispiel komplett übernehmen, also wär das angenehm wenn du mir zeigen könntest wie die prozeduren bei dir aussehen.
Ach und super, dass sich jemand die Arbeit dafür gemacht hat!
_________________ self-improvement is masturbation;
self-destruction is the answer - Tyler Durden
|
|
maximus
      
Beiträge: 896
Win XP, Suse 8.1
Delphi 4/7/8 alles prof
|
Verfasst: Do 26.08.04 10:59
JanHH hat folgendes geschrieben: | So hab ich das gelöst.
...
|
Das wäre nicht unbedingt nötig gewesen. Genau die selben funktionen bieten die stream-hifsklassen TReader und TWriter. Und noch vieles mehr.
Ich sag das nur damit damit andere sich das etvl. zu nutze machen können, nicht um diene unit zu kritisieren.
_________________ mfg.
mâximôv
|
|
JanHH 
      
Beiträge: 46
Erhaltene Danke: 1
|
Verfasst: Do 26.08.04 15:40
Ich glaub es gibt ne ganze Menge Hilfsfunktionen in Delphi, von denen man nicht unbedingt was weiss und die man redundanterweise dann selber nachprogrammiert.
Ich kann mich an den ganzen TreeView-Kram gar nicht mehr so genau erinnern und hab das mittlerweile eh verworfen (da ein treeview-speichern unnötig ist in einem vernünftig entworfenen Programm, da das speichern von daten direkt im treeview designmässig ziemlicher Müll ist, allerdings mag es bei eher kleinen Programmen ok sein), aber das Lesen und Schreiben der NodeData-Objekte hängt natürlich vom Objekt an sich ab, insofern kann man das kaum generell beantworten. Am besten, man hat einen Typ TNodeData, ein NodeData ist halt ein myNodeData: TNodeData, und die Klasse hat eine Funktion Read und Write.
Gruß
Jan
|
|
|