Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2009-01

Saving user created component structure to LFM file

9 messages · 4 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Martin Friebe

Hi, I have a problem saving a certain component struicture to an LFM (lazarus) file and read it back.

since a good amount of code is in FPC, I am not sure which list to go to?

There is classes/writer.inc with has TBinaryObjectWriter. This is where things go first.
Then, later this gets translated into text. This translation seems to be stricter in terms of what can be nested.

Anyway the problem is, that if I want to save a component (that can be of different class/classes) then I can use WriteComponent. But only if I am in a Component that is itself written with WriteComponent.

In the final LFM it seems a Component can only be nested in an other component. It does not seem to be possible to put it in:
- Not in a subcomponent (because a subcomponent does not write it's class, but just the properties that are visible for the class given by the property

     property Foo: TPanel read .. write ...
  writes
     Foo.Name = 'abc'
     Foo.Top = 1
     ...

- Not in a TCollection, or at least I havent managed. Also again a TCollection is restricted to one classs for all Items
- Not Using DefineProperties
All propwerties with define property are of the kind Name = Value
I can add a list, but even inside the list I can not write a component

What I want to archive is theFollowing. I have

 TMainComponent = class (foo)
   property Options : TPersitentBar read  x write x;
 end

If It has to be it can be a TComponent, with SubComponent in TComponentStyle.
Problem 1) Options is always created in the constructor, it can not be stored as a nested Component in the LFM. If it was, it would exist twice (once created in the constructor, and once loaded from the lfm)

TPersitentBar should have a list of components, variable number, and different subclasses (could inherit from TPersistent, only Component if it makes it easier)

How can I get this list saved into the lfm? Each item has a diff class, and diff properties, so they must saved via WriteComponent. (Which does not work for DefineProerties)

And of course a default list of items is created in the constructor, so I need to detect if they are loaded, and remove the defaults.

Any Idea?

Example of the final structure (if only it was valid)

object MainComp1 : TMainComponent
Options.AllowFoo = 1
Options.AllowABC = 2
object Options.SubOptionsForCars : TCarOptions
MAxCar = 4

      end
      object Options.SubOptionsForTrains : TTrainOptions
          MAxTrain = 1
      end

end

Best Regards
Martin

Reply by email

Thierry Coq
to Martin Friebe

Dear Martin,
In Delphi, I frequently used the mecanism you described, but I had to use the root "WriteComponentRes" procedure, not the WriteComponent one, which is only available for subcomponents.
I did not use TCollection, which is capable of writing components (too complex for my usage). What I did do, was create container classes with TComponent as Parent. By default, TComponent stores all children. The root component must also give a name to each component, or the storage will not work.

The end result is the capability to store a hierarchy of objects in a consistent manner.

You should look for WriteComponentResFile in classes.inc and see how this work, with a few tries.

Hope this helps,
Thierry

Martin Friebe wrote 85 lines
Hi, I have a problem saving a certain component struicture to an LFM (lazarus) file and read it back. since a good amount of code is in FPC, I am not sure which list to go to? There is classes/writer.inc with has TBinaryObjectWriter. This is where things go first. Then, later this gets translated into text. This translation seems to be stricter in terms of what can be nested. Anyway the problem is, that if I want to save a component (that can be of different class/classes) then I can use WriteComponent. But only if I am in a Component that is itself written with WriteComponent. In the final LFM it seems a Component can only be nested in an other component. It does not seem to be possible to put it in: - Not in a subcomponent (because a subcomponent does not write it's class, but just the properties that are visible for the class given by the property property Foo: TPanel read .. write ... writes Foo.Name = 'abc' Foo.Top = 1 ... - Not in a TCollection, or at least I havent managed. Also again a TCollection is restricted to one classs for all Items - Not Using DefineProperties All propwerties with define property are of the kind Name = Value I can add a list, but even inside the list I can not write a component What I want to archive is theFollowing. I have TMainComponent = class (foo) property Options : TPersitentBar read x write x; end If It has to be it can be a TComponent, with SubComponent in TComponentStyle. Problem 1) Options is always created in the constructor, it can not be stored as a nested Component in the LFM. If it was, it would exist twice (once created in the constructor, and once loaded from the lfm) TPersitentBar should have a list of components, variable number, and different subclasses (could inherit from TPersistent, only Component if it makes it easier) How can I get this list saved into the lfm? Each item has a diff class, and diff properties, so they must saved via WriteComponent. (Which does not work for DefineProerties) And of course a default list of items is created in the constructor, so I need to detect if they are loaded, and remove the defaults. Any Idea? Example of the final structure (if only it was valid) object MainComp1 : TMainComponent Options.AllowFoo = 1 Options.AllowABC = 2 object Options.SubOptionsForCars : TCarOptions MAxCar = 4 end object Options.SubOptionsForTrains : TTrainOptions MAxTrain = 1 end end Best Regards Martin _______________________________________________ Lazarus mailing list http://www.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Martin Friebe
to Thierry Coq
Thierry Coq wrote 19 lines
Dear Martin, In Delphi, I frequently used the mecanism you described, but I had to use the root "WriteComponentRes" procedure, not the WriteComponent one, which is only available for subcomponents. I did not use TCollection, which is capable of writing components (too complex for my usage). What I did do, was create container classes with TComponent as Parent. By default, TComponent stores all children. The root component must also give a name to each component, or the storage will not work. The end result is the capability to store a hierarchy of objects in a consistent manner. You should look for WriteComponentResFile in classes.inc and see how this work, with a few tries. Hope this helps, Thierry

Thanks, I think I found a better way.

The outer container has no nested components of it's own. It seems I can hook into (override) TComponent.WriteState which is calling WriteComponentData (properties, then Children). Since no children are there, I can make my own calls to WriteComponent from there, supplying all the components, of all the helper objects.

On Read, I can Hook into ReadState. I can let the reader add them normally, and sort them out/once they have been read by the reader.

The remaining question is: Does that look like something that will stay compatible with future code? The generated LFM file definitely looks like any LFM file (it has a component, with properties first then other nested Components)

Thanks Martin

Show quoted text 94 lines
Martin Friebe wrote: > Hi, I have a problem saving a certain component struicture to an LFM > (lazarus) file and read it back. > > since a good amount of code is in FPC, I am not sure which list to go to? > > There is classes/writer.inc with has TBinaryObjectWriter. This is where > things go first. > Then, later this gets translated into text. This translation seems to be > stricter in terms of what can be nested. > > > Anyway the problem is, that if I want to save a component (that can be > of different class/classes) then I can use WriteComponent. But only if I > am in a Component that is itself written with WriteComponent. > > In the final LFM it seems a Component can only be nested in an other > component. It does not seem to be possible to put it in: > - Not in a subcomponent (because a subcomponent does not write it's > class, but just the properties that are visible for the class given by > the property > property Foo: TPanel read .. write ... > writes > Foo.Name = 'abc' > Foo.Top = 1 > ... > - Not in a TCollection, or at least I havent managed. Also again a > TCollection is restricted to one classs for all Items > - Not Using DefineProperties > All propwerties with define property are of the kind > Name = Value > I can add a list, but even inside the list I can not write a component > > > What I want to archive is theFollowing. I have > > TMainComponent = class (foo) > property Options : TPersitentBar read x write x; > end > > If It has to be it can be a TComponent, with SubComponent in > TComponentStyle. > Problem 1) Options is always created in the constructor, it can not be > stored as a nested Component in the LFM. If it was, it would exist twice > (once created in the constructor, and once loaded from the lfm) > > TPersitentBar should have a list of components, variable number, and > different subclasses (could inherit from TPersistent, only Component if > it makes it easier) > > How can I get this list saved into the lfm? Each item has a diff class, > and diff properties, so they must saved via WriteComponent. (Which does > not work for DefineProerties) > > And of course a default list of items is created in the constructor, so > I need to detect if they are loaded, and remove the defaults. > > Any Idea? > > Example of the final structure (if only it was valid) > > object MainComp1 : TMainComponent > Options.AllowFoo = 1 > Options.AllowABC = 2 > object Options.SubOptionsForCars : TCarOptions > MAxCar = 4 > end > object Options.SubOptionsForTrains : TTrainOptions > MAxTrain = 1 > end > end > > > Best Regards > Martin > > > > > _______________________________________________ > Lazarus mailing list > > http://www.lazarus.freepascal.org/mailman/listinfo/lazarus > > > > _______________________________________________ Lazarus mailing list http://www.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Mattias Gaertner
to Martin Friebe
Show quoted text 16 lines
[...] Thanks, I think I found a better way. The outer container has no nested components of it's own. It seems I can hook into (override) TComponent.WriteState which is calling WriteComponentData (properties, then Children). Since no children are there, I can make my own calls to WriteComponent from there, supplying all the components, of all the helper objects. On Read, I can Hook into ReadState. I can let the reader add them normally, and sort them out/once they have been read by the reader. The remaining question is: Does that look like something that will stay compatible with future code? The generated LFM file definitely looks like any LFM file (it has a component, with properties first then other nested Components)

To store a property that is a component and which is not owned by the Lookuproot (form, datamodule, frame) you must set the csSubComponent flag in ComponentStyle.

To store a list use TCollection. If you can not use TCollection please explain why not.

To store data of arbitrary length/format, use DefineProperties. This has a drawback: In case of an error the IDE can not help fixing it.

Mattias

Reply by email

Martin Friebe
to Mattias Gaertner
Mattias Gaertner wrote 22 lines
> [...] > Thanks, I think I found a better way. > > The outer container has no nested components of it's own. It seems I > can hook into (override) TComponent.WriteState which is calling > WriteComponentData (properties, then Children). Since no children are > there, I can make my own calls to WriteComponent from there, > supplying all the components, of all the helper objects. > > On Read, I can Hook into ReadState. I can let the reader add them > normally, and sort them out/once they have been read by the reader. > > The remaining question is: Does that look like something that will > stay compatible with future code? The generated LFM file definitely > looks like any LFM file (it has a component, with properties first > then other nested Components) > To store a property that is a component and which is not owned by the Lookuproot (form, datamodule, frame) you must set the csSubComponent flag in ComponentStyle.

yes, I found the csSubComponent. But it stores the only the properties of the SubComponent, not the Component's Class.

SubComponent does (Within the list of properties of the actual component): MySubCompProp.Value1 = 120
MySubCompProp.Foo = 'abc'

And also subcomps, look (correctly) at the definition of the property. It the Property is defined as TFoo (" property mySubCompProp: TFoo reads FMyFoo write FMyFoo;" , then all properties known by this class (TFoo) are stored. In My case the actual Object may be a descend and of TFoo (that is the objects stored in FMyFoo), and have additional Properties (This additional Properties would be lost)

My Components are in a TList, the have different classes, so I need to save them the same way like nested components:
object MySubCompFromListClassABC: TSubCompClassABC Value1 = 120

  end;

The Real Background is SynEdits Gutter.

The Gutter, I have managed to get saved. (via csSubComponent). But then the Gutter has a list of GutterParts (all of them Objects (and I can/will make them Components)). I need to save all those GutterParts. [ I know, I will also need a Property Editor for the object inspector, but that's not the issue ]

If A Gutter is created, with a new SynEdit, and *not* loaded from LFM; then it creates a default set.
If it is loaded, it needs to remove the default set (can be done in ReadState of SynEdit (SynEdit can inform the gutter) / Can depend on The LCL Version, so loading a 0.9.26 form, will keep the defaults, as no saved parts can exist)

To store a list use TCollection. If you can not use TCollection please explain why not.

Because All collection Items a of the same class. If I make that TGutterPartBase, then only properties exposed by TGutterPartBase are saved. but each GutterPart has additional published properties. (Same applies, if I make the CollectionItem a wrapper class with a property "property TheRealGutterPart: TGutterPartBase" => because as csSubComponen it does not save the class-info)
Also even If I manage to get this subclasses into the collection (which is hard enough, as it is not supposed to be), If the collection is loaded, it restores them all to the base class, because it never saved the class-info

To store data of arbitrary length/format, use DefineProperties. This has a drawback: In case of an error the IDE can not help fixing it.

DefineProperties only takes simple values. Because in define properties I must give it the name. Then In Read/WriteProc I deal with the value. So define Property will always create an LFM entry like: MyDefinedPropertyName = xxx
xxx Comes from the writecallback.

If the write callback attempts to do WriteComponent, then this will fail later, when the binary format is translated into text-lfm-format (because " MyDefinedPropertyName = MyObject: TMyClass" is not allowed

That is even, if the WriteCallback starts a list first, it will still fail. MyDefinedPropertyName = (
MyObject: TMyClass
....

With "DefineProperties" I have 2 (undesirable) possibilities:
1) use DefineBinarryProperties => and write my own format, no one can edit it, and why invent the wheel again?
2) use DefineProperties and define a large set of properties:
- define a classname property for each of my objects
- and define a property for each value that each of my objects needs to store.
Again there is code (WriteComponent) that does streaming of objects including class info; why invent the wheel again?

---------------

So as I see it I found 2 possibilities

1) The GutterParts are created as components owned by the SynEdit (or does it have to be the form; if it does have to be the form then there will be issues if there is more than one synedit.... ). that will be a lot of easy to break maintenance work. because you need to search through the component list to find the Objects.
And when loading I still need to remove the Objects created in Synedit.Create or I get double entries.
- Also I tried it, and SynEdit did not seem to stream them (MyGutterPart := TcomponentBasedClass.Create(TheSynEditAsOwner);) => nothing in the lfm

2) Maintain the objects as they are, but use SynEdit.WriteState to append them to the lfm
(They can easily be caught, if they get read (by the default reader) during loading

Best Regards
Martin

Reply by email

Martin Friebe
to Leonardo M. Ramé: In Delphi you have to explicitly set the subcomponent: Example: constructor…
Leonardo M. Ram? wrote 14 lines
In Delphi you have to explicitly set the subcomponent: Example: constructor TMyComponent.create.... begin ... FSubComponent := TSubComponent.Create(Self); ... FSubComponent.SetSubComponent(True); ... end;
Same here, just yopu can modify the flag directly:
  include(ComponentStyle, csSubComponent);

But that only streams the properties, and does assume a specific class. It doesn't work for Anything that can change it;'s class.

In fact in Lazarus you can skip this, IF the property has write access. There are 2 conditons that will save the valuse of a Class-property
1) property is TPersistend or descendant, and has both read and write access (write can end up in assign)
2) property is TComponent or descendant, and has csSubComponent (in this case it does not need a write access method)

Show quoted text 150 lines
Martin Friebe escribi?: > Mattias Gaertner wrote: > > >>> [...] >>> Thanks, I think I found a better way. >>> >>> The outer container has no nested components of it's own. It seems I >>> can hook into (override) TComponent.WriteState which is calling >>> WriteComponentData (properties, then Children). Since no children are >>> there, I can make my own calls to WriteComponent from there, >>> supplying all the components, of all the helper objects. >>> >>> On Read, I can Hook into ReadState. I can let the reader add them >>> normally, and sort them out/once they have been read by the reader. >>> >>> The remaining question is: Does that look like something that will >>> stay compatible with future code? The generated LFM file definitely >>> looks like any LFM file (it has a component, with properties first >>> then other nested Components) >>> >>> >>> >> To store a property that is a component and which is not owned by the >> Lookuproot (form, datamodule, frame) you must set the csSubComponent >> flag in ComponentStyle. >> >> >> > yes, I found the csSubComponent. But it stores the only the properties > of the SubComponent, not the Component's Class. > > SubComponent does (Within the list of properties of the actual component): > MySubCompProp.Value1 = 120 > MySubCompProp.Foo = 'abc' > > And also subcomps, look (correctly) at the definition of the property. > It the Property is defined as TFoo (" property mySubCompProp: TFoo reads > FMyFoo write FMyFoo;" , then all properties known by this class (TFoo) > are stored. In My case the actual Object may be a descend and of TFoo > (that is the objects stored in FMyFoo), and have additional Properties > (This additional Properties would be lost) > > > My Components are in a TList, the have different classes, so I need to > save them the same way like nested components: > object MySubCompFromListClassABC: TSubCompClassABC > Value1 = 120 > end; > > > The Real Background is SynEdits Gutter. > > The Gutter, I have managed to get saved. (via csSubComponent). > But then the Gutter has a list of GutterParts (all of them Objects (and > I can/will make them Components)). I need to save all those GutterParts. > [ I know, I will also need a Property Editor for the object inspector, > but that's not the issue ] > > If A Gutter is created, with a new SynEdit, and *not* loaded from LFM; > then it creates a default set. > If it is loaded, it needs to remove the default set (can be done in > ReadState of SynEdit (SynEdit can inform the gutter) / Can depend on The > LCL Version, so loading a 0.9.26 form, will keep the defaults, as no > saved parts can exist) > > > >> To store a list use TCollection. If you can not use TCollection please >> explain why not. >> >> >> > Because All collection Items a of the same class. If I make that > TGutterPartBase, then only properties exposed by TGutterPartBase are > saved. but each GutterPart has additional published properties. (Same > applies, if I make the CollectionItem a wrapper class with a property > "property TheRealGutterPart: TGutterPartBase" => because as > csSubComponen it does not save the class-info) > Also even If I manage to get this subclasses into the collection (which > is hard enough, as it is not supposed to be), If the collection is > loaded, it restores them all to the base class, because it never saved > the class-info > > >> To store data of arbitrary length/format, use DefineProperties. This >> has a drawback: In case of an error the IDE can not help fixing it. >> >> >> > DefineProperties only takes simple values. Because in define properties > I must give it the name. Then In Read/WriteProc I deal with the value. > So define Property will always create an LFM entry like: > MyDefinedPropertyName = xxx > xxx Comes from the writecallback. > > If the write callback attempts to do WriteComponent, then this will fail > later, when the binary format is translated into text-lfm-format > (because " MyDefinedPropertyName = MyObject: TMyClass" is not allowed > > That is even, if the WriteCallback starts a list first, it will still fail. > MyDefinedPropertyName = ( > MyObject: TMyClass > .... > > With "DefineProperties" I have 2 (undesirable) possibilities: > 1) use DefineBinarryProperties => and write my own format, no one can > edit it, and why invent the wheel again? > 2) use DefineProperties and define a large set of properties: > - define a classname property for each of my objects > - and define a property for each value that each of my objects needs > to store. > Again there is code (WriteComponent) that does streaming of objects > including class info; why invent the wheel again? > > --------------- > > So as I see it I found 2 possibilities > > 1) The GutterParts are created as components owned by the SynEdit (or > does it have to be the form; if it does have to be the form then there > will be issues if there is more than one synedit.... ). that will be a > lot of easy to break maintenance work. because you need to search > through the component list to find the Objects. > And when loading I still need to remove the Objects created in > Synedit.Create or I get double entries. > - Also I tried it, and SynEdit did not seem to stream them (MyGutterPart > := TcomponentBasedClass.Create(TheSynEditAsOwner);) => nothing in the lfm > > 2) Maintain the objects as they are, but use SynEdit.WriteState to > append them to the lfm > (They can easily be caught, if they get read (by the default reader) > during loading > > > Best Regards > Martin > _______________________________________________ > Lazarus mailing list > > http://www.lazarus.freepascal.org/mailman/listinfo/lazarus > > > _______________________________________________ Lazarus mailing list http://www.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Martin Friebe
to Martin Friebe: yes, I found the csSubComponent. But it stores the only the properties of the…

So as I see it I found 2 possibilities

Show quoted text 16 lines
1) The GutterParts are created as components owned by the SynEdit (or does it have to be the form; if it does have to be the form then there will be issues if there is more than one synedit.... ). that will be a lot of easy to break maintenance work. because you need to search through the component list to find the Objects. And when loading I still need to remove the Objects created in Synedit.Create or I get double entries. - Also I tried it, and SynEdit did not seem to stream them (MyGutterPart := TcomponentBasedClass.Create(TheSynEditAsOwner);) => nothing in the lfm 2) Maintain the objects as they are, but use SynEdit.WriteState to append them to the lfm (They can easily be caught, if they get read (by the default reader) during loading

There may be a 3rd way.
Inside TWriter.WriteChildren is a call to
TComponent.GetChildren(RefToWriteComponetn, RootComponnet)

This is used in TControl and TFrame and others to return (one by one, via callback) all the children that should be written.

That is probably the best place to go; Only I was/am not sure, if this is used explicitly for the Component serialization?

Martin

Reply by email

Leonardo M. Ramé
to Martin Friebe: yes, I found the csSubComponent. But it stores the only the properties of the…

In Delphi you have to explicitly set the subcomponent:

Example:

constructor TMyComponent.create....
begin
   ...
  FSubComponent := TSubComponent.Create(Self);
  ...
  FSubComponent.SetSubComponent(True);
  ...
end;

Leonardo M. Ram?
Griensu S.A. - Medical IT C?rdoba
Tel.: 0351 - 4247979

Martin Friebe escribi?:

Show quoted text 135 lines
Mattias Gaertner wrote: >> [...] >> Thanks, I think I found a better way. >> >> The outer container has no nested components of it's own. It seems I >> can hook into (override) TComponent.WriteState which is calling >> WriteComponentData (properties, then Children). Since no children are >> there, I can make my own calls to WriteComponent from there, >> supplying all the components, of all the helper objects. >> >> On Read, I can Hook into ReadState. I can let the reader add them >> normally, and sort them out/once they have been read by the reader. >> >> The remaining question is: Does that look like something that will >> stay compatible with future code? The generated LFM file definitely >> looks like any LFM file (it has a component, with properties first >> then other nested Components) >> >> > To store a property that is a component and which is not owned by the > Lookuproot (form, datamodule, frame) you must set the csSubComponent > flag in ComponentStyle. > > yes, I found the csSubComponent. But it stores the only the properties of the SubComponent, not the Component's Class. SubComponent does (Within the list of properties of the actual component): MySubCompProp.Value1 = 120 MySubCompProp.Foo = 'abc' And also subcomps, look (correctly) at the definition of the property. It the Property is defined as TFoo (" property mySubCompProp: TFoo reads FMyFoo write FMyFoo;" , then all properties known by this class (TFoo) are stored. In My case the actual Object may be a descend and of TFoo (that is the objects stored in FMyFoo), and have additional Properties (This additional Properties would be lost) My Components are in a TList, the have different classes, so I need to save them the same way like nested components: object MySubCompFromListClassABC: TSubCompClassABC Value1 = 120 end; The Real Background is SynEdits Gutter. The Gutter, I have managed to get saved. (via csSubComponent). But then the Gutter has a list of GutterParts (all of them Objects (and I can/will make them Components)). I need to save all those GutterParts. [ I know, I will also need a Property Editor for the object inspector, but that's not the issue ] If A Gutter is created, with a new SynEdit, and *not* loaded from LFM; then it creates a default set. If it is loaded, it needs to remove the default set (can be done in ReadState of SynEdit (SynEdit can inform the gutter) / Can depend on The LCL Version, so loading a 0.9.26 form, will keep the defaults, as no saved parts can exist) > To store a list use TCollection. If you can not use TCollection please > explain why not. > > Because All collection Items a of the same class. If I make that TGutterPartBase, then only properties exposed by TGutterPartBase are saved. but each GutterPart has additional published properties. (Same applies, if I make the CollectionItem a wrapper class with a property "property TheRealGutterPart: TGutterPartBase" => because as csSubComponen it does not save the class-info) Also even If I manage to get this subclasses into the collection (which is hard enough, as it is not supposed to be), If the collection is loaded, it restores them all to the base class, because it never saved the class-info > To store data of arbitrary length/format, use DefineProperties. This > has a drawback: In case of an error the IDE can not help fixing it. > > DefineProperties only takes simple values. Because in define properties I must give it the name. Then In Read/WriteProc I deal with the value. So define Property will always create an LFM entry like: MyDefinedPropertyName = xxx xxx Comes from the writecallback. If the write callback attempts to do WriteComponent, then this will fail later, when the binary format is translated into text-lfm-format (because " MyDefinedPropertyName = MyObject: TMyClass" is not allowed That is even, if the WriteCallback starts a list first, it will still fail. MyDefinedPropertyName = ( MyObject: TMyClass .... With "DefineProperties" I have 2 (undesirable) possibilities: 1) use DefineBinarryProperties => and write my own format, no one can edit it, and why invent the wheel again? 2) use DefineProperties and define a large set of properties: - define a classname property for each of my objects - and define a property for each value that each of my objects needs to store. Again there is code (WriteComponent) that does streaming of objects including class info; why invent the wheel again? --------------- So as I see it I found 2 possibilities 1) The GutterParts are created as components owned by the SynEdit (or does it have to be the form; if it does have to be the form then there will be issues if there is more than one synedit.... ). that will be a lot of easy to break maintenance work. because you need to search through the component list to find the Objects. And when loading I still need to remove the Objects created in Synedit.Create or I get double entries. - Also I tried it, and SynEdit did not seem to stream them (MyGutterPart := TcomponentBasedClass.Create(TheSynEditAsOwner);) => nothing in the lfm 2) Maintain the objects as they are, but use SynEdit.WriteState to append them to the lfm (They can easily be caught, if they get read (by the default reader) during loading Best Regards Martin _______________________________________________ Lazarus mailing list http://www.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email

Leonardo M. Ramé
to Martin Friebe: Same here, just yopu can modify the flag directly: include(ComponentStyle,…

Is the subcomponent declared as a "published" property of its owner?. Maybe that's why the streaming system can't view it.

Leonardo M. Ram?
Griensu S.A. - Medical IT C?rdoba
Tel.: 0351 - 4247979

Martin Friebe escribi?:

Show quoted text 197 lines
Leonardo M. Ram? wrote: > In Delphi you have to explicitly set the subcomponent: > > Example: > > constructor TMyComponent.create.... > begin > ... > FSubComponent := TSubComponent.Create(Self); > ... > FSubComponent.SetSubComponent(True); > ... > end; > > > Same here, just yopu can modify the flag directly: include(ComponentStyle, csSubComponent); But that only streams the properties, and does assume a specific class. It doesn't work for Anything that can change it;'s class. In fact in Lazarus you can skip this, IF the property has write access. There are 2 conditons that will save the valuse of a Class-property 1) property is TPersistend or descendant, and has both read and write access (write can end up in assign) 2) property is TComponent or descendant, and has csSubComponent (in this case it does not need a write access method) > Martin Friebe escribi?: > > >> Mattias Gaertner wrote: >> >> >> >>>> [...] >>>> Thanks, I think I found a better way. >>>> >>>> The outer container has no nested components of it's own. It seems I >>>> can hook into (override) TComponent.WriteState which is calling >>>> WriteComponentData (properties, then Children). Since no children are >>>> there, I can make my own calls to WriteComponent from there, >>>> supplying all the components, of all the helper objects. >>>> >>>> On Read, I can Hook into ReadState. I can let the reader add them >>>> normally, and sort them out/once they have been read by the reader. >>>> >>>> The remaining question is: Does that look like something that will >>>> stay compatible with future code? The generated LFM file definitely >>>> looks like any LFM file (it has a component, with properties first >>>> then other nested Components) >>>> >>>> >>>> >>>> >>> To store a property that is a component and which is not owned by the >>> Lookuproot (form, datamodule, frame) you must set the csSubComponent >>> flag in ComponentStyle. >>> >>> >>> >>> >> yes, I found the csSubComponent. But it stores the only the properties >> of the SubComponent, not the Component's Class. >> >> SubComponent does (Within the list of properties of the actual component): >> MySubCompProp.Value1 = 120 >> MySubCompProp.Foo = 'abc' >> >> And also subcomps, look (correctly) at the definition of the property. >> It the Property is defined as TFoo (" property mySubCompProp: TFoo reads >> FMyFoo write FMyFoo;" , then all properties known by this class (TFoo) >> are stored. In My case the actual Object may be a descend and of TFoo >> (that is the objects stored in FMyFoo), and have additional Properties >> (This additional Properties would be lost) >> >> >> My Components are in a TList, the have different classes, so I need to >> save them the same way like nested components: >> object MySubCompFromListClassABC: TSubCompClassABC >> Value1 = 120 >> end; >> >> >> The Real Background is SynEdits Gutter. >> >> The Gutter, I have managed to get saved. (via csSubComponent). >> But then the Gutter has a list of GutterParts (all of them Objects (and >> I can/will make them Components)). I need to save all those GutterParts. >> [ I know, I will also need a Property Editor for the object inspector, >> but that's not the issue ] >> >> If A Gutter is created, with a new SynEdit, and *not* loaded from LFM; >> then it creates a default set. >> If it is loaded, it needs to remove the default set (can be done in >> ReadState of SynEdit (SynEdit can inform the gutter) / Can depend on The >> LCL Version, so loading a 0.9.26 form, will keep the defaults, as no >> saved parts can exist) >> >> >> >> >>> To store a list use TCollection. If you can not use TCollection please >>> explain why not. >>> >>> >>> >>> >> Because All collection Items a of the same class. If I make that >> TGutterPartBase, then only properties exposed by TGutterPartBase are >> saved. but each GutterPart has additional published properties. (Same >> applies, if I make the CollectionItem a wrapper class with a property >> "property TheRealGutterPart: TGutterPartBase" => because as >> csSubComponen it does not save the class-info) >> Also even If I manage to get this subclasses into the collection (which >> is hard enough, as it is not supposed to be), If the collection is >> loaded, it restores them all to the base class, because it never saved >> the class-info >> >> >> >>> To store data of arbitrary length/format, use DefineProperties. This >>> has a drawback: In case of an error the IDE can not help fixing it. >>> >>> >>> >>> >> DefineProperties only takes simple values. Because in define properties >> I must give it the name. Then In Read/WriteProc I deal with the value. >> So define Property will always create an LFM entry like: >> MyDefinedPropertyName = xxx >> xxx Comes from the writecallback. >> >> If the write callback attempts to do WriteComponent, then this will fail >> later, when the binary format is translated into text-lfm-format >> (because " MyDefinedPropertyName = MyObject: TMyClass" is not allowed >> >> That is even, if the WriteCallback starts a list first, it will still fail. >> MyDefinedPropertyName = ( >> MyObject: TMyClass >> .... >> >> With "DefineProperties" I have 2 (undesirable) possibilities: >> 1) use DefineBinarryProperties => and write my own format, no one can >> edit it, and why invent the wheel again? >> 2) use DefineProperties and define a large set of properties: >> - define a classname property for each of my objects >> - and define a property for each value that each of my objects needs >> to store. >> Again there is code (WriteComponent) that does streaming of objects >> including class info; why invent the wheel again? >> >> --------------- >> >> So as I see it I found 2 possibilities >> >> 1) The GutterParts are created as components owned by the SynEdit (or >> does it have to be the form; if it does have to be the form then there >> will be issues if there is more than one synedit.... ). that will be a >> lot of easy to break maintenance work. because you need to search >> through the component list to find the Objects. >> And when loading I still need to remove the Objects created in >> Synedit.Create or I get double entries. >> - Also I tried it, and SynEdit did not seem to stream them (MyGutterPart >> := TcomponentBasedClass.Create(TheSynEditAsOwner);) => nothing in the lfm >> >> 2) Maintain the objects as they are, but use SynEdit.WriteState to >> append them to the lfm >> (They can easily be caught, if they get read (by the default reader) >> during loading >> >> >> Best Regards >> Martin >> _______________________________________________ >> Lazarus mailing list >> >> http://www.lazarus.freepascal.org/mailman/listinfo/lazarus >> >> >> >> > _______________________________________________ > Lazarus mailing list > > http://www.lazarus.freepascal.org/mailman/listinfo/lazarus > > _______________________________________________ Lazarus mailing list http://www.lazarus.freepascal.org/mailman/listinfo/lazarus

Reply by email