AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi TreeView gibt über Data nicht die richtigen Daten zurück
Thema durchsuchen
Ansicht
Themen-Optionen

TreeView gibt über Data nicht die richtigen Daten zurück

Ein Thema von the_real_didi · begonnen am 21. Sep 2006 · letzter Beitrag vom 21. Sep 2006
Antwort Antwort
the_real_didi

Registriert seit: 7. Jul 2004
Ort: Reinsdorf/Vielau
5 Beiträge
 
#1

TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 08:36
Hallo,

Also ich habe eine Treeview, die ich aus einer Datenbank mit Werten füttere.
in der .Data Property speichere ich die ID des Datensatzes und eine Variable Typ.


Code:
 TDatensatz = record
    ID: string;
    Typ: integer;
  end;


Dazu hab ich eine globale Variable


Code:
 Datensatz: ^TDatensatz;


Das füllen der Treeview ist auch noch kein Problem.


Code:
 procedure TForm1.wsgQuery1AfterScroll(DataSet: TDataSet);
var node2, node3: TTreenode;

 
begin
  treeview1.Items.Clear;
  treeview1.Items.BeginUpdate;
  while not wsgquery2.Eof do
  begin
    node2 := treeview1.Items.Addchild(nil, wsgQuery2.fieldByName('Bezeichnung').asstring);
    node2.ImageIndex := 2;
    node2.SelectedIndex := 0;
    node2.Selected := true;
    New(Datensatz);
    Datensatz^.Typ := 2;
    Datensatz^.ID := wsgQuery2.FieldByName('ID_Gewerk').AsString;
    Node2.Data := Datensatz;
    while not wsgquery3.Eof do
    begin
      node3 := treeview1.Items.Addchild(node2, wsgQuery3.fieldByName('Anfragedatum').asstring);
      node3.ImageIndex := 3;
      node3.SelectedIndex := 0;
      node3.Selected := true;
      New(Datensatz);
      Datensatz^.Typ := 3;
      Datensatz^.ID := wsgQuery3.FieldByName('ID_Anfrage').AsString;
      Node3.Data := Datensatz;
      wsgQuery3.next
    end;
    wsgQuery2.next;
  end;
  treeview1.Items.EndUpdate;
  treeview1.FullExpand;
end;


Die Probleme bekomme ich, wenn ich die Daten des gerade gewählten abfragen will.
Mit der folgenden Variante bekomme ich zwar ergebnisse, aber es sind nicht die richtigen. Der Typ wird nicht korrekt ausgegeben. Ich habe das gefühl, dass die Daten vom letzten bearbeiteten Knoten stammen. aber nicht vom aktuell gewählten.

Code:
procedure TForm1.TreeView1Changing(Sender: TObject; Node: TTreeNode;
  var AllowChange: Boolean);
begin
if assigned(Datensatz) then
begin
  showmessage('JETZT!!! Changing');
  showmessage(inttostr(Datensatz^.Typ));
  if Datensatz^.Typ = 2 then
  begin
    showmessage(Datensatz^.ID);
  end;
end
else
begin
  showmessage('Noch nicht ...');
end;
end;



Und wenn ich es so Versuche, bekomme ich eine Zugriffsverletzung


Code:
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
if assigned(Datensatz) then
begin
  showmessage('JETZT!!! Change');
  showmessage(inttostr(TDatensatz(treeview1.selected.Data^).Typ));
  if Datensatz^.Typ = 2 then
  begin
    showmessage(Datensatz^.ID);
  end;
end
else
begin
  showmessage('Noch nicht ...');
end;
end;


Mein Ziel ist eigentlich beim anklicken eines Knotens auf den dazugehörigen Datensatz zu springen. Das sollte dann auch kein Problem sein, wenn mir die Treeview die richtigen Daten bereit stellt.

Bitte helft mir.
Danke
Cowboys from Heaven
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#2

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 08:43
NodeChanging wird vor dem Knotenwechsel aufgerufen (wie in der Hilfe angegeben). Der erste Code bezieht sich dabei überhaupt nicht auf den Node der dann gewählt wird, sondern nur auf die globale Variable Datensatz (warum die auch immer global ist?? Die Daten hängen am Knoten, da braucht man nichts globales -> sofort als lokale Variable anlegen!). Bei dem zweiten Versuch vorher einfach mal abfragen, ob Node und Data überhaupt ungleich Nil ist, und somit überhaupt Werte enthalten können. Node kann auch Nil sein, wenn ein aktueller Knoten deselektiert wird und genauso gut kann ein Knoten (z.B. Root-Knoten?) ausgewählt werden, welcher überhaupt keine Daten von dem Query enthält und somit auch ein leeres Data besitzt.

Auch empfehle ich, lieber so auf Data zu zu greifen:
Delphi-Quellcode:
PDatensatz = ^TDatensatz;
TDatensatz = record
    ID: string;
    Typ: integer;
  end;

...

ShowMessage(PDatensatz(Node.Data)^.ID);
Vor allem erstmal die globale Variable entfernen! Die birgt viel mehr Fehlerquellen und Unsicherheiten als Vorteile (wie man vllt. gerade sieht)
  Mit Zitat antworten Zitat
Sascha L

Registriert seit: 4. Jun 2004
Ort: Hamm
390 Beiträge
 
Delphi 2006 Professional
 
#3

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 08:59
Hi,

du kannst dafür keine globale Variable nehmen, dann ist doch klar, dass immer das, was zuletzt eingetragen wurde, ausgelesen werden kann.

Du musst erstmal eine lokale Variable vom Typ PDatensatz einführen.

Im Code schreibste dann folgendes:

Delphi-Quellcode:
New(myPDatensatzVariable);
myPDatensatzVariable^.Irgendwas := BlabBla;
Am besten hängst du diese dann mit AddChildObject an die einzelnen Nodes ran.
Sascha
  Mit Zitat antworten Zitat
the_real_didi

Registriert seit: 7. Jul 2004
Ort: Reinsdorf/Vielau
5 Beiträge
 
#4

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 09:02
Danke euch für die schnelle Antwort. werde es gleich ausprobieren
Cowboys from Heaven
  Mit Zitat antworten Zitat
the_real_didi

Registriert seit: 7. Jul 2004
Ort: Reinsdorf/Vielau
5 Beiträge
 
#5

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 09:31
Aslo, ich glaube, ich habe es hin bekommen.

Habe Datensatz: ^TDatensatz; in der Funktion deklariert, in der die Daten der einzelnen Knoten erstellt werden. --> lokal!

Delphi-Quellcode:
procedure TForm1.wsgQuery1AfterScroll(DataSet: TDataSet);
var node2, node3: TTreenode;
 Datensatz: ^TDatensatz;
Und ich habe die Procedure Treeview1Change folgendermaßen geändert:

Delphi-Quellcode:
procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
if node <> nil then
begin
  if node.data <> nil then
  begin
  showmessage('JETZT!!! Change');
  showmessage(inttostr(TDatensatz(node.Data^).Typ));
  if TDatensatz(node.Data^).Typ = 2 then
  begin
    showmessage(TDatensatz(node.Data^).ID);
  end;
  end
  else
  begin
    showmessage('Knoten enthält keine Daten');
  end;
end
else
begin
  showmessage('Kein Knoten gewählt');
end;
end;

@Muetze1: Das einzige wo ich noch nicht so mit komme ist die Sache mit den Pointern. Zum einen warum ich die lokale Variable eigentlich so richtig brauche. Die ist nach dem Ende der Procedure weg und die Daten sind noch da. Die hängen am Knoten, das ist klar, aber was macht dann die Variable Datensatz?
Zum anderen hab ich die Bedeutung dieses ^-Zeichens noch nicht ganz verstanden. Und auf was sie sich bezieht. Du meintest ja, dass ich auf die Daten so zugreifen soll.

showmessage(TDatensatz(node.Data)^.ID); Da meckerte Delphi, dass es eine ungültige Typumwandlung sei. Deshalb habe ich einfach dieses Zeichen in die Klammern rein genommen, dann hat es funktioniert. Ich weiß aber nicht warum. Hab ich da noch irgendwo n Fehler in ner Deklaration?
Cowboys from Heaven
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#6

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 11:27
Zitat von the_real_didi:
@Muetze1: Das einzige wo ich noch nicht so mit komme ist die Sache mit den Pointern. Zum einen warum ich die lokale Variable eigentlich so richtig brauche. Die ist nach dem Ende der Procedure weg und die Daten sind noch da. Die hängen am Knoten, das ist klar, aber was macht dann die Variable Datensatz?
Du hast nach meiner Deklaration 2 Typen: TDatensatz und PDatensatz. TDatensatz deklariert einen Typ der direkt diese beiden Variablen deines Records beinhaltet. Der Typ PDatensatz ist ein Typ, welcher nur einen Zeiger darstellt. Dabei definiert er auch gleich, wie der Speicher aussieht, auf den er zeigt: er sieht aus wie TDatensatz. Wenn du nun eine lokale Variable deklarierst, dann wird dort lokal speicher für diese reserviert, welcher nach verlassen der Procedure wieder anders gebraucht wird und daher nicht fest ist. Wenn du nun eine lokale Variable vom Typ TDatensatz lokal anlegst, dann würden die Zuweisungen auf ID und Typ lokal gespeichert werden und verloren gehen bei verlassen der Procedure, da der Speicher direkt reserviert wird im lokalen Bereich. Wenn du nun eine Variable vom Typ PDatensatz lokal deklarierst, dann wird dort nur der Speicherplatz für einen Zeiger vorgehalten. Dieser zeigt auf irgendeine Adresse im Speicher. Um die Variable nutzen zu können, muss der Zeiger auf einen gültigen und alloziierten Speicher zeigen, dieses geschieht durch New(). New() weiss durch den Typ, wieviel Speicher alloziieren muss (die Grösse von TDatensatz auf die die Variable ja zeigt). Die Adresse wo im Speicher er Platz bekommen hat, trägt New() in die PDatensatzvariable ein. Beim Verlassen der Procedure wird zwar der Speicherplatz des Zeigers wieder anders genutzt, aber der mit New() alloziierte Speicher nicht - dieser wird erst durch einen Aufruf von Dispose wieder freigegeben. Um dies später noch zu können und auch später noch auf die dort abgelegten Daten zugreifen zu können, müssen wir uns diesen Zeiger merken. Dies erledigen wir, in dem wir die Adresse der Daten (mehr ist ein Zeiger nicht - es ist nur eine Adresse, wo man was findet - eine reine Adressangabe im Speicher) in der Data Eigenschaft des Nodes zuweisen und der merkt sich die. Wohin er zeigt und wie gross der Bereich ist auf den er zeigt oder was für Daten dort liegen, ist der Data Eigenschaft nicht bekannt (untypisierter Zeiger (Pointer), er weiss nur, er merkt sich eine Adresse, aber nicht auf welchen Typ er zeigt, also welchen Aufbau die Daten dort haben).

Zitat von the_real_didi:
Zum anderen hab ich die Bedeutung dieses ^-Zeichens noch nicht ganz verstanden. Und auf was sie sich bezieht. Du meintest ja, dass ich auf die Daten so zugreifen soll.
Und da kommen wir nun dazu: Das ^ dereferenziert. Also ein Pointer merkt sich eine Adresse - eine Stelle im Speicher. Du musst nun aber dem Compiler sagen, ob du nun bei Zuweisungen diese Adresse haben willst, die sich der Pointer merkt, oder ob du auf den Speicherbereich zugreifen willst, wohin er zeigt (also an die gemerkte Adresse selber). Das geschieht durch die Angabe von dem ^. Wenn du ein ^ hinten anhängst, dann greifst du an die Stelle zu, wohin der Zeiger zeigt und nicht auf die gemerkte Adresse selber. Und da ist nun die Sache mit den untypisierten Zeigern und den typisierten. PDatensatz ist ein typisierter Zeiger, da er gleich mit definiert wie die Datenstruktur aussieht (wie TDatensatz) an der Stelle wo er hinzeigt. Ein Pointer weiss dies nicht und daher
wird beim Zugriff der Typ Pointer auf den Pointer-Typ PDatensatz umgewandelt und schon weiss der Compiler auch wie die Daten dort aussehen und das es dort auch ID und Typ gibt.
  • Node.Data = Adresseablage - merkt sich eine Adresse
  • Node.Data^ = spricht die Daten an, die an der gemerkten Adresse liegen (dereferenzieren). Typ ist hier unbekannt (da Pointer)
  • PDatensatz(Node.Data) = Adressablage - merkt sich die Adresse und weiss durch den Typ, wie die Daten an der Adresse organisiert sind
  • PDatensatz(Node.Data)^ = das ist nun vom Typ TDatensatz, da er durch die Dereferenzierung (^) an die Stelle geht, welche an der in Node.Data abgelegten Adresse liegen. Durch den Typ wissen wir, dass dort TDatensatz liegt...

Zitat von the_real_didi:
showmessage(TDatensatz(node.Data)^.ID);
Dann schau nochmal hin und kopier nochmal richtig. Ich hatte PDatensatz geschrieben, also den typisierten Zeiger genommen.

Zitat von the_real_didi:
Da meckerte Delphi, dass es eine ungültige Typumwandlung sei. Deshalb habe ich einfach dieses Zeichen in die Klammern rein genommen, dann hat es funktioniert. Ich weiß aber nicht warum. Hab ich da noch irgendwo n Fehler in ner Deklaration?
Nein, aber mit dem ^ innerhalb der Klammer derefenzierst du ja (also sprichst die Daten an, die an der Stelle liegen, welche in Node.Data per Adresse beschrieben wird). Und dann sagst du dem Compiler, dass die Daten and er Speicherstelle wie TDatensatz aufgebaut ist.
  Mit Zitat antworten Zitat
the_real_didi

Registriert seit: 7. Jul 2004
Ort: Reinsdorf/Vielau
5 Beiträge
 
#7

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 12:36
Ah, so langsam komme ich dahinter.

korrigiere mich, wenn ich falsch liege.

Also:
Die Variable PDatensatz ist ein Pointer auf TDatensatz, meinen record, und wird direkt nach dem Wort "type" deklariert.
Delphi-Quellcode:
type
  PDatensatz = ^TDatensatz;
  TDatensatz = record
    ID: string;
    Typ: integer;
  end;

  TForm1 = class(TForm)
    ...
Und dann funktioniert das auch so, wie du das beschrieben hast. Ich hatte die Variable Datensatz: ^TDatensatz innerhalb einer procedure deklariert. Dann hat es nicht funktioniert.
Kann bei der Methode, wie ich sie verwendet habe irgendwas schief laufen oder könnte man das genau so gut so machen?

Reicht eigentlich die Kontrolle
if node.Data <> nil then um zu überprüfen, ob ich auf data zugreifen kann, oder muß ich noch nachschauen, ob wirklich daten an der Adresse liegen?
Cowboys from Heaven
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#8

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 13:04
Zitat von the_real_didi:
Die Variable PDatensatz ist ein Pointer auf TDatensatz, meinen record, und wird direkt nach dem Wort "type" deklariert.
Delphi-Quellcode:
type
  PDatensatz = ^TDatensatz;
  TDatensatz = record
    ID: string;
    Typ: integer;
  end;

  TForm1 = class(TForm)
    ...
Ja, richtig. Mit Type werden Typendeklarationen eingeleitet. Mit PDatensatz und TDatensatz deklarierst du einen neuen Datentyp. Und PDatensatz ist ein Typ in Form eines Zeigers auf TDatensatz, richtig.

Zitat von the_real_didi:
Und dann funktioniert das auch so, wie du das beschrieben hast. Ich hatte die Variable Datensatz: ^TDatensatz innerhalb einer procedure deklariert. Dann hat es nicht funktioniert.

Kann bei der Methode, wie ich sie verwendet habe irgendwas schief laufen oder könnte man das genau so gut so machen?
Damit ersparst du dir nur die Typendeklaration. Du deklarierst den Typen hier direkt bei der Variablendeklaration. Einziger Unterschied: du gibst dem neuen Kind (Typ) keinen Namen. Da ist aber eine Deklaration eines Typs besser geeignet, weil Delphi an unterschiedlichen Stellen deklarierte Typen als unterschiedliche Typen unterscheidet - auch wenn Ihre Deklarationen gleich aussehen. Daher wäre die Verwendung von PDatensatz innerhalb aller Routinen besser geeignet als ^TDatensatz.

Zitat von the_real_didi:
Reicht eigentlich die Kontrolle
if node.Data <> nil then um zu überprüfen, ob ich auf data zugreifen kann, oder muß ich noch nachschauen, ob wirklich daten an der Adresse liegen?
Naja, grundlegend schon, aber wenn das Wort "aber" nicht wäre. Grundlegend ist die Eigenschaft Data von TTreeNode Nil, d.h. im Normalfall klappt dies. Wenn du nun aber einem Knoten die Data Eigenschaft wieder entfernst, sprich du gibst den Speicher wieder mit Dispose() frei, dann müsstest du darauf achten, danach die Data Eigenschaft wieder auf Nil zu setzen, da dies nicht automatisch geschieht und weil sonst deine Abfragen fehlschlagen würden. Ob es ein gültiger Speicherbereich ist, auf den Data zeigt, kannst du so nicht nachprüfen, daher ist der Programmierer verpflichtet für richtige Werte zu sorgen.

Hinweis: Um ein Speicherleck zu vermeiden, musst du beim Löschen der Knoten vorher noch den mit New() alloziierten Speicher mit Dispose() wieder frei zu geben. Die TTreeNodes machen dies nicht, da sie nicht wissen was du dort zuweist. Du könntest die Eigenschaft auch für irgendwelche andere Dinge missbrauchen.
  Mit Zitat antworten Zitat
the_real_didi

Registriert seit: 7. Jul 2004
Ort: Reinsdorf/Vielau
5 Beiträge
 
#9

Re: TreeView gibt über Data nicht die richtigen Daten zurück

  Alt 21. Sep 2006, 13:15
Danke, ihr habt mir wirklich gut weiter geholfen. Die Treeview steht soweit.
Cowboys from Heaven
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:42 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz