![]() |
VirtualTreeView: Es klappt nicht ganz
So, jetzt stosse ich an erste Probleme. Ich habe ein Objekt erzeugt und möchte es an den Baum anhängen. Da läuft er auch durch, aber am Ende (scheinbar bevor er den VTV anzeigen will) kommt eine EAccessViolation. Ich kann den Fehler leider nicht ganz zurückverfolgen, ich weiss nur, dass er aus der VTV Klasse kommt. Delphi bleibt in der Funktion TCustomVirtualStringTree.DoGetText stehen, aber auch nach 2-maligem Durchlauf von TBaseVirtualTree.WndProc. Hier erstmal mein bisheriger Code:
Objekt-Definition:
Delphi-Quellcode:
Neues Objekt wird mit Daten versehen und an den Baum gehangen:
// Ebene 0: Sitzungsdaten
TSessionObject = class private fdatum: integer; fsessiontyp: string; fprotokollant: string; fteilnehmer: string; fverteiler: string; fndatum: integer; published property datum: integer read fdatum write fdatum; property sessiontyp: string read fsessiontyp write fsessiontyp; property protokollant: string read fprotokollant write fprotokollant; property teilnehmer: string read fteilnehmer write fteilnehmer; property verteiler: string read fverteiler write fverteiler; property ndatum: integer read fndatum write fndatum; end;
Delphi-Quellcode:
Die eigentliche Anhänge-Funktion:
// insert new session
procedure insertSession(); var SessionObject: TSessionObject; datum, ndatum: integer; begin Form1.VST1.NodeDataSize:=SizeOf(TTreeData); SessionObject:=TSessionObject.Create; try datum:=DateTimeToUnix(Form1.DateTimePicker1.Date); ndatum:=DateTimeToUnix(Form1.DateTimePicker2.Date); SessionObject.datum:=datum; SessionObject.sessiontyp:=Form1.ComboBox1.Text; SessionObject.protokollant:=Form1.Edit1.text; SessionObject.teilnehmer:=Form1.Edit2.Text; SessionObject.verteiler:=Form1.Edit3.Text; SessionObject.ndatum:=ndatum; AddVSTObject(Form1.VST1,nil,SessionObject); except SessionObject.Free; end; end;
Delphi-Quellcode:
Er läuft da überall ohne Probleme durch, keine Fehlermeldungen, nur am Ende dieses EAccessViolation... vielleicht kennt sich da jemand genauer aus. Danke!
// add new structure into VST
function AddVSTObject(AVST: TCustomVirtualStringTree; ANode: PVirtualNode; AObject: TObject): PVirtualNode; var Data: PTreeData; begin Result:=AVST.AddChild(ANode); AVST.ValidateNode(Result,False); Data:=AVST.GetNodeData(Result); Data^.FObject:=AObject; end; |
Re: VirtualTreeView: Es klappt nicht ganz
probier mal
Delphi-Quellcode:
später drauf zugreifen mit:
vst.addchild(nil, meinobj);
Delphi-Quellcode:
myobj:=TSessionObject(vst.getnodedate(node)^);
|
Re: VirtualTreeView: Es klappt nicht ganz
Die NodeDataSize Eigenschaft hast du aber schon auf die richtige Größe (SizeOf(TTreeData)) gesetzt?
|
Re: VirtualTreeView: Es klappt nicht ganz
Zitat:
|
Re: VirtualTreeView: Es klappt nicht ganz
Argh, hab den Fehler gefunden... hatte im Projekt noch Überbleibsel aus dem Tutorial drin, also so Sachen wie vstFreeNode und VSTGetText, die alle ohne Objekte arbeiteten... ach menno :D Exception ist nun weg. Dann bis zur nächsten Frage ;)
|
Re: VirtualTreeView: Es klappt nicht ganz
Weiter gehts ;)
Ich habe noch nicht viel mit Pointern und Objekten programmiert. Ich will nun die Spalte korrekt beschriften und das Tutorial hat dazu diesen Code:
Delphi-Quellcode:
Leider ist das noch die Geschichte ohne Objekte und ich frage mich nun, wie ich die Eigenschaften meines Objektes ansprechen soll. Irgendwas mit
procedure TForm1.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
Column: Integer; TextType: TVSTTextType; var Text: WideString); var Data: PTreeData; begin Data:=VST.GetNodeData(Node); case Column of 0: Text := Data.FCaption; 1: Text := Data.FColumn1; end; end; Data^.FObject... oder so? |
Re: VirtualTreeView: Es klappt nicht ganz
jupp, du mußt halt nur den DatenTyp ändern Data: PTreeData; ist ja ein Pointer auf 'nen Record und du brauchst Data: TSessionObject; ... eine ObjektVariable ist ja schon ein Pointer auf ein Objekt, also kannst du auch gleich dieses nutzen und mußt es nicht nochmal über 'nen Pointer ansprechen ^^
Und dann halt nur noch Data.FObject... |
Re: VirtualTreeView: Es klappt nicht ganz
Jepp, das klappt :) Danke!
|
Re: VirtualTreeView: Es klappt nicht ganz
So, diesmal finde ich aber gar nichts zu diesem Thema: Ich will meinen Baum nun speichern. Das Tutorial behandelt aber nur das Speichern von Strings und Integer, nicht das Speichern von ganzen Objekten. Wie speichere ich da?
Ein Node kann bei mir 3 verschiedene Objekte beinhalten, die ich aber leicht über die Spalten-Nummer identifizieren kann. Ein Node hat also ein FObject, ich kann doch nicht das ganze Objekt speichern oder? Ich muss doch sicher noch IN das Objekt rein und alle Paramter einzeln speichern, oder gehts doch einfacher? |
Re: VirtualTreeView: Es klappt nicht ganz
Im Grunde muß immer nur der DatenTyp angepasst werden. (abgesehn von den CompilerMagicSachen)
Delphi-Quellcode:
var Data: PString;
var Data: PInteger; ... Wobei der String nicht dem Pointer übergeben wird (Strings und dynamische Arrays sind halt 'ne CompilerMagicSache), sondern der Pointer zum String gemacht wird und diesem dann erst der String gegeben wird (also andersrum). Ein AnsiRING) hat ja eine Interne referenzzähling und wenn nur der Pöinter übergeben wird, dann wird der Referenzzähler nicht erhöht und somit kann der String eventuell vorzeigt freigegeben werden. Wenn dem String z.B. erst in der Prozedur der Inhalt erzeugt/zugewiesen wurde und dann der String auch noch eine Lokale Variable ist, dann wird er ja am Prozedurende freigegeben. Zitat:
Code:
Und der Integer ... für den brauchste nichtma Speicher reservieren, da dieser ja genausogroß wie der Pointer ist:
function AddVSTObject(AVST: TCustomVirtualStringTree; ANode: PVirtualNode;
[b]AString: String[/b]): PVirtualNode; begin Result:=AVST.AddChild(ANode); AVST.ValidateNode(Result,False); Data:=AVST.GetNodeData(Result); [b]PString(Data^.FObject)^ := AString;[/b] end;
Code:
function AddVSTObject(AVST: TCustomVirtualStringTree; ANode: PVirtualNode;
[b]AInteger: Integer[/b]): PVirtualNode; begin Result:=AVST.AddChild(ANode); AVST.ValidateNode(Result,False); Data:=AVST.GetNodeData(Result); [b]Data^.FObject := Pointer(AInteger);[/b] end; |
Re: VirtualTreeView: Es klappt nicht ganz
Das hab ich jetzt nicht so erwartet ;) Nochmal gaaaanz langsam:
Problem 1: Speichern. Wie speichere ich ein Objekt? Übernimmt das Programm die interne Verwaltung der internen Objekt-Daten oder muss ich manuell an die Daten ran? Im Moment speichere ich ganz einfach das Objekt in den Stream. Er macht das auch ohne zu murren und in der Datei auf der Festplatte sehe ich einen der internen Strings (aber nur diesen einen) und jede Menge anderes ASCII-Zeugs, das für mich als Betrachter sicher nicht wichtig ist. Problem 2 stelle ich erst, wenn das Speichern funktioniert ;) |
Re: VirtualTreeView: Es klappt nicht ganz
Du speicherst nur einen Pointer auf das Objekt in den Stream ... das Objekt selber ist nicht in der VTV ... genaus wird es mit dem String gemacht (halt nur der Pointer auf den String in der VTV), nur das man da halt auf den Referenzzähler achten muß.
Du kannst natürlich den String auch in einem ShortString (String[255], odeer so) speichern, wobei der hortString wieder nur ein Record wäre und somit selbst in der VTV landet. PS: hatte oben noch einiges nachgetragen. |
Re: VirtualTreeView: Es klappt nicht ganz
Puh, oje, das ist mir im Moment etwas zu hoch... kannst Du mir vielleicht etwas konkreten Code zu meinem konkreten Objekt (hier ganz oben) geben? :|
|
Re: VirtualTreeView: Es klappt nicht ganz
warum macht ihr alle das so umständlich.
schaut euch bitte von mir nochmal folgenden beispielcode an: ![]() |
Re: VirtualTreeView: Es klappt nicht ganz
zu deinem Projekt steht's ja sozusagen schon dort oben (
![]() Du muß halt bedenken, das dein Objekt ja nicht nur aus einem zusammenhängendem Speicherblock besteht. Var irgendwas: TSessionObject; ist halt nur ein Pointer auf das Objekt und in dem Objekt sind ja auch wiederum Pointer auf andere Dinge (z.B. die Strings). Also, will man jetzt ein Objekt "nur" mit der VTV verbinden, dann stimmt das schon, aber die gesamten Daten eines Objekts in einen Stream zu bekommen ist nicht so leicht ... man kann da eigentlich nur das Objekt zerlegen und eine Kopie der einzelnen ObjektDaten in dem Stream speichern und müßte dann daraus (beim Auslesen) dann wieder ein neues Objekt erstellen. |
Re: VirtualTreeView: Es klappt nicht ganz
@generic: Von OnLoad und OnSave steht da aber nichts drin ;)
Ich probiere nun, die Daten des Objekts einzeln in den Stream zu schreiben (erstmal nur der erste Parameter) mit
Delphi-Quellcode:
Funktioniert soweit. Nur wie bekomme ich die Daten da wieder raus?
// save nodes to disk
procedure TForm1.VST1SaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode; Stream: TStream); var Data: PTreeData; Len: integer; begin Data := VST1.GetNodeData(Node); Len := Length(TSessionObject(Data.FObject).name); Stream.Write(Len, SizeOf(Len)); Stream.write(PChar(TSessionObject(Data.FObject).name)^, Len); end;
Delphi-Quellcode:
Das klappt irgendwie nicht, wegen der markierten Zeile. Wieso?
// load nodes from disk
procedure TForm1.VST1LoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode; Stream: TStream); var Data: PTreeData; Len: integer; begin Data := VST1.GetNodeData(Node); Stream.read(Len, SizeOf(Len)); SetLength(TSessionObject(Data.FObject).name, Len); //<---- Error: Ein Konstantenobjekt kann nicht als Var-Parameter übergeben werden Stream.read(PChar(TSessionObject(Data.FObject).name)^, Len); end; |
Re: VirtualTreeView: Es klappt nicht ganz
Ah, jetzt verstehe ich Dich ;) Dann muss ich das Objekt beim Laden ja neu erstellen durch ein Create. Dann mach ich das mal...
|
Re: VirtualTreeView: Es klappt nicht ganz
OK, jetzt habe ich folgenden Code und eine EAccessViolation:
Delphi-Quellcode:
// save nodes to disk
procedure TForm1.VST1SaveNode(Sender: TBaseVirtualTree; Node: PVirtualNode; Stream: TStream); var Data: PTreeData; Len: integer; begin Data := VST1.GetNodeData(Node); Stream.Write(TSessionObject(Data.FObject).datum, SizeOf(TSessionObject(Data.FObject).datum)); Len := Length(TSessionObject(Data.FObject).name); Stream.Write(Len, SizeOf(Len)); Stream.write(PChar(TSessionObject(Data.FObject).name)^, Len); //[... die restlichen Parameter] end;
Delphi-Quellcode:
Aber wieso die Exception? Irgendwo ein Pointer oder ein Objekt nicht richtig?
// load nodes from disk
procedure TForm1.VST1LoadNode(Sender: TBaseVirtualTree; Node: PVirtualNode; Stream: TStream); var Data: PTreeData; Len,datum,ndatum: integer; SessionObject: TSessionObject; name,sessiontyp,protokollant,teilnehmer,verteiler: string; begin Data := VST1.GetNodeData(Node); SessionObject:=TSessionObject.Create; Stream.Read(datum, SizeOf(datum)); Stream.read(Len, SizeOf(Len)); SetLength(name, Len); Stream.read(name, Len); // [... + der rest] SessionObject.datum:=datum; SessionObject.name:=name; // <--------------- hier kommt die Exception // [... + der rest] Data^.FObject:=SessionObject; end; EDIT: Habe die Zeile markiert, wo die Exc geworfen wird. Muss aber jetzt leider für heute weg. Komme beim nächsten Arbeitstag wieder auf diesen Thread zurück. Danke soweit! :) |
Re: VirtualTreeView: Es klappt nicht ganz
Der Code zum Lesen der String-Var ist nicht ganz richtig. So ist es besser:
Delphi-Quellcode:
Gruß Hawkeye
Stream.read(Len, SizeOf(Len));
SetLength(name, Len); // Stream.read(name, Len); // <-- name ist nur ein Zeiger! if (Len > 0) then Stream.read(name[1], Len); |
Re: VirtualTreeView: Es klappt nicht ganz
Wo kommt den der Fehler und warum willst du unbedingt das gesamte Objekt din haben?
Es ist zumindestens Platzsparender, wenn nur der Pointer in der VTV steht und man bräuchte dann das Objekt nicht zu zerlegen... Aber versuch es mal so:
Delphi-Quellcode:
oder
Stream.Write(TSessionObject(Data.FObject).Name[1], Len);
Stream.Read(TSessionObject(Data.FObject).Name[1], Len);
Delphi-Quellcode:
Ich weiß nicht, ob Write/Read 'nen VAR-Parameter (ersteres), oder 'nen Pointer (zweiteres) will.
Stream.Write(@TSessionObject(Data.FObject).Name[1], Len);
Stream.Read(@TSessionObject(Data.FObject).Name[1], Len); [add] OK, Markierung gesehn ^^ Dann lag es bestimmt an der Stringübergabe (siehe Code) [/add] PS: @generic Na ja, eigentlichj ist es von der interen Speicherung in der VTV her egal wo man die Daten übergibt, aber wie man sie übergibt/ließt ist da schon wichtig.
Delphi-Quellcode:
Hier ist ja mehr das Problem, wie die Daten in der VTV gespeichert/verwaltet sollen.
vst.addchild(nil, [b]Data[/b]);
[i][b]Pointer[/b][/i] := [b]Data[/b]; vst.addchild(nil, [i][b]Pointer[/b][/i]); N := vst.addchild(nil); AVST.SetNodeData(N, [b]Data[/b]); [i][b]Pointer[/b][/i] := [b]Data[/b]; n := vst.addchild(nil); AVST.SetNodeData(N, [i][b]Pointer[/b][/i]); ... // vieles, vieles mehr So, nun noch der wohl einfachere Weg ... das Objekt erzeigen und nur die referenz daruf übergeben:
Delphi-Quellcode:
procedure insertSession();
var SessionObject: TSessionObject; begin SessionObject := TSessionObject.Create; SessionObject.datum:=DateTimeToUnix(Form1.DateTimePicker1.Date); SessionObject.sessiontyp:=Form1.ComboBox1.Text; SessionObject.protokollant:=Form1.Edit1.text; SessionObject.teilnehmer:=Form1.Edit2.Text; SessionObject.verteiler:=Form1.Edit3.Text; SessionObject.ndatum:=DateTimeToUnix(Form1.DateTimePicker2.Date); Result:=Form1.VST1.AddChild(nil, SessionObject); end; Diese kann auch ins OnCreate der Form (falls man es nicht auch direkt im OI ändern kann/will) ... einmal reicht ja, die Größe ändert sich ja nicht (PS: SizeOf(TTreeData) = Pointer = 4 Byte, also 4)
Delphi-Quellcode:
VST1.NodeDataSize:=SizeOf(TTreeData);
Delphi-Quellcode:
procedure deleteSession(Node: PVirtualNode);
var SessionObject: TSessionObject; begin TSessionObject(Form1.VST1.GetNodeData(Node)).Free; Form1.VST1.Delete; // keine Ahnung ob das wirklich so heißt end; Folgendes kann auch ins OnCreate der Form (falls man es nicht direkt im OI ändern kann/will) ... einmal reicht ja, die Größe ändert sich schließlich nicht (PS: SizeOf(TTreeData) = Pointer = 4 Byte, also 4)
Delphi-Quellcode:
VST1.NodeDataSize:=SizeOf(TTreeData);
|
Re: VirtualTreeView: Es klappt nicht ganz
das sieht sehr komplizert aus.
die vcl hat doch noch die klasse tpersistent. warum leitest du nicht von der ab und nutzt die methoden der vcl um objekte in streams zu speichern? (ich muss zugeben mit dem thema habe ich mich noch nicht beschäftigt) |
Re: VirtualTreeView: Es klappt nicht ganz
So, habe heute endlich mal weitermachen können an dem Projekt. Laden und Speichern funktioniert jetzt einwandfrei. Nächstes Problem:
Ich rechtsklicke auf einen Node, um auf einem PopUpMenu "edit" auszuwählen. Dann erscheint ein neues Formblatt in das ich einen neuen Namen für den Knoten eintippe. Dann klicke ich auf Speichern und das Formblatt verschwindet. Der Name des Knotens wird nun im VST aktualisiert und angezeigt. Problem: Solange der Knoten immer noch blau selektiert ist bleibt diese "blaue Box" wie sie vorher war und passt sich nicht der neuen Wortlänge an. Ich muss erst woandershin klicken und zurückklicken, dann ist die "blaue Selektionsbox" so breit wie der Name des Knotens. Versteht Ihr was ich meine? Ich mache sonst maln Screenshot. Gibts irgendwie eine Repaint-Funktion, falls die überhaupt die Lösung wäre? |
Re: VirtualTreeView: Es klappt nicht ganz
da sollte folgendes helfen:
Delphi-Quellcode:
VirtualTreeView.InvalidateNode(Node);
Gruß Dale |
Re: VirtualTreeView: Es klappt nicht ganz
Wann genau soll ich das ausführen? Ich habs jetzt nach der Knotenaktualisierung, aber es passiert nichts.
|
Re: VirtualTreeView: Es klappt nicht ganz
hm, wundert mich, dass es nicht funktioniert. Versuchs mal mit:
Delphi-Quellcode:
ansonsten solltest du mal den Quelltext posten.
VirtualTreeView.BeginUpdate;
... // Knoten aktualisieren; ... VirtualTreeView.EndUpdate; Gruß Dale |
Re: VirtualTreeView: Es klappt nicht ganz
Hier etwas Code, es klappt immernoch nicht:
Delphi-Quellcode:
procedure updateSession;
var Data: PTreeData; begin Form1.VST1.BeginUpdate(); Data := Form1.VST1.GetNodeData(currentNode); TSessionObject(Data.FObject).datum:=datetimetounix(Form1.DateTimePicker1.date); [...] Form1.VST1.EndUpdate(); Form1.VST1.InvalidateNode(currentNode); end;
Delphi-Quellcode:
currentNode ist global und enthält die aktuell selektierte Node.
procedure TForm2.Button1Click(Sender: TObject);
begin if editmode then updatePoint else insertPoint; saveTree; editmode:=false; close; end; |
Re: VirtualTreeView: Es klappt nicht ganz
Stimmt alles was du geschrieben hast,
es hilft nur folgendes: (ich habs getestet)
Delphi-Quellcode:
Gruß Dale
VirtualStringTree.BeginUpdate;
... VirtualStringTree.ReinitNode(CurrentNode, False); VirtualStringTree.EndUpdate; |
Re: VirtualTreeView: Es klappt nicht ganz
So, da bin ich wieder. Hab den Vorschlag mit dem ReInitNode getestet, aber es wirkt sich immernoch nicht positiv auf das Programm aus. Habe auch mal dieses RepaintNode ausprobiert, aber es klappt nicht. Vielleicht liegt der Fehler woanders, ich schau nochmal rein...
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:57 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