![]() |
Kniffliges Problem beim Serialisieren
Liste der Anhänge anzeigen (Anzahl: 1)
Hello party people,
also ich bin da auf ein sehr interessantes, wenn auch frustrierendes, und zugleich möglicherweise sogar noch logisches Problem gestoßen, an dem sich jeder gerne versuchen darf :D Hintergrund: Entwickelt wurde eine MDI-Anwendung. In den jeweiligen MDI-Children werden in zahlreichen Controls Eingaben gemacht, die in ihrer Gesamtheit gespeichert und später wieder geladen werden sollen. Damit ich das nicht in der Manier: "Schreibe KomponenteX.EigenschaftA in TextDatei t, gehe eine Zeile weiter, Schreibe KomponenteY.EigenschaftB in TextDatei t, gehe ..." machen muss, habe ich das (auch dank einiger Beispiele von DelphiPraxis :D) so gelöst: Speichern:
Delphi-Quellcode:
Laden:
procedure TMainForm.FileSaveAs1Execute(Sender: TObject);
var FileStream : TFilestream; begin if SaveDialog1.Execute then begin FileStream:=TFileStream.Create(SaveDialog1.Filename, fmCreate); Try FileStream.WriteComponent(MainForm.ActiveMDIChild); Finally FileStream.Free; end; end; end;
Delphi-Quellcode:
So das funktioniert auch soweit schon ganz gut.
procedure TMainForm.ToolButton12Click(Sender: TObject);
var Child: TForm; FileStream : TFileStream; begin if OpenDialog.Execute then begin FileStream:=TFileStream.Create(OpenDialog.Filename, fmOpenRead); Try //Eine Instanz eines Unterfensters wird erzeugt Child := TMDIChild.Create(MainForm); //Alle Enthaltenen Komponenten darin müssen aber erst gelöscht werden, //da beim Laden sonst eine Komponente mit demselben Namen schon existieren //würde. Ist quick'n'dirty, aber es funktioniert erstmal. While Child.ComponentCount > 0 do begin Child.Components[0].Destroy; end; //Der Stream mit allen Eigenschaften und Unterkomponenten wird nun in Child hineingelesen FileStream.ReadComponent(Child); Finally FileStream.Free; end; end; end; Nun aber das Problem: Aus den gesetzten Eigenschaften werden zur Laufzeit (eigene) Komponenten erzeugt (wen's interessiert: es handelt sich dabei um simple Software-Agenten, die miteinander auf einem simulierten Markt handeln sollen, sowie "Agenten-Pools", dem die Agenten angehören.) Kurz gesagt werden die dynamisch erzeugten Komponenten weder komplett geschrieben (das ist aber nur ein educated guess, weil ich mir die Datei mal im Wordpad angeschaut hab) noch geladen. Wenn ich das Teil wieder lade, wird nicht mehr "gehandelt". Nähere Infos: Es wird zuerst ein Agenten-Pool (auch: "MarketCoordinator") erzeugt. Das ist eine simple Spezialisierung von TComponent mit einigen zusätzlichen Eigenschaften. Der Owner dieser Komponente ist das MDI-Child. Dann werden die Agenten erzeugt. Auch sie sind Spezialisierungen von TComponent mit einigen eigenschaften und der methode "handeln()". Der Owner eines jeden Agenten ist der angegebene Agenten-Pool. Der Name und der Typ des Agent-Pools stehen in der gespeicherten Datei an letzter Stelle (siehe Anhang). Mehr aber nicht. Die Agenten tauchen so wie ich das sehe gar nicht auf. Meine Vermutung: Ohne euch auf eine falsche Spur zu kriegen! Kann es evtl. daran liegen, dass der Typ TMDIChild zwar mit den einzelnen Controls definiert ist (die stehen ja auch zur Entwurfszeit alle drin in der Definition/Formulardatei), aber nicht mit den dynamisch erzeugten Komponenten? Kann mir das nicht so richtig vorstellen, denn der Agenten-Pool wird ja zumindest mit seinem Namen auch mitgespeichert und der wurde ja dynamisch erzeugt. Habe auch schon versucht, mit der procedure "setSubComponent" was zu reißen, aber das scheint nichts zu bringen - ist wohl für die Entwurfszeit gedacht :( |
Re: Kniffliges Problem beim Serialisieren
Update: Nur zur Info: Meine Vermutung ist vermutlich falsch ;). Es liegt an etwas anderem. Wenn ich nur eine Unterkomponente speichere (z.B. sage ich myStream.WriteComponent(AgentPool1)) werden deren Eigenschaften (eigentlich nur name und tag und sowas gespeichert, aber nicht die in "AgentPool1.Components[]" enthaltenen Komponenten. Die Kinder werden also einfach nicht geschrieben. Bleibt die Frage, warum?
Wenn jemand sich der Sache mal annehmen will aber mehr infos braucht, besorg ich die euch gerne. Naja, es ist nun nicht so, als ob das ein Riesenproblem ist. Ist ja eine grundlegende Sache, wenn das nicht richtig gestreamt wird. Aber halt eine schwerwiegende :( |
Re: Kniffliges Problem beim Serialisieren
Hallo,
Zitat:
Grüße vom marabu |
Re: Kniffliges Problem beim Serialisieren
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe mal ein einfachst gehaltenes Projekt mit wenigen Zeilen erstellt, welches das Problem verdeutlicht:
(siehe Anhang) Dabei wird über den ersten Button eine Komponente created, deren Owner das Fenster ist. Der zweite Button erzeugt dann eine KOmponente, deren Owner die erste Komponente ist. Der dritte Button speichert das Ganze. Wenn ihr euch dann mal die gespeicherten Dateien anseht (am besten die Textform), seht ihr, dass am Ende die erste Komponente ("ComponentA") mit gespeichert wurde. Die zweite wird aber dann nicht mitgespeichert. Meine Frage ist nun, warum das so ist? Ich versuche schon massig Workarounds zu basteln. Bisher mit mäßigem Erfolg.
Code:
object Form1: TForm1
Left = 46 Top = 46 ActiveControl = Button3 Caption = 'Form1' ClientHeight = 204 ClientWidth = 470 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'Tahoma' Font.Style = [] OldCreateOrder = False Position = poDesigned Visible = True PixelsPerInch = 96 TextHeight = 13 object Button1: TButton Left = 32 Top = 8 Width = 154 Height = 25 Caption = 'ComponentA.Create(Form1)' TabOrder = 0 OnClick = Button1Click end object Button2: TButton Left = 32 Top = 56 Width = 209 Height = 25 Caption = 'ComponentB.Create(ComponentA)' TabOrder = 1 OnClick = Button2Click end object Button3: TButton Left = 32 Top = 104 Width = 89 Height = 25 Caption = 'Speichern' TabOrder = 2 OnClick = Button3Click end object ComponentA: TComponent end end
Delphi-Quellcode:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button3Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var ComponentA : TComponent; begin ComponentA := TComponent.Create(self); ComponentA.Name := 'ComponentA'; end; procedure TForm1.Button2Click(Sender: TObject); var ComponentB : TComponent; begin ComponentB := TComponent.Create(FindComponent('ComponentA')); ComponentB.Name := 'ComponentB'; // ComponentB.setSubComponent(true); end; procedure TForm1.Button3Click(Sender: TObject); var FileStream : TFilestream; StringStream : TStringStream; StringList : TStringList; begin FileStream:=TFileStream.Create('speichern-binaer.dat',fmCreate); StringStream := TStringStream.Create(''); Try FileStream.WriteComponent(Form1); FileStream.Position := 0; ObjectBinaryToText(FileStream, StringStream); StringList := TStringList.Create; StringList.add(StringStream.DataString); StringList.SaveToFile('speichern-textform.txt'); Finally FileStream.Free; StringStream.Free; end; end; end. |
Re: Kniffliges Problem beim Serialisieren
Hi,
hast du es schon mal mit Kompos zur Designzeit probiert? In der Regel reicht beim Create von Compos zur Laufzeit der Owner im Create nicht aus. Es muß explizit der Parent gesetzt werden.
Delphi-Quellcode:
Dabei ist wir aber noch nicht ganz klar warum ComponentA der Owner sein soll. Ich gehe mal davon aus, dass alle compos auf Form1 plaziert sein sollen. Dann würde ich immer Form1 als Owner und Parent halten. Für deine Hirarchie halte ich es für besser einen eigenen Member (Property) anzulegen.
procedure TForm1.Button2Click(Sender: TObject);
var ComponentB : TComponent; begin ComponentB := TComponent.Create(FindComponent('ComponentA')); ComponentB.Parent := ComponentA; ComponentB.Name := 'ComponentB'; // ComponentB.setSubComponent(true); end; Das mit dem Parent ist aber nur eine Vermutung. Die OH sagt auch folgendes Zitat:
P.S. Ich denke das mit dem Parent war nicht. Gruß oki |
Re: Kniffliges Problem beim Serialisieren
Infos:
- Die ComponentA soll deshalb ein Besitzer von ComponentB sein, weil sich sozusagen um eine Komposition bzw. Aggregation handelt. Die Agenten sollen einem AgentenPool angehören. Beim starten der Tätigkeiten des jeweiligen Agentenpools braucht der dann nur noch seine Kinder durchzugehen (for COmponents[i]) und die jeweils was machen zu lassen. Das wäre umständlicher, wenn die Agenten alle dem Formular gehören würden und jeder Pool sich erst wieder seine Agenten suchen müsste. - Die Eigenschaft Parent gibt es meines Wissens nach erst ab TControl - SetSubComponent sollte das mit der Rekursion eigentlich erledigen, hatte ich gehofft. Dem ist aber nicht so. Das ist das was mich stutzig macht. Beim Speichern eines Formulars in Delphi geht sowas doch normalerweise auch. - Was mich noch stutziger macht, ist, dass eben der Aufruf "WriteComponent(FindComponent('ComponentA'))" statt "WriteComponent(Form1)" beim Speichern nicht darin resultiert das nun ComponentA und dann ComponentB als deren Kind geschrieben wird, sondern ebenfalls nur ComponentA in der Datei steht. Danke für eure Hilfe! Würde mich freuen, wenn wir das Problem lösen können. |
Re: Kniffliges Problem beim Serialisieren
Zitat:
Dann bleibt dir wohl nur einen eigenen Algotythmus zu schreiben. Gruß oki |
Re: Kniffliges Problem beim Serialisieren
Naja können wir das jetzt als Bug bezeichnen und melden es CodeGear? Ich meine, probiert das mit dem Speichern von ComponentA statt Form1 ruhig mal aus. Da wird keine Unterkomponente gespeichert, auch wenn das so in der Onlinehilfe steht. Bei Form1 werden sie aber gespeichert (dann eben nur 1 Level). Ein bissl inkonsistent ... oder ich hab' wirklich was übersehen.
Ich werd das jetzt Workaroundmäßig mal versuchen, so zu lösen: Ich rufe WriteComponent(FormX) auf. Dann schaue ich hartcodiert nach ob in FormX Komponenten vom Typ "ComponentA" enthalten sind (Also genauer: AgentPools). Dann mache ich für jeden AgentPool eine neue Datei auf und gebe ihr den Namen "[Name des Agentpools].dat". Da rein werden dann die Kinder des AgentPools, also die Agenten bzw. im vereinfachten Bsp. alle ComponentB's geschrieben. Beim Lesen läuft es dann analog. Erst wird die Form geladen. Dann wird nach ComponentA's bzw. "AgentPools" geschaut, deren Name+'.dat' dann wieder den Zugriff auf den Stream mit den ComponentB's, sprich: Agents, ermöglicht. Meiner Meinung nach eine sehr unelegante und unschöne Lösung, aber ein Workaround muss ja offenbar her. :( |
Re: Kniffliges Problem beim Serialisieren
Hallo,
Zitat:
Grüße vom marabu |
Re: Kniffliges Problem beim Serialisieren
Dann hab' ich das tatsächlich übersehen und mir die Workaround-Arbeit wahrscheinlich umsonst gemacht. Ich würde das mit dem override von GetChildren aber dennoch noch einmal probieren. Kannst du mir dazu nochmal das Hilfethema sagen, wo das stand?
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:24 Uhr. |
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-2025 by Thomas Breitkreuz