AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein GUI-Design mit VCL / FireMonkey / Common Controls Delphi Fehlerhafter Zugriff auf NodeData von VirtualStringTree
Thema durchsuchen
Ansicht
Themen-Optionen

Fehlerhafter Zugriff auf NodeData von VirtualStringTree

Ein Thema von Hobbycoder · begonnen am 13. Mär 2017 · letzter Beitrag vom 14. Mär 2017
Antwort Antwort
Hobbycoder

Registriert seit: 22. Feb 2017
930 Beiträge
 
#1

Fehlerhafter Zugriff auf NodeData von VirtualStringTree

  Alt 13. Mär 2017, 17:09
Ich mache grade meine ersten Schritt mit dem VirtualStringTree. Das Konzept ist ja nicht wirklich schlecht.
Nun bin ich aber auf ein Problem gestoßen, bei dem ich nicht wirklich weiter komme.

Ich will Dateien per Drag-and-Drop erfassen und für die weitere Verwendung erst mal ein einem VirtualStringTree darstellen. Das klapp auch sehr gut. Für die Daten die zu den Dateien gehören erstelle ich mir ein Objekt mit den Properties, die ich brauche. Auch keine Problem.
Jetzt will ich in einem Formular dem Anwender die Möglichkeit geben, diese Properties noch zu ändern, damit die weitere Verarbeitung individueller wird.
Aber wenn ich dann auf mein Datenobjekt zugreife, bekomme ich recht merkwürdige Daten, die Teilweise dann zu einer Exception führen.

Hier mal ein bischen Code:

Das Datenobject:
Delphi-Quellcode:
  TTreeData = class
  private
    FFilename: string;
    FIconIndex: Integer;
    FDokumentenType: TDokumentenTyp;
    FFGST: Boolean;
    FTermin: Boolean;
    FKennzeichen: Boolean;
    FFiletype: string;
    FDebitor: Boolean;
    FFGSTText: string;
    FKennzeichenText: string;
    FDebitorText: string;
    procedure SetDebitor(const Value: Boolean);
    procedure SetDokumentenType(const Value: TDokumentenTyp);
    procedure SetFGST(const Value: Boolean);
    procedure SetFiletype(const Value: string);
    procedure SetKennzeichen(const Value: Boolean);
    procedure SetTermin(const Value: Boolean);
    procedure SetDebitorText(const Value: string);
    procedure SetFGSTText(const Value: string);
    procedure SetKennzeichenText(const Value: string);
  public
    constructor Create(const AFilename: string; const Kennzeichen, FGST, Debitor, Termin: Boolean; const KennzeichenText, FGSTText, DebitorText: string; const DokumentenTyp: TDokumentenTyp);
    destructor Destroy; override;
    property IconIndex: Integer read FIconIndex write FIconIndex;
    property Filename: string read FFilename write FFilename;
    property Filetype: string read FFiletype write SetFiletype;
    property Kennzeichen: Boolean read FKennzeichen write SetKennzeichen;
    property KennzeichenText: string read FKennzeichenText write SetKennzeichenText;
    property FGST: Boolean read FFGST write SetFGST;
    property FGSTText: string read FFGSTText write SetFGSTText;
    property Debitor: Boolean read FDebitor write SetDebitor;
    property DebitorText: string read FDebitorText write SetDebitorText;
    property Termin: Boolean read FTermin write SetTermin;
    property DokumentenType: TDokumentenTyp read FDokumentenType write SetDokumentenType;
  end;
Das erstellen von Nodes:
Delphi-Quellcode:
var
  node: PVirtualNode;
  I: Integer;
begin
  for I := 0 to Files.Count-1 do
  begin
    node:=vrtlstrngtr1.AddChild(nil, TTreeData.Create(Files[i], chk_kennzeichen.Checked, chk_vin.Checked, chk_debitor.Checked, chk_termin.Checked, edt_kennzeichen.Text, edt_vin.Text, edt_debitor.Text, TDokumentenTyp(rg_dokumententyp.ItemIndex)));
  end;
Dazugehörig der Constructor von TTreeData:
Delphi-Quellcode:
constructor TTreeData.Create(const AFilename: string; const Kennzeichen, FGST, Debitor, Termin: Boolean; const KennzeichenText, FGSTText, DebitorText: string; const DokumentenTyp: TDokumentenTyp);
begin
  FFilename := AFilename;
  FKennzeichen:=Kennzeichen;
  FKennzeichenText:=KennzeichenText;
  FFGST:=FGST;
  FFGSTText:=FGSTText;
  FDebitor:=Debitor;
  FDebitorText:=DebitorText;
  FTermin:=Termin;
  FDokumentenType:=DokumentenTyp;
  FIconIndex := -1;
end;
Bis hier hin läuft das sehr gut und vor allem Fehlerfrei.

Das VirtualStringTree liegt auf einem Formular, wo der Anwender dann die Properties zu den TTreeData-Objekten einstellen können soll.
Das geschieht folgendermaßen:
Delphi-Quellcode:
  if vrtlstrngtr1.TotalCount>0 then
  begin
    Node := vrtlstrngtr1.GetFirst();
    if (vrtlstrngtr1.SelectedCount=vrtlstrngtr1.TotalCount) or (vrtlstrngtr1.SelectedCount=0) then
    begin
      while Assigned(Node) do
      begin
        Data := vrtlstrngtr1.GetNodeData(Node);
        Data.Kennzeichen:=chk_kennzeichen.Checked;
        Data.KennzeichenText:=edt_kennzeichen.Text;
        Data.FGST:=chk_vin.Checked;
        Data.FGSTText:=edt_vin.Text;
        Data.Debitor:=chk_debitor.Checked;
        Data.DebitorText:=edt_debitor.Text;
        Data.DokumentenType:=TDokumentenTyp(rg_dokumententyp.ItemIndex);
        Node := vrtlstrngtr1.GetNext(Node);
      end;
    end else begin
      while Assigned(Node) do
      begin
        if vrtlstrngtr1.Selected[Node] then
        begin
          Data := vrtlstrngtr1.GetNodeData(Node);
          Data.Kennzeichen:=chk_kennzeichen.Checked;
          Data.KennzeichenText:=edt_kennzeichen.Text;
          Data.FGST:=chk_vin.Checked;
          Data.FGSTText:=edt_vin.Text;
          Data.Debitor:=chk_debitor.Checked;
          Data.DebitorText:=edt_debitor.Text;
          Data.DokumentenType:=TDokumentenTyp(rg_dokumententyp.ItemIndex);
        end;
        Node := vrtlstrngtr1.GetNext(Node);
      end;
    end;
    vrtlstrngtr1.Refresh;
  end;
Hier treten nun Fehler auf. Im Debugger kann ich sehen, dass z.B. im TreeData.Filename beim Hinzufügen der Nodes der korrekte Dateiname steht.
Wenn ich aber wieder darauf zugreife (siehe letzter Codeabschnitt), dann bekomme einen gänzlich anderen Speicherbereich, der in das TreeData-Objekt geladen wird. Und das knallt.

Da ich zum ersten mal mit VirtualStringTree arbeite bin ich mir nicht ganz sicher, ob ich richtig auf die Daten zugreife. Also meine Frage: Wo mache ich den Fehler?

Gruß Hobbycoder
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
930 Beiträge
 
#2

AW: Fehlerhafter Zugriff auf NodeData von VirtualStringTree

  Alt 13. Mär 2017, 18:08
Auch hier liefere ich mir selbst die Antwort:

der Zugriff auf das TreeDataObject war falsch. Richtig muss das lauten: Data := TTreeData(vrtlstrngtr1.GetNodeData(Node)^);
  Mit Zitat antworten Zitat
Aviator

Registriert seit: 3. Jun 2010
1.610 Beiträge
 
Delphi 10.3 Rio
 
#3

AW: Fehlerhafter Zugriff auf NodeData von VirtualStringTree

  Alt 13. Mär 2017, 20:27
Hallo Hobbycoder,

ich finde es super, dass du nicht aufgibst und auch immer weiter probierst und testest. Nur so kann man beim Programmieren was schaffen.

Ich würde dir für den Anfang mal die Advanced VirtualTree Demo ans Herz legen die mit dem VirtualTreeView mitkommt. Da kann man sehr viel lernen. Es ist allerdings eher eine Einstiegsdemo auch wenn sie Advanced heißt. Gewisse Dinge sollte man anders lösen als es in der Demo gemacht wird.

Hier mal noch ein paar Tipps wie du deinen eigenen Source verbessern kannst:
  • Benutze anstatt von vst.AddChild() lieber die RootNodeCount Property des VirtualTrees. Die trifft das virtuelle etwas besser, da immer nur die Nodes initialisiert werden, die aktuell sichtbar sind oder bereits sichtbar waren. Für den Anfang kann man zwar AddChild() verwenden, aber das solltest du dir besser nicht angewöhnen. Hier verweise ich immer gerne auf den Kommentar von Mike Lischke der über der AddChild() Function in der VirtualTrees.pas zu finden ist.
    Delphi-Quellcode:
    procedure TForm1.InitTree;
    begin
      vst1.RootNodeCount := 10; // <-- Hier die Anzahl der benötigten Nodes auf der RootEbene eintragen
    end;
    Hinzu kommt, dass du dann das Event OnInitNode() und OnInitChildren() benutzen musst, um die entsprechenden Nodes zu initialisieren. Du kannst ja einmal selbst ausprobieren, ob du weiter kommst. Falls nicht, lass es uns wissen und wir helfen dir. Es kann natürlich immer etwas dauern bis mal jemand zurückschreiben kann. Also nicht direkt nervös werden.
  • Verwende Pointer anstelle von harten Casts
    Delphi-Quellcode:
    PTreeData = ^TTreeData;

    procedure TForm1.vst1GetText(Sender: TBaseVirtualTree; Column: TColumnIndex; Node: PVirtualNode; CellText: string); // <-- Der Header könnte ein paar Fehler haben, habe ich jetzt mal so aus dem Kopf runtergetippt (glaube hier fehlt noch StaticText oder sowas
    var
      NodeData: PTreeData;
    begin
      NodeData := Sender.GetNodeData(Node);
      case Column of
        0: CellText := NodeData^.DebitorText;
        1: CellText := NodeData^.FGSTText;
      end;
    end;
  • Ein weiterer Tipp der bei mir bisher immer gut funktioniert hat: Verwende immer (wie das in der VirtualTreeView Komponente auch gemacht wird) die Bezeichner Node für PVirtualNode und NodeData für PTreeData (oder auch einen anderen Datentyp). Der Vorteil ist, dass du immer die gleichen Bezeichner hast und dich nicht jedes Mal aufs Neue ärgerst, dass dir der Bezeichner gerade nicht einfällt.
  • Verwende das OnGetNodeDataSize Event um die NodeDataSize festzulegen. Das ist vor allem bei Records wichtig, da die in der Größe variieren. Bei Zeigern auf eine Objektinstanz ist es soweit ich weiß nicht zwingend erforderlich, da der DefaultWert auf 4 Byte gesetzt wurde. Hier könnte es bei 64-bit Kompilaten zu Problemen kommen.
    Delphi-Quellcode:
    procedure TForm1.vst1OnGetNodeDataSize(Sender: TBaseVirtualTree; var NodeDataSize: Integer);
    begin
      NodeDataSize := SizeOf(TTreeData); // Achtung: Hier nicht den Pointer sondern den eigentlichen Record bzw. die Klasse verwenden
    end;
  • Schau dir mal die Methoden TVirtualStringTree.GetNext, TVirtualStringTree.GetNextSelected, TVirtualStringTree.GetNextVisible, TVirtualStringTree.GetNextChecked usw. an. Analog dazu gibt es auch TVirtualStringTree.GetPrevious, ... Methoden. Somit musst du bspw. nicht selbst prüfen, ob eine Node gerade Selected ist. Der Tree durchsucht sich selbst und gibt dir die entsprechende Node zurück. Spart dir einiges an Tipparbeit und geht meistens auch ein Ticken schneller.

So jetzt fällt mir langsam nichts mehr ein was ich dir für deinen Source bzw. genrell für Tipps für den VST geben kann. Ich hoffe du kannst damit etwas anfangen.

PS: Und nicht beleidigt sein, dass nicht innerhalb von einer Stunde jemand geantwortet hat. Gerade um die Zeit sind nicht mehr so viele auf der Arbeit und haben Feierabend und wollen nichts mehr vom Programmieren wissen.
  Mit Zitat antworten Zitat
Ghostwalker

Registriert seit: 16. Jun 2003
Ort: Schönwald
1.299 Beiträge
 
Delphi 10.3 Rio
 
#4

AW: Fehlerhafter Zugriff auf NodeData von VirtualStringTree

  Alt 14. Mär 2017, 06:38
und noch ein Tipp:

Wenn du größere Änderungen an der Struktur bzw. den Daten des Treeviews machen mußt, solltest
du TreeView.BeginUpdate und TreeView.EndUpdate verwenden. Das verhindert das neuzeichnen des Treeviews
während du die Struktur/Daten änderst und vermeided so Flacker-Effekte.
Uwe
e=mc² or energy = milk * coffee²
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
930 Beiträge
 
#5

AW: Fehlerhafter Zugriff auf NodeData von VirtualStringTree

  Alt 14. Mär 2017, 06:58
Danke für eure Tipps. Ich werde das berücksichtigen.
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#6

AW: Fehlerhafter Zugriff auf NodeData von VirtualStringTree

  Alt 14. Mär 2017, 09:56
Wenn du größere Änderungen an der Struktur bzw. den Daten des Treeviews machen mußt, solltest
du TreeView.BeginUpdate und TreeView.EndUpdate verwenden. Das verhindert das neuzeichnen des Treeviews
während du die Struktur/Daten änderst und vermeided so Flacker-Effekte.
Und verhindert zudem, dass OnGetText und Konsorten aufgerufen werden, bevor du dein NodeData vollständig initialisiert hast (für den Fall, dass du beim manuellen Hinzufügen der Nodes per AddChild bleibst).
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  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 17:02 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