Autor Beitrag
Peter18
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 17.02.09 13:04 
Hallo Leute,

ich habe das Problem, dass ich bei einem Formular zur Laufzeit Komponenten austauschen möchte. Dazu schreibe ich die zu tauschenden Komponenten in eine Datei. Wenn die Komponenten aber wieder eingelesen werden, werden sie jedoch nicht auf dem Parent-Objekt erzeugt, vielmehr wird es selbst verändert. Hat jemand einen Tipp, was da falsch ist? (Sooo viele Bäume aber wo ist der Wald?) :P

Hier die Testroutinen:

ausblenden volle Höhe Delphi-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:
procedure TVertrieb.Button10Click(Sender: TObject);
var
  I    : Integer;
  Path : String;
  fs   : TFileStream;
begin
  Path := Application.ExeName;
  Path := ExtractFilePath( Path );
  Path := Path + 'Datensatz.frm';
  fs:= TFileStream.Create( Path,  fmCreate or fmOpenWrite );
  with Datensatz do
  begin
    for I := 0 to ControlCount - 1 do
      if Controls[ I ].Tag < 1000 then
        fs.WriteComponent(Controls[ I ]);
  end;
  fs.Free;
end;

procedure TVertrieb.Button11Click(Sender: TObject);
var
  I  : Integer;
begin
  with Datensatz do
  begin
    I := 0;
    while (I < (ControlCount - 1)) do
    begin
      if Controls[ I ].Tag < 1000 then
        Controls[ I ].Destroy
      else
        I := I + 1;
    end;
  end;
end;

procedure TVertrieb.Button12Click(Sender: TObject);
var
  I    : Integer;
  Path : String;
  fs   : TFileStream;
begin
  Path := Application.ExeName;
  Path := ExtractFilePath( Path );
  Path := Path + 'Datensatz.frm';
  if fileExists(Path) then
  begin
    fs:= TFileStream.Create( Path,  fmOpenRead );
    fs.ReadComponent(Datensatz);
    fs.Free;
  end;
end;

"Datensatz" ist vom Type "TTabSheet". Statt die Elemente auf dessen Oberfläche zu erzeugen wird der Name des TabSheet durch den Text eines Labels ersetzt. Anschließend folgen Fehlermeldungen: "Eigenschaft nicht vorhanden".

Dank euch im Voraus und Grüße von der Nordsee
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Di 17.02.09 13:18 
Hallo,

du schreibst die Controls vom TTabsheet Datensatz in den Stream und liest das TTabsheet Datensatz selbst aus dem Stream wieder aus. Deswegen auch die Änderung der Caption im Tabsheet -danach kommt irgendein Property, das ein TTabsheet nicht besitzt (dewegen die Fehlermeldung). Entweder du schreibst Datensatz komplett in den Stream:
ausblenden Delphi-Quelltext
1:
fs.WriteComponent(Datensatz);					


oder du liest die einzelnen Komponenten auch so wieder aus, wie du sie geschreiben hast
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Var Comp:TComponent;
begin
[...]
while fs.Position < fs.Size do
begin
  Comp := fs.ReadComponent(nil);
  if Comp <> nil then
    Comp.Parent := Datensatz;
end;
[...]
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 17.02.09 14:25 
Hallo Nico72,

Klappt leider nicht. :(

ausblenden Delphi-Quelltext
1:
fs.WriteComponent(Datensatz);					

Schreibt leider nur Datensatz aber nicht die Child-Komponenten.

Bei "TComponent" ist "Parent" nicht definiert, außerdem werden alle Komponenten auf einmal eingelesen.

Werde auf dieser Basis weiter experimentieren, aber vielleicht hat ja noch jemand einen Tipp.

Grüße von der Nordsee
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Di 17.02.09 14:51 
mh, habe es ohne Delphi hingerotzt... :oops:

Geht natürlich nicht. Habe bis jetzt nur selbsterstellte Komponenten in/aus Stream geschreiben/geladen...

Versuch es mal so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  Con:TControl;
begin
[...]
  while FS.Position < FS.Size do
  begin
    Con :=  TControl(FS.ReadComponent(nil));
    if Con <> nil then
      Con.Parent := Datensatz;
  end;
[...]
end;


Welche Komponenten verwendest du denn da? Ich musste bei mir interessanterweise selbst ein TLabel (und alle anderen) erst registrieren...
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
initialization
  RegisterClass(TLabel);
  [...]

finalization
  UnRegisterClass(TLabel);
  [...]


und selbst dann klappt es nicht bei allen... mh.

E: Quelltext korrigiert
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 17.02.09 15:13 
Hallo Nico72,

Danke für Deine Antwort. In dieser Richtung habe ich auch schon geforscht, bekomme aber leider immer die Meldung: "Klasse TLabel nicht gefunden". Hängt das mit der Registrierung zusammen? Bei Versuchen konnte ich Controls erzeugen und einer Variablen zuweisen, warum klappt das nicht bei "ReadComponent"? Direkt einlesen klappt (fs.ReadComponent(Datensatz)) jedenfalls.

Ich verwende zur Zeit TLabel, TComboBox, TEdit. TButton, TBitBtn und TImage werden noch dazu kommen, vielleicht noch weitere.

Grüße von der Nordsee
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Di 17.02.09 15:38 
Woher kommen den die Komponenten, die du in den Stream schreiben willst? Wenn du sie dynamisch erzeugst und dabei als AOwner dein Tabsheet angibst, sollte er sie auch mit .WriteComponent(Tabsheet) in den Stream schreiben und auch auslesen können. Geht jedenfalls bei mir...
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 17.02.09 15:51 
Halo Nico72,

die Komponenten habe ich bisher mit dem Formulareditor von Delphi erzeugt. Nun Taste ich mich an die Veralgemeinerung heran. Dazu möchte ich die Komponenten erstellen und speichern um sie zur Laufzeit leicht austauschen zu können.

Grüße von der Nordsee
Peter
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Di 17.02.09 16:03 
Wenn du diesen ganzen Aufwand betreibst, weil dein Programm dem Anwender ermöglichen soll, die Controls selber anzulegen und zu designen, dann musst du sie dynamisch erzeugen. Dann sollte das kein Problem mehr sein.

Wenn du aber nur verschiedene Seiten mit den Komponenten erstellen willst (und du ja schon ein PageControl verwendest), die dann nach Situation verschieden angezeigt werden sollen, dann schau dir doch mal TTabsheet.TabVisible oder auch Frames an (wenn es die in deinem Delphi schon gibt :roll: ).
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Di 17.02.09 18:48 
Hallo Nico72,

Nach "TTabsheet.TabVisible" hatte ich schon gesucht, doch nicht gefunden. Werde mich damit befassen. Frames gibt es aber nur beim "TMediaPlayer". (Delphi 4)

Aber was hat es mit der Registrierung auf sich die Du erwähntest??

Inzwischen haben meine Forschungen folgendes ergeben:
Mit "TControl(fs.ReadComponent(Self))" kann ich die Controls einzeln lesen, das heißt den 1. danach (beim 2.) folgt ein Stack-Fehler.

Grüße von der Nordsee
Peter
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Mi 18.02.09 09:18 
Nach der Registrierung kennt er die Klasse. Dann lädt und erzeugt er eine Instanz aus dem Stream (d.h. er kommt nicht mit der Fehlermeldung "Klasse xyz nicht gefunden").

Nach der ersten eingelesenen Komponente kommt ein Stack-Fehler? Oder meinst du Stream-Fehler?

Hast du denn mal probiert, die Komponenten dynamisch zu erzeugen (mit AOwner Datensatz) und dann diesen Tabsheet in den Stream zu schreiben?
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Do 19.02.09 10:41 
Hallo Nico72,

den Fehler "Klasse xyz nicht gefunden" bekomme ich, wenn ich "ReadComponent" mit "Nil" aufrufe, Stackfehler (Überlauf) wenn ich mit "Self" oder dem Formularnamen aufrufe. Das Formular verschwindet bei "ReadComponent", der Stackfehler kommt beim 2. Aufruf. Übergebe ich das TabSheet kommt der Fehler "Klasse TLabel nicht gefunden". "AOwner" finde ich in der Hilfe nicht, "Owner" ist nur zum lesen.

Werde mal Register ausprobieren. Wo werden die Registrierungen vorgenommen? In der Registry?

Grüße von der Nordsee
Peter
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Do 19.02.09 11:54 
Hallo Peter18,

ich habe immer noch nicht verstanden, was du genau machen willst. :gruebel:

Wenn dein Plan ist, dass der Anwender sein Tabsheet selbst designen kann, dann kommst du ja nicht darum herum, die Komponenten dynamisch zu erstellen. AOwner ist der Parameter im Konstruktor, Owner ist ein Nur-Lesen-Property der Komponente. D.h. wenn du z.B. ein Label so erzeugst
ausblenden Delphi-Quelltext
1:
MyLabel := TLabel.Create(Datensatz);					

dann hat das Label den Owner "Datensatz". Um es dort auch anzuzeigen, darfst du nicht vergessen, auch den Parent zu setzen
ausblenden Delphi-Quelltext
1:
MyLabel.Parent := Datensatz;					

Natürlich musst du auch die anderen Properties (Left, Top, Caption etc.) zuweisen, um was zu sehen. :wink:
Wenn du jetzt mit
ausblenden Delphi-Quelltext
1:
FS.WriteComponent(Datensatz);					

in den Stream schreibst, solltest du die Oberfläche mit
ausblenden Delphi-Quelltext
1:
FS.ReadComponent(Datensatz);					

wieder herstellen können.

Diese "Klasse xyz nicht gefunden" kommt bei Nil, weil er dann die Komponente ja erst erzeugen muss, und die Klasse lädt er dann aus dem Stream. Damit er sie erzeugen kann, muss ihm vorher die Klasse "bekannt" gemacht werden, und das geschieht, wenn du im initialization-Abschnitt der Unit dieses RegisterClass(TXYZ) aufrufst. Das solltest du aber bei der oben gezeigten Vorgehensweise nicht brauchen.
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Do 19.02.09 12:55 
Hallo Nico72,

ich möchte verschiedene Datensätze mit unterschiedlichen Strukturen anzeigen. Um mir die Programmierung jedes einzelnen Controls zu ersparen wollte ich sie mit dem Editor erstellen und in eine Datei speichern, die ich dann zur Laufzeit abhängig vom Datensatz einlesen kann. Da Delphi selbst diese Möglichkeiten nutzt, nehme ich an, das es möglich sein sollte.

Wenn ich Dich richtig verstanden habe ersetzt "RegisterClass" die Deklaration im Programmkopf.

Grüße von der Nordsee
Peter
Peter18 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 489
Erhaltene Danke: 2


Delphi4
BeitragVerfasst: Do 19.02.09 17:19 
Hallo an alle,

inzwischen klappt es mit dem Lesen und Erzeugen der Objekte. Einige verschwinden zwar auf wundersame Weise wieder :cry: , aber ich denke das liegt am Prototyp. Damit habe ich mich erst mal in Delphi eingearbeitet. "RegisterClass" und "ReadComponent(nil)" sind die entscheidenden Schritte.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
    RegisterClass(TLabel);
    RegisterClass(TComboBox);
    RegisterClass(TEdit);
    RegisterClass(TBitBtn);
    while fs.Position < fs.Size do
    begin
      Con := TControl(fs.ReadComponent(Nil));
      if Con <> nil then
        Con.Parent := Datensatz;
    end;
    UnRegisterClass(TLabel);
    UnRegisterClass(TComboBox);
    UnRegisterClass(TEdit);
    UnRegisterClass(TBitBtn);


Ich forsche weiter und berichte. :?

Grüße von der Nordsee
Peter
Nico72
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 50

Win XP, Vista
Delphi 7, Delphi 2006
BeitragVerfasst: Do 19.02.09 17:40 
user profile iconNico72 hat folgendes geschrieben Zum zitierten Posting springen:

Versuch es mal so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  Con:TControl;
begin
[...]
  while FS.Position < FS.Size do
  begin
    Con :=  TControl(FS.ReadComponent(nil));
    if Con <> nil then
      Con.Parent := Datensatz;
  end;
[...]
end;

[...]
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
initialization
  RegisterClass(TLabel);
  [...]

finalization
  UnRegisterClass(TLabel);
  [...]



Naja, wenn es jetzt geht, ist ja gut.
Logikmensch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Fr 20.02.09 09:25 
Nico schrieb weiter oben:
Zitat:
Wenn du aber nur verschiedene Seiten mit den Komponenten erstellen willst (und du ja schon ein PageControl verwendest), die dann nach Situation verschieden angezeigt werden sollen, dann schau dir doch mal TTabsheet.TabVisible oder auch Frames an (wenn es die in deinem Delphi schon gibt )


Ich finde, dass er recht hat. Selbst wenn Deine Lösung funktioniert, halte ich sie doch für umständlich.
In den Jedi-Componenten (ich hoffe, Du kennst sie) gibt es eine Registerblatt-Komponente, die keine Reiter hat. Ich verwende sie in meinen Anwendungen, wenn ich, wie Du, zu unterschiedlichen Situationen unterschiedliche Formularbereiche benötige.

_________________
Es gibt keine Probleme - nur Lösungen!