Search code examples
c++buildervcltcollectionitemtownedcollection

How to add a 2-times derived class of TCollectionItem to TOwnedCollection?


I want to implement a collection or list using TOwnedCollection / TCollectionItem. I need a persistent list (to load and create from a FileStream) of classes with polymorphism.

Here is (part of) my code so far, but I didn't succeed to create the derived class TGenerator instead of its parent TPowerComponent and add it to the Collection.

//-------------------------------------------------------------------------------------
class TPCCollection : public TOwnedCollection
    {
            typedef TOwnedCollection inherited;
    private:
            TPowerComponent* __fastcall GetPowerComponent(int Index);
            void __fastcall SetPowerComponent(int Index, TPowerComponent *Value);

    public:
            __fastcall TPCCollection(TPersistent *Owner);

            HIDESBASE TPowerComponent* __fastcall Add(void);
            HIDESBASE TPowerComponent* __fastcall Insert(int Index);

            __property TPowerComponent* PCCollection[int Index] = {read=GetPowerComponent, write=SetPowerComponent};
};

//-------------------------------------------------------------------------------------
class TPowerComponent : public TCollectionItem
{
    typedef TCollectionItem inherited;
public :
    int X, Y, Rotation;
    PowSymbType HisType;

    __fastcall TPowerComponent(TCollection *Collection, PowSymbType AType );
    void __fastcall Assign(TPersistent *Source);
    virtual void __fastcall Paint(TCanvas * Canvas);
};
//-------------------------------------------------------------------------------------
class TGenerator : public TPowerComponent
{
            typedef TPowerComponent inherited;
public :
    double PG, Qgmin, Qgmax, Vsch;

    __fastcall TGenerator(TCollection *Collection, PowSymbType AType );
    void __fastcall Assign(TPersistent *Source);
    virtual void __fastcall Paint(TCanvas * Canvas);    
    };
//-------------------------------------------------------------------------------------
// implementation
//-------------------------------------------------------------------------------------
//  TPCCOllection
//-------------------------------------------------------------------------------------
__fastcall TPCCollection::TPCCollection(TPersistent *Owner)
        : TOwnedCollection(Owner, __classid(TPowerComponent))
{
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::Add()
{
    return static_cast<TPowerComponent>(inherited::Add());
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::Insert(int Index)
{
    return static_cast<TPowerComponent>(inherited::Insert(Index));
}
//-------------------------------------------------------------------------------------
TPowerComponent* __fastcall TPCCollection::GetPowerComponent(int Index)
{
    return static_cast<TPowerComponent>(inherited::GetItem(Index));
}
//-------------------------------------------------------------------------------------
void __fastcall TPCCollection::SetPowerComponent(int Index, TPowerComponent *Value)
{
    inherited::SetItem(Index, Value);
}
//-------------------------------------------------------------------------------------
//  TPowerComponent
//-------------------------------------------------------------------------------------
__fastcall TPowerComponent::TPowerComponent(TCollection *Collection, PowSymbType AType )
        : TCollectionItem(Collection)
{
    HisType=AType;
    Rotation=0;
}
//-------------------------------------------------------------------------------------
void __fastcall TPowerComponent::Assign(TPersistent *Source)
{
    TPowerComponent *Src = dynamic_cast<TPowerComponent>(Source);
    if( Src )
        {
                // copy members from Src...
        }
    else    inherited::Assign(Source);
}
//-------------------------------------------------------------------------------------
// se dessine
void __fastcall TPowerComponent::Paint(TCanvas * Canvas)
{
...
}
//-------------------------------------------------------------------------------------
//  TGenerator
//-------------------------------------------------------------------------------------
__fastcall TGenerator::TGenerator(TCollection *Collection, PowSymbType AType )
        :TPowerComponent( Collection, AType )
{
    PG=0; Qgmin=0; Qgmax=0; Vsch=1.0; Con=-1;
}
//-------------------------------------------------------------------------------------
void __fastcall TGenerator::Assign(TPersistent *Source)
{
    TGenerator *Src = dynamic_cast<TGenerator>(Source);
    if( Src )
        {
                // copy members from Src...
        }
    else    inherited::Assign(Source);
}



//-------------------------------------------------------------------------------------
//  Usage 
TPCCollection * NetWork = new TPCCollection(this);

//  Usage to Access all the collection
for( int i=0; i< NetWork->Count; i++)
    {
    ((TPowerComponent*)(NetWork->Items[i]))->Paint(Canvas);
    }

To add a TGenerator and not a TPowerComponent, I use:

TGenerator * Gen=new TGenerator( NetWork, Generator);

The creation of the TCollectionItem child automatically add itself to the TCollection

The problem here is that we can't separate the process of item creation from adding it to the collection.

When I need another list that can have some of the items of the first collection list, for example, SelectedComponents can have one or some of the Items of the NetWork Collection, without recreating them.

This can be done with

std::list<TPowerComponent*> SelectedComponents;

but I can't write/read them using FileStream / persistent list. I need to put them in a TCollection but without recreating them.

How?


Solution

  • The RTL's native DFM streaming for TCollection objects only partially supports polymorphic TCollectionItem classes.

    You can add polymorphic TCollectionItem objects to a TCollection in code (at runtime, as well as at design-time with the aid of a custom editor), as long as they all derive from a common base class that is passed to the TCollection constructor. And such a collection can even be saved as-is to a DFM.

    However, when loading back a DFM, native streaming will force all collection items read from the DFM to use whatever TCollectionItem class type you pass to the TCollection constructor. So, polymorphic classes can't be loaded natively.

    The only way to override that behavior is to disable native streaming for the collection (make your TCollection property be non-published, or at least mark it as stored=false), and then stream the collection items manually.

    Have your main component (or whatever TPersistent class owns the collection) override the virtual DefineProperties() method to call TFiler.DefineProperty() to register custom reading/writing methods for streaming the collection items. To support polymorphic classes, you will have to write each item's ClassName to the DFM before writing its property values, then read the name back so you know which class to instantiate before then reading property values.