[Lazarus] Saving user created component structure to LFM file

Martin Friebe lazarus at mfriebe.de
Mon Jan 12 13:40:22 CET 2009


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



More information about the Lazarus mailing list