Autor Beitrag
Schatten^parker
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Fr 14.02.03 22:07 
Hi leute,
ich habe wie immer wenn ich mich an euch wende ein problem!

zum verständnis:
ich schreibe ein programm dass eine klasse, tmitglied mit den eigenschaften .vorname .nachname blablabla, in eine liste einträgt.
nun möchte ich die Klasse in einer datei speichern, *.dat oder so, um sie später wieder laden zu können.

nun weiss ich ja nicht wie delphi eine klasse mit eigenschaften in der datei speichert (wenns jemand weiss bitte erklären), noch weiss ich ob man jede klasse in einer seperaten zeile speichern muss noch wie man allgemein sowas in dateien speichert.

bitte keine antwort mit fachsimpelei, danke

(( und bitte keine hilfeverweise, danke ))


Zuletzt bearbeitet von Schatten^parker am So 16.02.03 16:39, insgesamt 1-mal bearbeitet
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Fr 14.02.03 22:10 
Ich denke du sprichst nicht von einer Klasse, sondern von einem Record. Und den kannst mit typisierten Dateien abspeichern.
Andreas L.
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1703
Erhaltene Danke: 25

Windows Vista / Windows 10
Delphi 2009 Pro (JVCL, DragDrop, rmKlever, ICS, EmbeddedWB, DEC, Indy)
BeitragVerfasst: Sa 15.02.03 09:07 
Oder mit INI-Dateien, deren du einfach eine andere Dateiendung gibst!
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: Sa 15.02.03 15:18 
Hi, natürlich kannst du auch klassen-properties in dateien speichern! Sie muss dazu von TPersistent abgeleitet sein und die properties müssen 'published' sein! Dann kannst du mittels RTTI (RunTimeTypeInformation) die props speichern und laden, und zwar automatisch alle auf einmal.

Wenn du von TComponent ableitest, dann kannst du dies sogar mit jedem beliebigen Stream tun -> Stream.writeComponent(compo) ,...readComponent(compo)!
Is aber nicht so schön, weil du dann viel unnötigen quatsch in der klasse hasst.

viel spass!

PS: auf [url]www.joachimDeVries.de[/url] findest du eine gute RTTI unit.
Schatten^parker Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Sa 15.02.03 15:42 
Ich verstehe nicht ganz was ich machen soll, hier mein quelltext zum verständnis! danke



ausblenden volle Höhe Quelltext
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:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
unit Mhaupt;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, mdialog,mmitglied;

type
  TFHaupt = class(TForm)
    bt_dialog: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Button1: TButton;
    lb_mitglied: TListBox;
    procedure bt_dialogClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
  fdialog: Tfdialog;
  mitglied: tmitglied;
  mitgliedliste: Tlist;
  listbox: tlistbox;
    { Private-Deklarationen }
  public
  function getvorname: string;
  function getnachname: string;
  function getgebdatum: string;
  function getnummer: string;

    { Public-Deklarationen }
  end;

var
  FHaupt: TFHaupt;


implementation

{$R *.DFM}
function TFHaupt.getvorname;
begin
     getvorname:= fdialog.edit1.text;
end;

function TFHaupt.getnachname;
begin
     getnachname:= fdialog.edit2.text;
end;

function TFHaupt.getgebdatum;
begin
     getgebdatum:= fdialog.edit3.text;
end;

function TFHaupt.getnummer;
begin
     getnummer:= fdialog.edit4.text;
end;


procedure TFHaupt.bt_dialogClick(Sender: TObject);
begin
     fdialog:= tfdialog.create(SELF);
     fdialog.showmodal;

     edit1.text:= getvorname;
     edit2.text:= getnachname;
     edit3.text:= getgebdatum;
     edit4.text:= getnummer;

     mitglied:= tmitglied.create;
     mitglied.vorname:= getvorname;
     mitglied.nachname:= getnachname;
     mitglied.gebdatum:= getgebdatum;
     mitglied.nummer:=getnummer;
     mitgliedliste.add(mitglied);

    lb_mitglied.items.add(mitglied.nummer+' - '+mitglied.nachname+', '+mitglied.vorname+'('+mitglied.gebdatum+')');
   { mitgliedliste.items[0].savetofile('c:\schule\info\ml.dat'); }
    showmessage(mitglied.vorname+' wurde erfolgreich in die Liste aufgenommen!');

    fdialog.free;

end;

procedure TFHaupt.Button1Click(Sender: TObject);
begin
     close;
end;

procedure TFHaupt.FormCreate(Sender: TObject);
begin
     mitgliedliste:= tlist.create;
     listbox:= tlistbox.create(self);
    { mitgliedliste.items.loadfromfile('Mitgliedliste.dat');  }
end;

end.

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
unit mmitglied;

interface

type
  tmitglied = class(Tobject)
    vorname: string;
    nachname: string;
    gebdatum: string;
    nummer: string;
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;


implementation

end.
aogwaba
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 109



BeitragVerfasst: Sa 15.02.03 17:43 
benutz doch statt TList( welches eigentlich nur Pointer aufnimmt), ein TStringList, da sind die Read und Write Methoden mit onBoard.

cu
waba
Schatten^parker Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Sa 15.02.03 18:10 
ausblenden Quelltext
1:
[Fehler] Mhaupt.pas(81): Inkompatible Typen: 'String' und 'tmitglied'					


die klasse mitglied kann nicht in die stringlist aufgenommen werden da sie warscheinlich garkein string ist, sie hatt properties vom typ string ( .vorname , .nachname ,...).
aogwaba
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 109



BeitragVerfasst: Sa 15.02.03 19:20 
gemeint war das so:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
myList:tstringList;
myList:=tstringList.create;

mitglied:= tmitglied.create; 
mitglied.vorname:= getvorname; 
mitglied.nachname:= getnachname; 
mitglied.gebdatum:= getgebdatum; 
mitglied.nummer:=getnummer;
    
myList.add(mitglied.nummer+' - '+mitglied.nachname+', '
+mitglied.vorname+'('+mitglied.gebdatum+')');
   
myList.saveToFile(...);

Dieses Konstruct fügt die Strings zu einem String zusammen und speicht diesen.
Beim Laden muss dieser wieder zerbröselt, und die Token den einzelnen Propertys zugewiesen werden.

Sehr primitiv. Besser man benutzt, wie Luckie schon sagte, typisierte Dateien.
Hier ein Link: www.tutorials.delphi...urce.de/sequdateien/

oder noch besser TFileStream.

cu
waba
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: Sa 15.02.03 20:30 
Hallo! Hier die möglichkeit via RTTI...das rockt hart!
ausblenden volle Höhe Quelltext
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:
unit mmitglied; 

interface 

type 
  tmitglied = class(TComponent) // TComponent weil es sich mit streams speichern lässt!
    Fvorname: string; 
    Fnachname: string; 
    Fgebdatum: string; 
    Fnummer: string; 
  private 
    { Private-Deklarationen } 
   published // edit
    { Public-Deklarationen }
    SaveToFile(FileName);
    LoadFromFile(FileName);

    property Vorname : string read FVorname write FVorname;
    property Nachname : string read FNachname  write FNachname ;
    .    
    .
    .
  end; 


implementation 

procedure tmitglied.SaveToFile; 
var fs:TFileStream;
begin
  fs:=TFileStream.create(fileName,fmCreate);
  try
    fs.writeComponent(self);
  finally
    fs.free;
  end;
end;

procedure tmitglied.LoadFromFile; 
var fs:TFileStream;
begin
  if not fileExists(filename) then exit;
  fs:=TFileStream.create(fileName,fmOpenRead);
  try
    fs.ReadComponent(self);
  finally
    fs.free;
  end;
end;

end.


Hierbei musst du dich halt nicht mehr um die einzelnen properties kümmern, da sie alle automatisch gespeichert werden (im binären DFM format). Mit 'ObjectBinaryToText' oder 'ObjectTextToBinary' könntest du das format sogar in regulären DFM-text convertieren und zurück!

IMHO sehr cool, da voll dynamisch :wink:

mfg maximus
Schatten^parker Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: So 16.02.03 16:36 
Hi Maximus, danke für deinen Tip.
Etwas ähnliches habe ich auch schon probiert, aber jedes mal wenn ich eine Klasse von TComponent ableiten will kommt folgender Fehler

ausblenden Quelltext
1:
[Fehler] mmitglied.pas(6): Undefinierter Bezeichner: 'TComponent'					


Ich weiss auch nicht woran es liegt, vielleicht hab ich vergessen was zu deklrieren??? bitte hilf mir ;-)))
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: So 16.02.03 17:28 
Du musst die Datei Classes einbinden:
ausblenden Quelltext
1:
2:
Uses
  Classes;


Gruß
TINO
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: So 16.02.03 18:26 
Genau! Mit 'ObjectBinaryToText' oder 'ObjectTextToBinary' echten DFM-text zu erzeugen ist nicht ganz so leicht! Deshalb der vollständigkeit halber, wie ich es gemacht habe:
ausblenden volle Höhe Quelltext
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:
...
function SaveCompoToFile(aCompo : TComponent; FileName: string; asBinary: Boolean = false): boolean;
var Stream1:TMemoryStream;
    Stream2:TMemoryStream;
    format : TStreamOriginalFormat;
begin
  result := false;
  format  := sofText;
  Stream1 := TMemoryStream.Create;
  Stream2 := TMemoryStream.Create;
  try
    Stream1.WriteComponent(aCompo);
    Stream1.Position := 0;
    if not asBinary then
    begin
      format := sofText;
      ObjectBinaryToText(Stream1,Stream2,format);
    end else Stream1.SaveToStream(Stream2);
    Stream2.SaveToFile(FileName);
    result := true;
  finally
    Stream1.Free;
    Stream2.Free;
  end;
end;


function LoadCompoFromFile(aCompo : TComponent; FileName: string; asBinary: Boolean = false): boolean;
var FileStream:TFileStream;
    MemoryStream:TMemoryStream;
    format:TStreamOriginalFormat;
begin
  result := false;
  if not FileExists(fileName) then exit;
  MemoryStream := TMemoryStream.Create;
  FileStream := TFileStream.Create(FileName,fmOpenRead);
  try
    if not asBinary then
    begin
      format := sofBinary;
      ObjectTextToBinary(FileStream,MemoryStream,format);
    end else MemoryStream.LoadFromStream(FileStream);
    MemoryStream.Position := 0;
    MemoryStream.ReadComponent(aCompo);
    result := true;
  finally
    FileStream.Free;
    MemoryStream.Free;
  end;
end;
...wer an einer lösung interessiert, die ohne TComponent auskommt und mit der man auch einzelne properties speichern kann, der soll sich hier melden :D

mfg maximus
Eisenherz
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 48



BeitragVerfasst: Mo 17.02.03 18:59 
maximus hat folgendes geschrieben:
Hierbei musst du dich halt nicht mehr um die einzelnen properties kümmern, da sie alle automatisch gespeichert werden (im binären DFM format).


Somit kann man mit sehr wenig Code etwas speichern. Ich habe aber das Gefühl, dass das unter die Rubrik "Quick and dirty" fällt.
Das automatische Speichern klappt nur, wenn das Property die Sichtbarkeit Published hat. Will man etwas Speichern, was vom Konzept her Private sein sollte, dann wird man gezwungen dies Published zu machen. Das stimmt nicht ganz, da es auch eine Möglichkeit gibt, dass man "Properties" speichern kann, die nicht published sind. Dann muss man allerdings deutlich mehr Code schreiben.
Weiterhin ist man so gezwungen ein published Property bis in alle Ewigkeit beizubehalten. Man kassiert eine Exception, wenn man versucht eine Komponente aus einer Datei zu laden, die erzeugt wurde, als die Komponente noch Properties hatte, über die sie jetzt nicht mehr verfügt.

_________________
aloa Eisenherz
Tino
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Veteran
Beiträge: 9839
Erhaltene Danke: 45

Windows 8.1
Delphi XE4
BeitragVerfasst: Di 18.02.03 10:12 
Hallo Leute,

eine weitere Möglichkeit wäre mal auf die Website von Eisenherz zu gehen. Dort kann man sich die Unit RakBinaryStreamData downloaden. Dadurch wird das speichern und laden von beliebigen Daten vereinfacht.

Gruß
TINO
FBrk
Hält's aus hier
Beiträge: 8



BeitragVerfasst: Di 18.02.03 11:51 
Titel: tud dat not???
Sag mal muss das sein das die Daten in einer Klasse gespeichert werden???
Klassen sind da um Daten und Routinen in einer Komponente zu verbinden und zu vererben um dann neue Komponenten hinzu zu fürgen. In diesem Falle ist ein Record das mittel der wahl, und das Speichert man logischer weise in Typisierten Datein.
Das ist erstens einfacher und zweitens sauberer.

Gruß Felix

_________________
Warum einfach wenn´s auch kompliziert geht??
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: Di 18.02.03 14:27 
Eisenherz hat folgendes geschrieben:
Somit kann man mit sehr wenig Code etwas speichern. Ich habe aber das Gefühl, dass das unter die Rubrik "Quick and dirty" fällt.
Das automatische Speichern klappt nur, wenn das Property die Sichtbarkeit Published hat. Will man etwas Speichern, was vom Konzept her Private sein sollte, dann wird man gezwungen dies Published zu machen. Das stimmt nicht ganz, da es auch eine Möglichkeit gibt, dass man "Properties" speichern kann, die nicht published sind. Dann muss man allerdings deutlich mehr Code schreiben...


8) Klar is das Quick&Dirty (hab ich aber auch, in einem nebensatz draufhin gewiesen) , deshalb hab ich ja auch eine möglichkeit angeboten das quick aber nicht dirty zu machen:
Zitat:
...wer an einer lösung interessiert, die ohne TComponent auskommt und mit der man auch einzelne properties speichern kann, der soll sich hier melden.
. ...mit einem einzigen befehl zB. Width, height,top,left,windowState zu speichern ist IMHO sehr geil. Bei allen componenten bezogenen sachen hat man die wichtigen props eh immer published( zumindest die, die gespeichert werden sollen). Und schicken DFM-text zu haben ist halt auch nicht zu verübeln, da man die datei dann automatisch als config benutzen kann, was man bei einem binär-stream getrost vergessen kann :D

Mich würd mal interessieren wie ich nicht 'published' properties automatisch speicherst...wie machst du das, via RTTI oder hab ich was verpasst?

Grüsse.
maximus

PS: Der code oben eignet sich prima um ganze Componenten-bäume zur laufzeit zu erstellen und deren aktuelle einstellungen zu speichern! Die von delphi erstellten compos sollte man damit nicht speichern wollen, weil er dann versucht, die im DFM-text definierten compos zu erstellen, obwolh sie schon existieren :?
Eisenherz
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 48



BeitragVerfasst: Di 18.02.03 15:44 
Titel: Re: tud dat not???
FBrk hat folgendes geschrieben:
Sag mal muss das sein das die Daten in einer Klasse gespeichert werden???

Wenn man streng objektorientiert programmiert, dann ja.

Zitat:
Klassen sind da um Daten und Routinen in einer Komponente zu verbinden und zu vererben um dann neue Komponenten hinzu zu fürgen.

Klassen wissen erst einmal gar nichts von Komponenten. Komponenten zeichnen sich dadurch aus, dass sie von der Klasse TComponent direkt oder indirekt abgeleitet sind. Eine Komponente ist also nur ein Spezialfall einer Klasse.
Zitat:
In diesem Falle ist ein Record das mittel der wahl, und das Speichert man logischer weise in Typisierten Datein.

Man kann Records in typisierten Dateien speichern, das ist aber nicht die einzige Möglichkeit und somit nicht logisch.
Records sind ein Überbleibsel aus der nicht objektorientierten Zeit von Pascal. Bei Java gibt es z.B. keine Records. Das braucht man auch nicht, da Records nur ein Spezialfall von Klassen sind, nämlich Klassen ohne Methoden.
Der einzig wirklich gewichtige Grund, der mir jetzt einfällt, um Daten in typisierten Dateien zu speichern ist, dass man schnellen Zugriff auch auf Daten braucht, die sich in der Mitte oder am Ende der Datei befinden.
Wenn man bedenkt, dass heutige Festplatten 20MByte/sec und mehr übertragen, lohnt sich der Einsatz von typisierten Dateien nur dann richtig, wenn man große Datenmengen hat. Dann sollte man aber darüber nachdenken, ob man nicht gleich mit einer richtigen Datenbank arbeitet.

Zitat:
Das ist erstens einfacher

Bei kleinen Programmen mag das stimmen.

Zitat:
und zweitens sauberer.

Das kommt darauf an, was man als "sauber" ansieht.

_________________
aloa Eisenherz
Eisenherz
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 48



BeitragVerfasst: Di 18.02.03 16:17 
maximus hat folgendes geschrieben:
8) Klar is das Quick&Dirty (hab ich aber auch, in einem nebensatz draufhin gewiesen) , deshalb hab ich ja auch eine möglichkeit angeboten das quick aber nicht dirty zu machen:
Zitat:
...wer an einer lösung interessiert, die ohne TComponent auskommt und mit der man auch einzelne properties speichern kann, der soll sich hier melden.
.


Mich würde Deine Lösung interessieren. Ich glaube zwar nicht, dass ich sie einsetzen werde, da ich viel von Datenkapselung halte und mir somit der Gedanke nicht gefällt, dass ich Properties published und schreibbar machen muss, die vom Konzept her protected/private oder readonly sind. Aber vielleicht hast Du ein paar interessante Ideen gehabt.

Zitat:
Und schicken DFM-text zu haben ist halt auch nicht zu verübeln, da man die datei dann automatisch als config benutzen kann, was man bei einem binär-stream getrost vergessen kann :D

Textdateien haben natürlich den Vorteil, dass man sie sogar mit Notepad bearbeiten kann. Sie haben damit aber auch den Nachteil, dass man sie schnell unbrauchbar machen kann.
Damit man meinem "binär-stream" editieren kann habe ich extra ein Tool erstellt.

Zitat:
Mich würd mal interessieren wie ich nicht 'published' properties automatisch speicherst...wie machst du das, via RTTI oder hab ich was verpasst?

Ich speichere bisher nichts automatisch. Aber vielleicht greife ich den Gedanken auf und erweitere meine Klassen in diese Richtung.

_________________
aloa Eisenherz
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: Di 18.02.03 18:13 
@Eisenherz:
Zitat:
Mich würde Deine Lösung interessieren. Ich glaube zwar nicht, dass ich sie einsetzen werde, da ich viel von Datenkapselung halte und mir somit der Gedanke nicht gefällt, dass ich Properties published und schreibbar machen muss, die vom Konzept her protected/private oder readonly sind. Aber vielleicht hast Du ein paar interessante Ideen gehabt.

Dich zwingt ja auch niemand irgendwas gegen deinen willen schreibbar zumachen :D Wenn deine props vom konzept her privat sein sollen, dann ist es ja auch unwahrscheinlich das du sie auf platte speichern willst, oder?

Du hast mich auf die idee gebracht, meinen ansatz mal auf read-only properties zu prüfen...die funktion ist da aber ob ich es hier auch berücksichtigt habe werde ich mal schecken.

Ich poste morgen mal den code.

bis denn. mx
maximus
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 896

Win XP, Suse 8.1
Delphi 4/7/8 alles prof
BeitragVerfasst: Mi 19.02.03 00:46 
So. Ich hab meinen code auf 'read-only' und 'stored false' properties sensibilisiert. Damit der code läuft, wird noch eine (sehr gute, IMO) RTTI unit benötigt (von Joachim De Vries).
ausblenden volle Höhe Quelltext
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:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
{-----------------------------------------------------------------------------
 Unit Name : mxService
 Author    : maximus (mäxeus, maxantat, muku)
 Date      : 10.02.2003
-----------------------------------------------------------------------------}
unit mxService;

interface

uses classes, sysUtils, TypInfo, dvRTTI {<-- von www.joachimDeVries.de -} ;

function SaveObjectToFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;
function LoadObjectFromFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;

procedure CleanSpacesVar(var s:String; inQuotes:boolean=false; QuoteChar: Char = '''');
function CleanSpaces(s:String; inQuotes:boolean=false; QuoteChar:Char = ''''):string;

implementation

function CleanSpaces(s:String; inQuotes:boolean=false; QuoteChar:Char = ''''):string;
var i:integer;
    f:boolean;
begin
  result := s;
  i := 0;
  f := false;
  while i <= length(result) do
  begin
    if not inQuotes and (result[i] = QuoteChar) then f := not f;
    if (result[i] = ' ') and not f then Delete(result,i,1) else inc(i);
  end;
end;

procedure CleanSpacesVar(var s:String; inQuotes:boolean=false; QuoteChar:Char = '''');
var i:integer;
    f:boolean;
begin
  i := 0;
  f := false;
  while i <= length(s) do
  begin
    if not inQuotes and (s[i] = QuoteChar) then f := not f;
    if (s[i] = ' ') and not f then Delete(s,i,1) else inc(i);
  end;
end;

function isWholeWordInString(aWord,s:string; delimiter:char = ','):boolean;
var n,i:integer;
    m:string;
    c1,c2:char;
begin
  result := false;
  m := delimiter+lowercase(s)+delimiter;
  n := pos(lowercase(aWord),m);
  if n <= 0 then exit;
  m := copy(m,n-1,length(m)-n+2);
  c1 := m[1];
  c2 := m[length(aWord)+2];
  if ((c1 = delimiter) or (c1 = ' '))and((c2 = delimiter) or (c2 = ' '))
  then result := true
  else begin
         delete(m,2,length(aWord));
         result := isWholeWordInString(aWord, m, delimiter);
       end;
end;


//----------------------------------------------------------------------------------------


function SaveObjectToFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;
var RTTI:TdvRTTIList;
    sl:TSTringList;
    i:integer;
    s,v,name:string;
    All:boolean;
begin
  result := false;
  if aObject = nil then exit;
  All := includedProps = '*';
  RTTI := TdvRTTIList.create(aObject);
  sl := TSTringList.Create;
  try
    RTTI.Filter := filter;
    if aObject is TComponent then name := TComponent(aObject).Name+' : ' else name := '';
    sl.Add('object '+name+aObject.ClassName);
    for i := 0 to RTTI.count-1 do
    begin
      if RTTI.Items[i].PropInfo.StoredProc = nil then continue;
      if not All and not isWholeWordInString(RTTI.Items[i].Name,includedProps) then continue;
      v := RTTI.Items[i].Value;
      if v <> '' then sl.Add('  ' + RTTI.Items[i].Name+' = '+ v);
    end;
    sl.Add('end');
    sl.SaveToFile(fileName);
    result := true;
  finally
    sl.Free;
    RTTI.Free;
  end;
end;

function LoadObjectFromFile(aObject : TPersistent; FileName: string; includedProps : String = '*'; filter : TTypeKinds = tkAny): boolean;
var RTTI:TdvRTTIList;
    sl:TSTringList;
    i:integer;
    s,name:string;
    All : boolean;
begin
  result := false;
  if aObject = nil then exit;
  if not FileExists(fileName) then exit;
  All := includedProps = '*';
  RTTI := TdvRTTIList.create(aObject);
  sl := TSTringList.Create;
  try
    RTTI.Filter := filter;
    sl.LoadFromFile(fileName);
    sl.Text := CleanSpaces(sl.Text);
    for i := 0 to RTTI.count-1 do
    begin
      if RTTI.Items[i].PropInfo.SetProc = nil then continue;
      name := RTTI.Items[i].Name;
      if not All and not isWholeWordInString(name,includedProps) then continue;
      if sl.IndexOfName(name) > -1
      then RTTI[i].Value := sl.Values[name];
    end;
    result := true;
  finally
    sl.Free;
    RTTI.Free;
  end;
end;

end.

Bindet man die unit ein, so kann man nu beliebige 'published' props eines objekts speichern und laden (ab D6):
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
uses mxService;
...
SaveObjectToFile(self, MyDir+self.Name+'.dmx','top,left,width,height,name,windowstate');
LoadObjectFromFile(self, MyDir+self.Name+'.dmx','top,left,width,height,name,windowstate');
// oder
SaveObjectToFile(self, MyDir+self.Name+'.dmx','*');  // speichert alle
LoadObjectFromFile(self, MyDir+self.Name+'.dmx','*');  // speichert alle
// oder
uses mxService, typInfo;
...
SaveObjectToFile(self, MyDir+self.Name+'.dmx','*', tkInteger); // speichert alle integer props
LoadObjectFromFile(self, MyDir+self.Name+'.dmx','*', tkInteger);

Das objekt muss von TPersistent abgeleitet sein(kann man vielleicht auch umbauen für Objekte die mit {M+} erstellt worden sind, allerdings beide units :? besser nicht!)

Viel spass damit...und wenn jemand verbesserungen hat, dann würd es mich freuen diese zu sehen(hier oder an maximus@eyer-systems.de).

mfg mx.