[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