| Autor |
Beitrag |
Peter18
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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?)
Hier die Testroutinen:
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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: 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:
Delphi-Quelltext 1:
| fs.WriteComponent(Datensatz); |
oder du liest die einzelnen Komponenten auch so wieder aus, wie du sie geschreiben hast
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 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: Di 17.02.09 14:25
Hallo Nico72,
Klappt leider nicht.
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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: Di 17.02.09 14:51
mh, habe es ohne Delphi hingerotzt...
Geht natürlich nicht. Habe bis jetzt nur selbsterstellte Komponenten in/aus Stream geschreiben/geladen...
Versuch es mal so:
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...
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 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: 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 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: 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  ).
|
|
Peter18 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: 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 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: Do 19.02.09 11:54
Hallo Peter18,
ich habe immer noch nicht verstanden, was du genau machen willst.
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
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
Delphi-Quelltext 1:
| MyLabel.Parent := Datensatz; |
Natürlich musst du auch die anderen Properties (Left, Top, Caption etc.) zuweisen, um was zu sehen.
Wenn du jetzt mit
Delphi-Quelltext 1:
| FS.WriteComponent(Datensatz); |
in den Stream schreibst, solltest du die Oberfläche mit
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 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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 
      
Beiträge: 489
Erhaltene Danke: 2
Delphi4
|
Verfasst: 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  , aber ich denke das liegt am Prototyp. Damit habe ich mich erst mal in Delphi eingearbeitet. "RegisterClass" und "ReadComponent(nil)" sind die entscheidenden Schritte.
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
      
Beiträge: 50
Win XP, Vista
Delphi 7, Delphi 2006
|
Verfasst: Do 19.02.09 17:40
|
|
Logikmensch
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: 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!
|
|