Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Datenhaltung mit VirtualStringTree und Trennung der Businesslogik (https://www.delphipraxis.net/200152-datenhaltung-mit-virtualstringtree-und-trennung-der-businesslogik.html)

jus 24. Mär 2019 17:57

Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Hallo,

ich versuche seit einiger Zeit ein paar von meinen Programmen zwischen GUI und Businesslogik aufzutrennen. Da kommt man dann vermutlich um die Begriffe wie MVVM oder generell MV* Sachen nicht mehr herum. Mich hat das MVVM Modell irgendwie eher zugesagt. Ich muß zu zugeben, dass ich noch nicht soweit bin, dass ich irgendwie konform mit dem MVVM Modell bin. Fürs Erste versuche ich mal das Programm auf 3 Schichten (GUI, Verbindungsschicht zwischen GUI und Businesslogik, Businesslogik) aufzutrennen. Im Prinzip speichere ich die benötigten Daten der GUI Komponenten(TEdit.Text, TCombox.Itemindex,...) in der Verbindungsschicht um diese bei Bedarf weiterverbeiten zu können.
Da in in meinen Programmen aber öfters auch mal VirtualStringTree einsetze bin ich auf ein grundlegendes Dilemma für mich gestossen und hätte gerne Eure Expertenmeinung benötigt. Und zwar enthält der VST implizit die sämtliche Daten, die man dann mit "VSTIrgendwas.GetNodeData(Node)" aufruft. Bei mir ist es so, dass der VST-Node ein einfaches Datenrecord enthält. Doch irgendwie widerspricht es doch irgendwie meinem Verständnis, dass die GUI Komponente alle Daten beherbergt. Sollte es nicht eigentlich eher die darunterliegenden Schichten sein, die alle Daten enthalten und kennen? :gruebel:
Derzeit mache ich es so, dass ich bei OnChange Ereignis von VST den Record von aktuell ausgewählten Node in die Verbindungsschicht mitschicke.
Doch meine Frage ist, wir würde ihr das machen?

Lg,
jus

Aviator 24. Mär 2019 19:59

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
MVVM ist ja mit Delphi wohl nicht so schön möglich wie bspw. mit C# und WPF. Zumindest gibt es hier im Forum diverse Threads darüber.

Bezüglich der Datenhaltung solltest du dir
Delphi-Quellcode:
TObjectList<T>
anschauen. Dort werden deine Instanzen gespeichert. Die Node erhält dann als NodeData nur noch einen Pointer oder alternativ den ItemIndex das Objekt in der Liste. Zumindest mache ich das so und das hat sich auch bewährt. Die Daten können auch ohne Tree existieren und man kann problemlos mit ihnen arbeiten.

Ghostwalker 25. Mär 2019 04:49

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Hi,

vor einiger Zeit stand ich vor einem Ähnlichen Problem mit VST, weshalb ich mir eine Klasse geschrieben hat,
die, die Daten in der gleichen Art und Weise handhabt, wie VST, diese aber eben nur verwaltet und nix mit der
Darstellung zu tun hat.

Ist vielleicht auch für dein Problem etwas (https://bitbucket.org/urupprecht/datatree) :)

jus 25. Mär 2019 23:36

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Ghostwalker (Beitrag 1428599)
Hi,

vor einiger Zeit stand ich vor einem Ähnlichen Problem mit VST, weshalb ich mir eine Klasse geschrieben hat,
die, die Daten in der gleichen Art und Weise handhabt, wie VST, diese aber eben nur verwaltet und nix mit der
Darstellung zu tun hat.

Ist vielleicht auch für dein Problem etwas (https://bitbucket.org/urupprecht/datatree) :)

@Ghostwalter: Vielen Dank für die Unit. Die muß ich mir in Ruhe mal anschauen.

jus 25. Mär 2019 23:38

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Aviator (Beitrag 1428584)
Bezüglich der Datenhaltung solltest du dir
Delphi-Quellcode:
TObjectList<T>
anschauen. Dort werden deine Instanzen gespeichert. Die Node erhält dann als NodeData nur noch einen Pointer oder alternativ den ItemIndex das Objekt in der Liste. Zumindest mache ich das so und das hat sich auch bewährt. Die Daten können auch ohne Tree existieren und man kann problemlos mit ihnen arbeiten.

Hallo Aviator, könntest du mir bitte ein Beispiel zeigen, wie man die Objekte von einer ObjectList mit VST Nodedaten verknüpft?

DieDolly 26. Mär 2019 00:14

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Ich bin mir nicht sicher aber ich finde die oben genannte Klasse macht alles nur noch viel komplizierter. Besonders für jemanden der noch nie was mit VST und paralleler Datenhaltung dafür zu tun hatte.
Mit einer ganz einfachen eventuell generischen TObjectList und 1 oder 2 Helferprozeduren, wenn überhaupt welche..., braucht man diese Klasse nicht.

Selbstgeschriebener Code ist noch immer am besten. Den den versteht man auch am besten.

Nur als kleines Beispiel, warum die Klasse eigentlich nicht notwendig ist.
Delphi-Quellcode:
function TVirtualNode.IsAssigned: Boolean;

// Returns False if this node is nil, True otherwise

begin
  Exit(@Self <> nil);
end;
Das da findet man schon von Haus aus im VST Code. Andere Sachen sicherlich auch.

mkinzler 26. Mär 2019 08:02

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Ghostwalker (Beitrag 1428599)
Ist vielleicht auch für dein Problem etwas (https://bitbucket.org/urupprecht/datatree) :)

Oder https://github.com/davidberneda/GenericTree

Aviator 26. Mär 2019 12:55

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Also eine sehr abgespeckte Variante wäre diese hier. (Aus dem Kopf geschrieben und auch nicht vollständig). Damit weist du der Node eine Objekt Instanz aus einer ObjectList zu. Dieses Objekt muss natürlich mit dem Index auch existieren. Beim Sortieren sollte sogar dann auch nichts passieren, da die Referenz ja bleibt. Es wird nur im Init Event einmal auf die ObjectList zugegriffen und das Item genommen, das mit dem NodeIndex übereinstimmt.

Delphi-Quellcode:
procedure TForm1.VSTInitNode(Sender: TBaseVirtualTree; Node: PVirtualNode; var InitialStates: TVTInitialStates);
var
  NodeData: PNodeData;
begin
  NodeData := Sender.GetNodeData(Node);
  NodeData^ := ObjectList[Node^.Index];
end;

Stevie 26. Mär 2019 13:43

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Wir nutzen dafür schon seit Jahren erfolgreich den TTreeViewPresenter aus DSharp. Der Übernimmt die Interaktion zwischen Daten (funktioniert über Spring4D IObjectList), Darstellungslogik (data template steuert, was wie ausgegeben und wie Daten strukturiert sind) und dem TVirtualTreeView.

DieDolly 26. Mär 2019 14:22

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Ich würde es so machen

1. Datenklasse
Delphi-Quellcode:
unit Datenklasse;

interface

type
 TNodeData = class(TClass)
 private
  FText: string;
 public
   property Text: string read FText write SetText;
 end;

type
 TNodeDataList = TObjectList<TNodeData>;

implementation

procedure TNodeData.SetText(const Value: string);
begin
 FText:= 'Hallo ' + Value + '!';
end;

end.
2. Instanz(variable) der generischen Datenklassenliste bereitstellen und Datenklassenliste erzeugen
Delphi-Quellcode:
var // irgendwo ...
 NodeDataList: TNodeDataList;

// FormCreate oder so
NodeDataList := NodeDataList.Create(True);
3. Daten der Liste hinzufügen
Delphi-Quellcode:
var
 NodeData: TNodeData;
begin
 NodeData := TNodeData.Create;
 NodeData.Text := 'jus';

 NodeDataList.Add(NodeData);
 VST.RootCount := NodeDataList.Count;
4. Node (löscht automatisch Datenklasseninstanz)
Delphi-Quellcode:
NodeDataList.Delete(2);
5. Node im VST initialisieren
Delphi-Quellcode:
procedure TNodeEvents.VSTInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
begin
 Node.SetData<TNodeData>(NodeDataList[Node.Index]);
end;
6. Node-Daten anzeigen
Delphi-Quellcode:
procedure TNodeEvents.VSTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType; var CellText: string);
var
 NodeData: TNodeData;
begin
 NodeData := Node.GetData<TNodeData>;
 if not Assigned(NodeData) then
  Exit;

 case Column of
  0: CellText := NodeData.Text;
 end;
end;

Codehunter 26. Mär 2019 14:37

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
@DieDolly: Huch, seit wann kann denn der VST generische Daten? Ich nutz den schon soooo lange, dass ich immer noch mit klassischen Records arbeite. Geht auch, aber dein Beispiel würde hier auch vieles sehr vereinfachen. Irgendwie ist dieses Feature komplett an mir vorbei gegangen.

DieDolly 26. Mär 2019 14:41

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Die OOP/generischen Ansätze gingen schon mehrmals hier durch die Praxis. Wie man sieht, jetzt schon wieder.
Ich benutze das VST nur noch so und es ist so unglaublich viel einfacher als vorher.

Man braucht keine riesen Helferklassen im Hintergrund. Mein Beispiel ist das was man braucht, um seine Daten getrennt vom VST halten und managen zu können.
Natürlich kommen noch mehr Properties und Helferprozeduren hinzu. Je nachdem was man denn braucht. Aber das ist dann noch immer einfacher als mit den hier erwähnten Helferklassen.

Codehunter 26. Mär 2019 14:58

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von DieDolly (Beitrag 1428770)
Man braucht keine riesen Helferklassen im Hintergrund. Mein Beispiel ist das was man braucht, um seine Daten getrennt vom VST halten und managen zu können.
Natürlich kommen noch mehr Properties und Helferprozeduren hinzu. Je nachdem was man denn braucht. Aber das ist dann noch immer einfacher als mit den hier erwähnten Helferklassen.

Eben eben! Das hier landet letztendlich auch im VST. Da habe ich derzeit einen Record wo ein Pointer drin ist auf eine Objektinstanz. Im Grunde will ja der TE auch genau sowas machen. Wäre mit Generics ungleich einfacher.

Allerdings, wenn man mit dem VST noch nicht viel Erfahrung hat, kann einen das völlig erschlagen, dieses Featuremonster. Egal ob mit Records oder Generics. Man kann auch viele Vorteile zunichte machen, zum Bsp. wenn man iterativ Add() statt ChildCount verwendet. Wobei letzteres wiederum tricky ist bei der Initialisierung, wenn man keine indizierten Listen hat (z.B. TDictionary).

DieDolly 26. Mär 2019 15:12

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Ganz einfach erklärt kann man sich das so vorstellen.

Man stelle sich einen Node im VST vor. Jeder dieser Nodes hat nun als Datengrundlage eine komplette Instanz der TNodeData-Klassendaten.
In der Natur vergleichbar mit einer Goldfruchtpalme. Jeder Strang ist ein Node und jeder Node hat eine Klasseninstanz mit allen Properties (die Blätter am Strang).

Ich habe mich damit anfangs auch sehr schwer getan. Und eben deswegen habe ich dann auch erst Add() und sowas benutzt was das Ziel Daten von GUI trennen natürlich verfehlte.
Irgendwann habe ich mich hingesetzt und einfach Code aus der Praxis abgeschrieben. Der Lerneffekt kommt dann irgendwann von alleine, wenn man mal selber eine listenbasierte Datenhaltung benötigt.

Codehunter 26. Mär 2019 15:26

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Wobei man der Versuchung widerstehen muss, seinen Datenobjekten in irgendeiner Form einen PVirtualNode als Property zu verpassen. Das geht schnell, dass man dann wieder versucht die Visualisierung aus den Datenobjekten heraus zu steuern.

Ich nutze den VST jedenfalls bevorzugt als Grid. Da gibt es IMHO nichts effizienteres. Schon gar nicht solche Schwergewichte wie z.B. DevExpress. Wenn man die Daten ohnehin schon irgendwo im Speicher rumliegen hat, braucht man die nicht noch drölfzig Mal hin und her alloziieren bis man sie mal auf den Bildschirm gepinselt hat.

generic 26. Mär 2019 16:55

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Ich werfe mal einfach folgendes zur Diskussion bei:

Meine Geschäftslogik ruft Emails ab. D.h. ich habe eine Liste von Emails.

Wenn ich diese Emails nun zur Anzeige bringen will, kommt die Darstellungsfrage auf.

D.h. möchte ich diese z.B. nach Datum, Betreff oder Thread sortiert/gruppiert sehen.

Abhängig davon würde ich jetzt anfangen meinen VST mit Knoten und Unterknoten zu erstellen. Die Knotendaten selbst sind aber nur ein Zeiger auf das Objekt in meiner Liste.

Objekte verlinken hatte ich u.a. 2007 im Stammtisch #2 gezeigt:
https://www.delphipraxis.net/712774-post4.html

Aviator 26. Mär 2019 21:39

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Codehunter (Beitrag 1428768)
@DieDolly: Huch, seit wann kann denn der VST generische Daten? Ich nutz den schon soooo lange, dass ich immer noch mit klassischen Records arbeite. Geht auch, aber dein Beispiel würde hier auch vieles sehr vereinfachen. Irgendwie ist dieses Feature komplett an mir vorbei gegangen.

Also ich würde mal sagen das es das schon seit 4 Jahren gibt. :lol: https://github.com/Virtual-TreeView/...c4f91c0a096aeb

Ich selbst benutze die Generics auch kaum bis gar nicht. Ich arbeite nur noch mit Objekt Instanzen in einer TObjectList<T>. Und da noch ein
Delphi-Quellcode:
PClass = ^TClass
dazu zuschreiben stört mich weniger.

DieDolly 26. Mär 2019 21:42

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Ich selbst benutze die Generics auch kaum bis gar nicht. Ich arbeite nur noch mit Objekt Instanzen in einer TObjectList<T>. Und da noch ein PClass = ^TClass dazu zuschreiben stört mich weniger.
Ist mein Code auf der vorherigen Seite so wie du das schreiben würdest? Bin gerade etwas verwirrt, weil du sagst du benutzt keine Generics.

Aviator 26. Mär 2019 22:26

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von DieDolly (Beitrag 1428829)
Zitat:

Ich selbst benutze die Generics auch kaum bis gar nicht. Ich arbeite nur noch mit Objekt Instanzen in einer TObjectList<T>. Und da noch ein PClass = ^TClass dazu zuschreiben stört mich weniger.
Ist mein Code auf der vorherigen Seite so wie du das schreiben würdest? Bin gerade etwas verwirrt, weil du sagst du benutzt keine Generics.

Hmm ja. Das war vielleicht etwas blöd ausgedrückt. Ich meinte, dass ich die generischen Funktionen des VST wie
Delphi-Quellcode:
GetNodeData<T>
nicht benutze. Ich nutze eigentlich immer die standard Funktionen. Wahrscheinlich auch, weil ich eine Zeit lang kompatibel zu alten Versionen geblieben bin.

jus 27. Mär 2019 15:42

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Liste der Anhänge anzeigen (Anzahl: 1)
Vielen Dank an die Mitwirkenden, besonders Aviator und DieDolly. Leider habe ich immer noch Delphi 2007 im Einsatz darum gehen keine Generics.
Anbei der Ansatz für Leute, die noch ein altes Delphi wie ich einsetzen. Ich hoffe, dass ich da keinen logischen Fehler drin habe.

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Contnrs, StdCtrls, VirtualTrees;

type
  TNodeDataClass = class
  private
    FSpalte1: string;
    FSpalte2: string;
    FSpalte3: Integer;
  public
  property Spalte1: string read FSpalte1 write FSpalte1;
  property Spalte2: string read FSpalte2 write FSpalte2;
  property Spalte3: Integer read FSpalte3 write FSpalte3;
  end;

  PTreeData = ^TTreeData;
  TTreeData = record
    FObject : TObject;
  end;

  TForm1 = class(TForm)
    vst: TVirtualStringTree;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure vstInitNode(Sender: TBaseVirtualTree; ParentNode,
      Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
    procedure vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  private
    { Private-Deklarationen }
    DataList: TObjectList;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  Node: PVirtualNode;
begin
  VST.NodeDataSize := SizeOf(TTreeData);
  VST.RootNodeCount := DataList.Count;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  NodeData: TNodeDataClass;
begin
  DataList := TObjectList.Create;

  for i := 0 to 3 do
  begin
    NodeData := TNodeDataClass.Create;
    NodeData.Spalte1 := IntToStr(i);
    NodeData.Spalte2 := 'Spalte '+IntToStr(i);
    NodeData.Spalte3 := i;
    DataList.Add(NodeData);
  end;
end;

procedure TForm1.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
  data: PTreeData;
begin
  data:= Sender.GetNodeData(Node);

  case Column of
    0: CellText := TNodeDataClass(data^.FObject).FSpalte1;
    1: CellText := TNodeDataClass(data^.FObject).FSpalte2;
    2: CellText := IntToStr(TNodeDataClass(data^.FObject).FSpalte3);
  end;
end;

procedure TForm1.vstInitNode(Sender: TBaseVirtualTree; ParentNode,
  Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  data: PTreeData;
begin
  data := Sender.GetNodeData(Node);
  data^.FObject := DataList[Node.Index];
end;

end.
Lg,
jus

DieDolly 27. Mär 2019 16:57

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zur Sicherheit würde ich immer einfügen ...
Delphi-Quellcode:
data:= Sender.GetNodeData(Node);

if not Assigned(data) //
 Exit; //
Zitat:

Leider habe ich immer noch Delphi 2007 im Einsatz darum gehen keine Generics.
Delphi Rio Community Edition womöglich eine Lösung?

jus 27. Mär 2019 22:19

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von DieDolly (Beitrag 1428910)
Zur Sicherheit würde ich immer einfügen ...
Delphi-Quellcode:
data:= Sender.GetNodeData(Node);

if not Assigned(data) //
 Exit; //
Zitat:

Leider habe ich immer noch Delphi 2007 im Einsatz darum gehen keine Generics.
Delphi Rio Community Edition womöglich eine Lösung?

yep... hast recht, sollte noch rein :thumb:

DieDolly 27. Mär 2019 22:24

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Und der Vollständigkeit halber, so gehts auch

Delphi-Quellcode:
data:= Sender.GetNodeData(Node);

if not data.IsAssigned then
 Exit;
Geschmackssache, was man wählt.

jus 27. Mär 2019 22:46

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von DieDolly (Beitrag 1428949)
Und der Vollständigkeit halber, so gehts auch

Delphi-Quellcode:
data:= Sender.GetNodeData(Node);

if not data.IsAssigned then
 Exit;
Geschmackssache, was man wählt.

Ich vermute, dass es bei den neueren VST den Befehl "data.IsAssigned" gibt. Bei meiner alten VST kennt er den Befehl noch nicht. Was bei meiner alten VST gerade noch aufgefallen ist, ist, dass mit der Prüfung von folgenden Befehl nicht getan ist, da es immer ASSIGNED ist:
Delphi-Quellcode:
data:= Sender.GetNodeData(Node);
if not Assigned(data) then Exit;
Es müßte eigentlich heissen, oder zumindest bei meiner alten VST Version unter Delphi 2007:
Delphi-Quellcode:
data:= Sender.GetNodeData(Node);
if not Assigned(data^.FObject) then Exit;
Lg,
jus

jus 27. Mär 2019 23:50

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Liste der Anhänge anzeigen (Anzahl: 1)
So zur späten Stunde noch eine neue Version mit einer kleinen Automatisierung beim Verknüpfen von VST mit TObjectList, indem ich eine neue Methode mit TBindVSTToObjectListHelperClass.BindVST einführe.

Die Code in TForm1.Button1Click ist ein bisschen kürzer geworden und TForm1.vstInitNode wurde entfernt.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, Contnrs, StdCtrls, VirtualTrees, VSTBindObjectListUnit;

type
  TNodeDataClass = class
  private
    FSpalte1: string;
    FSpalte2: string;
    FSpalte3: Integer;
  public
  property Spalte1: string read FSpalte1 write FSpalte1;
  property Spalte2: string read FSpalte2 write FSpalte2;
  property Spalte3: Integer read FSpalte3 write FSpalte3;
  end;

  TForm1 = class(TForm)
    vst: TVirtualStringTree;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  private
    { Private-Deklarationen }
    DataList: TObjectList;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  TBindVSTToObjectListHelperClass.BindVST(VST,DataList);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  NodeData: TNodeDataClass;
begin
  DataList := TObjectList.Create;

  for i := 0 to 3 do
  begin
    NodeData := TNodeDataClass.Create;
    NodeData.Spalte1 := IntToStr(i);
    NodeData.Spalte2 := 'Spalte '+IntToStr(i);
    NodeData.Spalte3 := i;
    DataList.Add(NodeData);
  end;
end;

procedure TForm1.vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
  data: PTreeData;
begin
  data:= Sender.GetNodeData(Node);
  if not Assigned(data^.FObject) then Exit;

  case Column of
    0: CellText := TNodeDataClass(data^.FObject).FSpalte1;
    1: CellText := TNodeDataClass(data^.FObject).FSpalte2;
    2: CellText := IntToStr(TNodeDataClass(data^.FObject).FSpalte3);
  end;
end;

end.
Dafür wurde das Verknüpfen in VSTBindObjectListUnit.pas ausgelagert.
Delphi-Quellcode:
unit VSTBindObjectListUnit;

interface

uses Contnrs, VirtualTrees;

type

  PTreeData = ^TTreeData;
  TTreeData = record
    FObject : TObject;
  end;

  TBindVSTToObjectListHelperClass = class
  private
    class var fObList: TObjectList;
    class var fVST: TVirtualStringTree;
    class procedure VstInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
  public
    class procedure BindVST(AVST: TVirtualStringTree; AObList: TObjectList);
  end;

implementation

{ TBindVSTToObjectListHelperClass }

class procedure TBindVSTToObjectListHelperClass.BindVST(AVST: TVirtualStringTree; AObList: TObjectList);
begin
  if not assigned(AObList) then exit;
  fObList := AObList;
  fVST := AVST;
  fVST.Clear;
  fVST.NodeDataSize := SizeOf(TTreeData);
  fVST.OnInitNode := TBindVSTToObjectListHelperClass.VstInitNode;
  fVST.RootNodeCount := fObList.Count;
end;

class procedure TBindVSTToObjectListHelperClass.VstInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
  data: PTreeData;
begin
  data := fVST.GetNodeData(Node);
  data^.FObject := fObList[Node.Index];
end;

end.
Wie schon vorher erwähnt für die Leute die noch ein Delphi 2007 im Einsatz haben.

Lg,
jus

DieDolly 28. Mär 2019 00:09

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Denk dran RootNodeCount neu zu setzen, wenn du einen Eintrag aus deiner ObjectList löschst.
Ich glaube jedenfalls, dass das notwendig ist.

generic 1. Apr 2019 12:36

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Code:
  PTreeData = ^TTreeData;
  TTreeData = record
    FObject : TObject;
  end;
Ich sehe immer diesen Quelltext.

Wir sehen ein Zeiger auf ein Objekt welches in einen Record gespeichert wird.
Also ist der Record genau so groß wie der Zeiger.
Wieso speichert Ihr den Objekt Zeiger nicht direkt im VST?
Dann braucht ihr die Zwischenklasse TTreeData nicht.

Code:
vst.NodeDataSize= sizeof(TMeinObjekt); // Zeigergröße - es geht auch TObject da alle Objektzeiger gleich groß sind.

vst.addChild(nil, MeinObjektInsatz); // Hinzufügen mit Zeigers


var
  data: TMeinObjekt;
begin
  data := TMeinObjekt(fVST.GetNodeData(Node)^);
end;

DieDolly 1. Apr 2019 13:46

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
AddChild benutzt man nicht. Das ist eine Todsünde, so wie ich das immer lese.

Wenn man AddChild benutzt, dann kann man auch gleich ein TListView benutzen mit Add().

Codehunter 1. Apr 2019 16:58

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von generic (Beitrag 1429220)
Wieso speichert Ihr den Objekt Zeiger nicht direkt im VST?
Dann braucht ihr die Zwischenklasse TTreeData nicht.

Ich vermute, das sind Altlasten. Lange Zeit ging das beim VST gar nicht anders. Die alten Tutorials im Netz haben leider kein Verfallsdatum. Mir ging das ganz genauso: Ich nutze den VST schon seit den allerersten Tagen und hab eben die alten Vorgehensweisen verinnerlicht. Dass er inzwischen auch Generics kann, hab ich auch erst jetzt mitbekommen...

Zitat:

Zitat von DieDolly (Beitrag 1429225)
AddChild benutzt man nicht. Das ist eine Todsünde, so wie ich das immer lese.

Wenn man AddChild benutzt, dann kann man auch gleich ein TListView benutzen mit Add().

Naja ganz so schlimm ist es nun auch wieder nicht. Kommt ganz auf den Einzelfall an. Wenn man eine Struktur von ein paar Dutzend Nodes hat (wie. z.B. in der Delphi-IDE die Projektverwaltung), dann ist es völlig Wumpe ob man iterativ mit AddChild arbeitet oder mit Root/Childcount. Im Zweifel opfere ich da sogar ein paar Millisekunden Runtime zugunsten lesbareren Codes.

Bei Anwendungen wie z.B. HeidiSQL, wo der VST hingegen ein Grid für viele Tausende Datensätze bilden kann, da macht das absolut Sinn. Wobei ich mich an der Stelle schon wieder Frage, ob die Vorteile die man mit Rootnodecount gewinnt, nicht durch das ganze Generics-RTTI-Gedönse wieder drauf geht.

jus 1. Apr 2019 19:03

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Codehunter (Beitrag 1429255)
Zitat:

Zitat von generic (Beitrag 1429220)
Wieso speichert Ihr den Objekt Zeiger nicht direkt im VST?
Dann braucht ihr die Zwischenklasse TTreeData nicht.

Ich vermute, das sind Altlasten. Lange Zeit ging das beim VST gar nicht anders. Die alten Tutorials im Netz haben leider kein Verfallsdatum. Mir ging das ganz genauso: Ich nutze den VST schon seit den allerersten Tagen und hab eben die alten Vorgehensweisen verinnerlicht. Dass er inzwischen auch Generics kann, hab ich auch erst jetzt mitbekommen...

Yep, ich habe mich bisher immer an das Tutorial gehalten. Vermutlich würde es auch direkt funktionieren. :oops:

Zitat:

Zitat von Codehunter (Beitrag 1429255)
Zitat:

Zitat von DieDolly (Beitrag 1429225)
AddChild benutzt man nicht. Das ist eine Todsünde, so wie ich das immer lese.

Wenn man AddChild benutzt, dann kann man auch gleich ein TListView benutzen mit Add().

Naja ganz so schlimm ist es nun auch wieder nicht. Kommt ganz auf den Einzelfall an. Wenn man eine Struktur von ein paar Dutzend Nodes hat (wie. z.B. in der Delphi-IDE die Projektverwaltung), dann ist es völlig Wumpe ob man iterativ mit AddChild arbeitet oder mit Root/Childcount. Im Zweifel opfere ich da sogar ein paar Millisekunden Runtime zugunsten lesbareren Codes.

Bei Anwendungen wie z.B. HeidiSQL, wo der VST hingegen ein Grid für viele Tausende Datensätze bilden kann, da macht das absolut Sinn. Wobei ich mich an der Stelle schon wieder Frage, ob die Vorteile die man mit Rootnodecount gewinnt, nicht durch das ganze Generics-RTTI-Gedönse wieder drauf geht.

Davon würde ich gerne mehr erfahren wollen? Also der Grund, warum man nicht AddChild verwenden soll, ist nur die Performance oder gibt es noch andere Gründe? Gab es Performancetests dazu?

DieDolly 1. Apr 2019 19:06

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Davon würde ich gerne mehr erfahren wollen? Also der Grund, warum man nicht AddChild verwenden soll, ist nur die Performance oder gibt es noch andere Gründe? Gab es Performancetests dazu?
Wenn man eine Liste benutzt und die Daten dort hinterlegt, die das VST dann auslesen kann, hat man einen großen Vorteil. Die Daten sind vom Control getrennt und man kann auf sie zugreifen, ohne auf das Control zugreifen zu müssen. Aus Threads heraus ist das eine feine Sache.

Aviator 1. Apr 2019 21:52

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von jus (Beitrag 1429272)
Davon würde ich gerne mehr erfahren wollen? Also der Grund, warum man nicht AddChild verwenden soll, ist nur die Performance oder gibt es noch andere Gründe? Gab es Performancetests dazu?

Ein Grund ist sicherlich die Performance. Der Tree initialisiert gewisse Dinge erst dann, wenn sie benötigt werden. Und das passiert nur, wenn
Delphi-Quellcode:
RootNodeCount
bzw.
Delphi-Quellcode:
ChildCount
benutzt wird. Bei AddChild hingegen wird die Node direkt initialisiert. Zudem ist es mit
Delphi-Quellcode:
RootNodeCount
soweit ich weiß auch möglich, die NodeDaten erst dynamisch und zu dem Zeitpunkt zu laden an dem sie benötigt werden (eben virtuell).

Zum Schluss noch ein kleiner Auszug aus dem VirtualTreeView SourceCode zur
Delphi-Quellcode:
AddChild()
Methode.

Zitat:

Zitat von VirtualTreeView
// Adds a new node to the given parent node. This is simply done by increasing the child count of the
// parent node. If Parent is nil then the new node is added as (last) top level node.
// UserData can be used to set the first SizeOf(Pointer) bytes of the user data area to an initial value which can be used
// in OnInitNode and will also cause to trigger the OnFreeNode event (if <> nil) even if the node is not yet
// "officially" initialized.
// AddChild is a compatibility method and will implicitly validate the parent node. This is however
// against the virtual paradigm and hence I dissuade from its usage.


Codehunter 2. Apr 2019 07:10

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Aviator (Beitrag 1429287)
Zudem ist es mit
Delphi-Quellcode:
RootNodeCount
soweit ich weiß auch möglich, die NodeDaten erst dynamisch und zu dem Zeitpunkt zu laden an dem sie benötigt werden (eben virtuell).

Also das wird ja schon durch das von dir zitierte Komment widerlegt :-) Genauer gesagt durch die überladene Methode AddChild, die es mit und ohne UserData gibt.

Ich will hier ja keinesfalls für die Verwendung von AddChild sprechen. Aber es gibt eben Anwendungsfälle, wo man mit dem Weg über ChildCount erhebliche Verrenkungen machen muss. Als grobe Orientierung kann man evtl. die Anzahl der zu erzeugenden Nodes nehmen. Oder auch die Frage, ob man mit Listen arbeitet oder nicht.

Außerdem möchte ich noch darauf hinweisen, dass es die Methoden BeginUpdate und EndUpdate gibt. Dadurch kann man Initialisierungen direkt durch AddChild reduzieren. Außerdem lässt man den Rechenaufwand durch Initialisierung mit dem Gebrauch von ChildCount nicht einfach weg. Vielmehr geschieht er dann "wenn er gebraucht wird". Was in der Regel bedeutet, dass er bei der Visualisierung initialisiert wird. Das kann sogar von Nachteil sein, wenn die Initialisierung rechenintensiv ist und der Anwender viel vom Scrolling Gebrauch macht.

Aviator 2. Apr 2019 21:28

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Codehunter (Beitrag 1429298)
Zitat:

Zitat von Aviator (Beitrag 1429287)
Zudem ist es mit
Delphi-Quellcode:
RootNodeCount
soweit ich weiß auch möglich, die NodeDaten erst dynamisch und zu dem Zeitpunkt zu laden an dem sie benötigt werden (eben virtuell).

Also das wird ja schon durch das von dir zitierte Komment widerlegt :-) Genauer gesagt durch die überladene Methode AddChild, die es mit und ohne UserData gibt.

Please elaborate! Also diesen Satz von dir verstehe ich nicht so ganz. Habe mir den jetzt 3 mal durchgelesen und bin noch so schlau wie am Anfang. :oops:

DieDolly 2. Apr 2019 21:38

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zudem ist es mit RootNodeCount soweit ich weiß auch möglich, die NodeDaten erst dynamisch und zu dem Zeitpunkt zu laden an dem sie benötigt werden (eben virtuell).
Ist das nicht schon standardmäßig so?

Wenn mein VST 1.000.000 Nodes hat und aktuell wegen der kleinen Größe des Fensters nur 20 angezeigt werden, dann sollten doch eigentlich auch nur diese 20 initialisert werden. Oder nicht?


Edit
habe das gerade geprüft. Es werden alle auf einen schlag initialisiert. Egal ob aktuell sichtbar oder nicht.

jus 16. Apr 2019 15:01

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Sorry, dass ich den Beitrag wieder aufwärme, aber bin auf ein weiteres Problem gestossen. Also der Ansatz mit der der TObjectList zur Verwaltung der Nodedaten in den vorigen Beträgen, wenn diese keine weiteren Unterebenen besitzen, funktioniert sehr gut. Doch wie sieht es dann aus, wenn die Nodes unterebenen besitzen? :gruebel:

lg,
jus

Aviator 16. Apr 2019 15:28

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Dann gibt es in deiner
Delphi-Quellcode:
TObjectList<T>
eine zusätzliche Property die ebenfalls eine
Delphi-Quellcode:
TObjectList<T>
oder ein anderer ContainerType ist. Die ChildNodes verweisen dann auf einen Eintrag in dieser Liste. Wenn die Daten vom gleichen Typ sind (wie z.B. bei einer Verzeichnisstruktur), dann ist das recht einfach, da rekursiv. Bedenke, dass du dann im
Delphi-Quellcode:
Constructor
ein
Delphi-Quellcode:
Create
des ChildContainers und im
Delphi-Quellcode:
Destruktor
ein
Delphi-Quellcode:
Free
ausführen solltest.

jus 16. Apr 2019 17:29

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Aviator (Beitrag 1430429)
Dann gibt es in deiner
Delphi-Quellcode:
TObjectList<T>
eine zusätzliche Property die ebenfalls eine
Delphi-Quellcode:
TObjectList<T>
oder ein anderer ContainerType ist. Die ChildNodes verweisen dann auf einen Eintrag in dieser Liste. Wenn die Daten vom gleichen Typ sind (wie z.B. bei einer Verzeichnisstruktur), dann ist das recht einfach, da rekursiv. Bedenke, dass du dann im
Delphi-Quellcode:
Constructor
ein
Delphi-Quellcode:
Create
des ChildContainers und im
Delphi-Quellcode:
Destruktor
ein
Delphi-Quellcode:
Free
ausführen solltest.

ok, das NodeObjekt um ein TObjectList zu erweitern sollte kein Problem darstellen. Doch wenn es nun Unterebenen gibt. Sollte eigentlich das Objekt, das die Nodedaten beinhaltet auch eine Referenz auf das überliegende Nodedaten-Objekt haben? Würdet ihr sowas machen reingeben?

Derzeit OHNE mehrere Unterebenen schaut es bei mir so aus:
Delphi-Quellcode:
unit Unit1;

interface

//....
type
  TNodeDataClass = class
  private
    FSpalte1: string;
    FSpalte2: string;
    FSpalte3: Integer;
  public
  property Spalte1: string read FSpalte1 write FSpalte1;
  property Spalte2: string read FSpalte2 write FSpalte2;
  property Spalte3: Integer read FSpalte3 write FSpalte3;
  end;

  TForm1 = class(TForm)
    vst: TVirtualStringTree;
    procedure FormCreate(Sender: TObject);
    procedure vstInitNode(Sender: TBaseVirtualTree; ParentNode,
      Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
    procedure vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  private
    DataList: TObjectList; //<--- hier die NodeObjekte von TNodeDataClass
  end;

var
  Form1: TForm1;

implementation
Nach der Erweiterung von VST Nodes MIT mehreren Unterebenen:
Delphi-Quellcode:
unit Unit1;

interface

//....
type
  TNodeDataClass = class
  private
    FParentNode: TNodeDataClass;  // <------ eine Referenz auf oberen Knoten notwendig????????
    FSpalte1: string;
    FSpalte2: string;
    FSpalte3: Integer;
    FSubNodeDataList: TObjectList; // <- Für eventuelle Subnodes
  public
  property Spalte1: string read FSpalte1 write FSpalte1;
  property Spalte2: string read FSpalte2 write FSpalte2;
  property Spalte3: Integer read FSpalte3 write FSpalte3;
  end;

  TForm1 = class(TForm)
    vst: TVirtualStringTree;
    procedure FormCreate(Sender: TObject);
    procedure vstInitNode(Sender: TBaseVirtualTree; ParentNode,
      Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
    procedure vstGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
      Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
  private
    RootNodeDataList: TObjectList; //<--- hier die NodeObjekte von TNodeDataClass
  end;

var
  Form1: TForm1;

implementation

Aviator 16. Apr 2019 17:33

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Naja du könntest so etwas machen:

Delphi-Quellcode:
type
  TNodeDataClass = class
  private
    FParentNodeData: TNodeDataClass;
    FSpalte1: string;
    FSpalte2: string;
    FSpalte3: Integer;
  public
    constructor Create(AParent: TNodeDataClass); override;
    property Spalte1: string read FSpalte1 write FSpalte1;
    property Spalte2: string read FSpalte2 write FSpalte2;
    property Spalte3: Integer read FSpalte3 write FSpalte3;
  end;



implementation

constructor TNodeDataClass.Create(AParent: TNodeDataClass);
begin
  inherited Create;

  FParentNodeData := AParent;
end;

generic 2. Mai 2019 08:15

AW: Datenhaltung mit VirtualStringTree und Trennung der Businesslogik
 
Zitat:

Zitat von Codehunter (Beitrag 1429255)
Zitat:

Zitat von generic (Beitrag 1429220)
Wieso speichert Ihr den Objekt Zeiger nicht direkt im VST?
Dann braucht ihr die Zwischenklasse TTreeData nicht.

Ich vermute, das sind Altlasten. Lange Zeit ging das beim VST gar nicht anders.

Ich finde nicht, dass das Altlasten sind. Ich finde das ist brillant umgesetzt.
Wenn die Knotenanzahl fest steht, dann der VST direkt den gesamten benötigten Speichern mit einer Anfrage belegen. Durch die Ereignisse können dieser dann "on demand" befüllt werden.

Also von der Geschwindigkeit her - hervorragend.
Ich persönlich habe das aber nie genutzt, weil es die Software imho schwieriger macht.
Man muss ggf. seine DB-Komponente globaler haben und die so lange offen lassen bis alles geladen ist.
Callbacks sind eh nicht jedermanns Stärke.

Zitat:

Zitat von DieDolly (Beitrag 1429225)
AddChild benutzt man nicht. Das ist eine Todsünde, so wie ich das immer lese.
Wenn man AddChild benutzt, dann kann man auch gleich ein TListView benutzen mit Add().

Das habe ich noch nie gelesen.
AddChild hat nur den Nachteil, dass jeweils nur für ein Element der Speicher geholt wird.
Hast du mal ein Link für mich, wo ICH mich schlauer machen kann?


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:54 Uhr.
Seite 1 von 2  1 2      

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 by Thomas Breitkreuz