Board index » cppbuilder » Saving component's properties of an aggregated component

Saving component's properties of an aggregated component

Has anybody experience to write aggregated components?
I can't make my component to save the properites of the aggregated components. Here is sample of my source.
The properties if the inner classes FADOConnection, FFIBDatabase are not saved in the .dfm file, when I change them at design time. Could you give me some suggestions to resolve this problem?
//======== ConnectFIB_ADO.cpp ==================
#include <vcl.h>
#pragma hdrstop

#include "ConnectFIB_ADO.h"
#pragma package(smart_init)
//------------------------
static inline void ValidCtrCheck(TConnectFIB_ADO *)
{
        new TConnectFIB_ADO(NULL);

Quote
}

//------------------------
__fastcall TConnectFIB_ADO::TConnectFIB_ADO(TComponent* Owner)
        : TComponent(Owner)
{
        FIsWriteDebug = true;
        FADOConnection = new TADOConnection(this);
        FFIBDatabase = new TpFIBDatabase(this);
        FFIBTransaction = new TpFIBTransaction(this);

        FFIBDatabase->Name    = "FFIBDatabase";
        FFIBTransaction->Name = "FFIBTransaction";
        FADOConnection->Name  = "FADOConnection";

        FFIBTransaction->DefaultDatabase = FFIBDatabase;
        FFIBDatabase->DefaultTransaction = FFIBTransaction;

Quote
}

//------------------------
__fastcall TConnectFIB_ADO::~TConnectFIB_ADO()
{
        trace("destructor ~TConnectFIB_ADO()");
Quote
}

//------------------------
namespace Connectfib_ado
{
        void __fastcall PACKAGE Register()
        {
                 RegisterPropertyEditor(__typeinfo(TADOConnection), 0L, "", __classid(TClassProperty));
                 RegisterPropertyEditor(__typeinfo(TpFIBDatabase), 0L, "", __classid(TClassProperty));
                 RegisterPropertyEditor(__typeinfo(TpFIBTransaction), 0L, "", __classid(TClassProperty));

                 TComponentClass classes[1] = {__classid(TConnectFIB_ADO)};
                 RegisterComponents("FIBPlus", classes, 0);
        }

Quote
}

//------------------------
void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
{
 if(FADOConnection != value) FADOConnection = value;
Quote
}

//------------------------
TADOConnection* __fastcall TConnectFIB_ADO::GetADOConnection()
{        return FADOConnection; }
//------------------------
void __fastcall TConnectFIB_ADO::SetFIBDatabase(TpFIBDatabase* value)
{
  if(FFIBDatabase != value) FFIBDatabase = value;
Quote
}

//------------------------
TpFIBDatabase* __fastcall TConnectFIB_ADO::GetFIBDatabase()
{      return FFIBDatabase; }
//------------------------
void __fastcall TConnectFIB_ADO::SetFIBTransaction(TpFIBTransaction* value)
{
        if(FFIBTransaction != value) {
                FFIBTransaction = value;
        }
Quote
}

//------------------------
TpFIBTransaction* __fastcall TConnectFIB_ADO::GetFIBTransaction()
{ return FFIBTransaction; }
//------------------------
void __fastcall TConnectFIB_ADO::SetUseDBAccess(TUseDBAccess value)
{
        if(FUseDBAccess != value) {
                FUseDBAccess = value;
        }
Quote
}

//------------------------
TUseDBAccess __fastcall TConnectFIB_ADO::GetUseDBAccess()
{        return FUseDBAccess; }
//------------------------
void __fastcall TConnectFIB_ADO::trace(AnsiString s)
{
  if (FIsWriteDebug) WriteDebug(s, "ConnectFIB_ADO" );

Quote
}

//==========================

//======== ConnectFIB_ADO.h ==================
#ifndef ConnectFIB_ADOH
#define ConnectFIB_ADOH
//------------------------
#include <SysUtils.hpp>
#include <Controls.hpp>
#include <Classes.hpp>
#include <Forms.hpp>
#include <ADODB.hpp>
#include <dsgnintf.hpp>
#include "pFIBDatabase.hpp"
#include "UnitCheck.hpp"

enum TUseDBAccess {useFIB, useADO}  ;
//------------------------
class PACKAGE TConnectFIB_ADO : public TComponent
{
private:
        TADOConnection* FADOConnection;
        TpFIBDatabase* FFIBDatabase;
        TpFIBTransaction* FFIBTransaction;
        void __fastcall SetADOConnection(TADOConnection* value);
        TADOConnection* __fastcall GetADOConnection();
        void __fastcall SetFIBDatabase(TpFIBDatabase* value);
        TpFIBDatabase* __fastcall GetFIBDatabase();
        void __fastcall SetFIBTransaction(TpFIBTransaction* value);
        TpFIBTransaction* __fastcall GetFIBTransaction();
        TUseDBAccess FUseDBAccess;
        void __fastcall SetUseDBAccess(TUseDBAccess value);
        TUseDBAccess __fastcall GetUseDBAccess();

protected:
public:
        __fastcall TConnectFIB_ADO(TComponent* Owner);
        __fastcall ~TConnectFIB_ADO();
__published:
        __property TADOConnection* ADOConnection  = { read=GetADOConnection, write=SetADOConnection };
        __property TpFIBDatabase* FIBDatabase  = { read=GetFIBDatabase, write=SetFIBDatabase};
        __property TpFIBTransaction* FIBTransaction  = { read=GetFIBTransaction, write=SetFIBTransaction };
        __property TUseDBAccess UseDBAccess  = { read=GetUseDBAccess, write=SetUseDBAccess, default=0 };

Quote
};

//------------------------
#endif
//==========================

I'm using BCB 5.0 (Update pack 1)
Thanks in advance for you help.

Andy

 

Re:Saving component's properties of an aggregated component


Quote
"Andy" <kua...@yahoo.co.uk> wrote in message

news:3dd4f24e$1@newsgroups.borland.com...

Quote
> The properties if the inner classes FADOConnection, FFIBDatabase
> are not saved in the .dfm file, when I change them at design time.

They're not supposed to be.  Only the main component itself is streamed by
default, not its inner contents.  You have to do that manually by overriding
the DefineProperties() method to provide your own reading and writing
functions for streaming your own custom data.  You can use that to then save
the inner components as needed.  The simpliest way would be to call the
ReadComponent() and WriteComponent() methods of the TReader and TWriter
classes, respectively.

Quote
> void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
> {
>  if(FADOConnection != value) FADOConnection = value;
> }

Do *NOT* override your pointers like that.  What you should be doing is
copying the individual properties of the source object to your internal
object, ie:

    void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
    {
        FADOConnection->Close();
        if( value )
        {
            FADOConnection->Attributes = value->Attributes;
            ...
        }
    }

Or, if TADOConnection provides an overriden Assign() method (I'm not sure if
it does or not):

    void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
    {
        FADOConnection->Assign(value);
    }

Quote
> void __fastcall TConnectFIB_ADO::SetFIBDatabase(TpFIBDatabase* value)
> {
>   if(FFIBDatabase != value) FFIBDatabase = value;
> }

Same here

Quote
> void __fastcall TConnectFIB_ADO::SetFIBTransaction(TpFIBTransaction*
value)
> {
>         if(FFIBTransaction != value) {
>                 FFIBTransaction = value;
>         }
> }

Same here

Gambit

Re:Saving component's properties of an aggregated component


Thank you very much for your response. But I have some questions again :
Question 1.

Quote
>They're not supposed to be.  Only the main component itself >is streamed by default, not its inner contents.  You have to >do that manually by overriding the DefineProperties() method >to provide your own reading and writing functions for >streaming your own custom data.  You can use that to then save
>the inner components as needed.  The simpliest way would be >to call the ReadComponent() and WriteComponent() methods of >the TReader and TWriter classes, respectively.

    I can't get working code when I override the DefineProperties() method in my class in the manner, described in the help.
I get "The stream read error" or "Out of memory", using following code, when I try to view my Form "as text"...

//---------------start ConnectFIB_ADO.cpp ---------------------
#include <vcl.h>
#pragma hdrstop

#include "ConnectFIB_ADO.h"
#pragma package(smart_init)
//-------------------------------------------------------------static inline void ValidCtrCheck(TConnectFIB_ADO *)
{
        new TConnectFIB_ADO(NULL);

Quote
}

//-------------------------------------------------------------__fastcall TConnectFIB_ADO::TConnectFIB_ADO(TComponent* Owner)
        : TComponent(Owner)
{
        ////////////////////
        FIsWriteDebug = true;
        ////////////////////
        FADOConnection = new TADOConnection(this);
        trace("constructor FADOConnection");
        FFIBDatabase = new TpFIBDatabase(this);
        trace("constructor FFIBDatabase");
        FFIBTransaction = new TpFIBTransaction(this);
        trace("constructor FFIBTransaction");

        FFIBDatabase->Name    = "FFIBDatabase";
        FFIBTransaction->Name = "FFIBTransaction";
        FADOConnection->Name  = "FADOConnection";

        //FFIBDatabase->Handle = Application->Handle;

        FFIBTransaction->DefaultDatabase = FFIBDatabase;
        FFIBDatabase->DefaultTransaction = FFIBTransaction;

Quote
}

//-------------------------------------------------------------__fastcall TConnectFIB_ADO::~TConnectFIB_ADO()
{
        trace("destructor ~TConnectFIB_ADO()");
Quote
}

//-------------------------------------------------------------namespace Connectfib_ado
{
        void __fastcall PACKAGE Register()
        {
                 RegisterPropertyEditor(__typeinfo(TADOConnection), 0L, "", __classid(TClassProperty));
                 RegisterPropertyEditor(__typeinfo(TpFIBDatabase), 0L, "", __classid(TClassProperty));
                 RegisterPropertyEditor(__typeinfo(TpFIBTransaction), 0L, "", __classid(TClassProperty));

                 TComponentClass classes[1] = {__classid(TConnectFIB_ADO)};
                 RegisterComponents("FIBPlus", classes, 0);
        }

Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
{
        if(FADOConnection != value) {
           FADOConnection->Attributes =
value->Attributes       ;
           FADOConnection->CommandTimeout   =
value->CommandTimeout   ;
           FADOConnection->Connected        =
 value->Connected        ;
           FADOConnection->ConnectionString =
 value->ConnectionString ;
           FADOConnection->ConnectionTimeout=
 value->ConnectionTimeout;
           FADOConnection->ConnectOptions   =
 value->ConnectOptions   ;
           FADOConnection->CursorLocation   =
 value->CursorLocation   ;
           FADOConnection->DefaultDatabase  =
 value->DefaultDatabase  ;
           FADOConnection->IsolationLevel   =
 value->IsolationLevel   ;
           FADOConnection->KeepConnection   =
 value->KeepConnection   ;
           FADOConnection->Mode             =
 value->Mode             ;
           FADOConnection->Provider         =
 value->Provider         ;
       }
Quote
}

//-------------------------------------------------------------TADOConnection* __fastcall TConnectFIB_ADO::GetADOConnection()
{
        return FADOConnection;
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::SetFIBDatabase(TpFIBDatabase* value)
{
        if(FFIBDatabase != value) {
            FFIBDatabase->AfterRestoreConnect    =
value->AfterRestoreConnect   ;
            FFIBDatabase->AfterStartTransaction  =
value->AfterStartTransaction ;
            FFIBDatabase->AliasName              =
value->AliasName             ;
            FFIBDatabase->BeforeStartTransaction =
value->BeforeStartTransaction;
            FFIBDatabase->CacheSchemaOptions     =
value->CacheSchemaOptions    ;
            FFIBDatabase->SaveDBParams           =
value->SaveDBParams          ;
            FFIBDatabase->WaitForRestoreConnect  =
value->WaitForRestoreConnect ;
            FFIBDatabase->CanTimeout             =
value->CanTimeout            ;
            FFIBDatabase->Connected              =
value->Connected             ;
            FFIBDatabase->ConnectParams          =
value->ConnectParams         ;
            FFIBDatabase->DatabaseName           =
value->DatabaseName          ;
            FFIBDatabase->DBName                 =
value->DBName                ;
            FFIBDatabase->DBParams               =
value->DBParams              ;
            FFIBDatabase->DefaultTransaction     =
value->DefaultTransaction    ;
            FFIBDatabase->DesignDBOptions        =
value->DesignDBOptions       ;
            FFIBDatabase->Handle                 =
value->Handle                ;
            FFIBDatabase->SQLDialect             =
value->SQLDialect            ;
            FFIBDatabase->SynchronizeTime        =
value->SynchronizeTime       ;
            FFIBDatabase->Timeout                =
value->Timeout               ;
            FFIBDatabase->UpperOldNames          =
value->UpperOldNames         ;
            FFIBDatabase->UseLoginPrompt         =
value->UseLoginPrompt        ;
        }
Quote
}

//-------------------------------------------------------------TpFIBDatabase* __fastcall TConnectFIB_ADO::GetFIBDatabase()
{
        return FFIBDatabase;
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::SetFIBTransaction(TpFIBTransaction* value)
{
        if(FFIBTransaction != value) {
           FFIBTransaction->AfterStart          =
value->AfterStart         ;
           FFIBTransaction->BeforeStart         =
value->BeforeStart        ;
           FFIBTransaction->TPBMode             =
value->TPBMode            ;
           FFIBTransaction->UserKindTransaction =
value->UserKindTransaction;
           FFIBTransaction->Active              =
value->Active             ;
           FFIBTransaction->DefaultDatabase     =
value->DefaultDatabase    ;
           FFIBTransaction->Handle              =
value->Handle             ;
           FFIBTransaction->Timeout             =
value->Timeout            ;
           FFIBTransaction->TimeoutAction       =
value->TimeoutAction      ;
           FFIBTransaction->TRParams            =
value->TRParams           ;

       }

Quote
}

//-------------------------------------------------------------TpFIBTransaction* __fastcall TConnectFIB_ADO::GetFIBTransaction()
{
        return FFIBTransaction;
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::SetUseDBAccess(TUseDBAccess value)
{
        if(FUseDBAccess != value) {
                FUseDBAccess = value;
        }
Quote
}

//-------------------------------------------------------------TUseDBAccess __fastcall TConnectFIB_ADO::GetUseDBAccess()
{
        return FUseDBAccess;
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::LoadCompProperty(TReader* Reader)
{
  trace("LoadCompProperty");
  if (Reader->ReadBoolean())
      FFIBDatabase = (TpFIBDatabase*)Reader->ReadComponent(NULL);
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::StoreCompProperty(TWriter * Writer)
{
  if (FFIBDatabase)
  {
    Writer->WriteBoolean(true);
    Writer->WriteComponent(FFIBDatabase);
  }
  else
    Writer->WriteBoolean(false);
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::DefineProperties(TFiler *Filer)
{
// before we do anything, let the base class define its props.
// Note that this example assumes that TSampleComponent
//derives directly from TComponent
  TComponent::DefineProperties(Filer);
  bool WriteValue;
  trace("DefineProperties");
  if (Filer->Ancestor) // check for inherited value
  {
    if (((TConnectFIB_ADO*)Filer->Ancestor)->FFIBDatabase == NULL)
        WriteValue = (FFIBDatabase != NULL);
    else if ((FFIBDatabase == NULL) ||
            (((TConnectFIB_ADO*)Filer->Ancestor)->FFIBDatabase->Name !=
                                                  FFIBDatabase->Name))
      WriteValue = true;
    else WriteValue = false;
  }
  else // no inherited value, write property if not null
    WriteValue = (FFIBDatabase != NULL);
  Filer->DefineProperty("FIBDatabase",LoadCompProperty,StoreCompProperty, WriteValue);
Quote
}

//-------------------------------------------------------------void __fastcall TConnectFIB_ADO::trace(AnsiString s)
{
  if (FIsWriteDebug) WriteDebug(s, "ConnectFIB_ADO" );
Quote
}

//-------------------------------------------------------------
//---------------finish ConnectFIB_ADO.cpp --------------------

Question 2.

Quote
You wrote:
>> void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
>> {
>>  if(FADOConnection != value) FADOConnection = value;
>> }

>Do *NOT* override your pointers like that.  What you should be doing is
>copying the individual properties of the source object to your internal
>object, ie:

>    void __fastcall TConnectFIB_ADO::SetADOConnection(TADOConnection* value)
>    {
>        FADOConnection->Close();
>        if( value )
>        {
>            FADOConnection->Attributes = value->Attributes;
>            ...
>        }
>    }

Why I can't use my pointer directly, but the ...

read more »

Re:Saving component's properties of an aggregated component


Quote
"Andy" <kua...@yahoo.co.uk> wrote in message

news:3dda4524$1@newsgroups.borland.com...

Quote
>     I can't get working code when I override the
> DefineProperties() method in my class in the manner,
> described in the help.
> I get "The stream read error" or "Out of memory", using
> following code, when I try to view my Form "as text"...

You're trying to do multiple Read/Write operations in your stream handlers.
The streaming system was not designed to do that.  I know, I tried doing
something similiar in my own components earlier on and found it simply
wouldn't work that way.  Each handler is supposed to be for a specific,
single operation only.  In your particular case, you should only be calling
Read/WriteComponent() in your handlers.

Also keep in mind that your FFIBDatabase pointer will ALWAYS be set to a
valid object, because you created it in your constructor.  Even if the
streaming system supported when you're trying to do, you would always be
writing a 'true' anyway.  So you should just take that part out altogether.

Lastly, for the ReadComponent() part, you should not be using the return
value of ReadComponent() to assign your database pointer, as you just
created a memory leak again.  Pass the database object as the Instance
parameter instead:

    void __fastcall TConnectFIB_ADO::LoadCompProperty(TReader* Reader)
    {
        trace("LoadCompProperty");
        Reader->ReadComponent(FFIBDatabase);
    }

    void __fastcall TConnectFIB_ADO::StoreCompProperty(TWriter * Writer)
    {
        trace("StoreCompProperty");
        Writer->WriteComponent(FFIBDatabase);
    }

The alternative to all of this would be to simply expose your inner
components as published read/write properties of the main component, so that
the default streaming mechanism can pick them up for you instead of you
having to do it all yourself.  But I suspect that defeats the design you're
trying to do with your component, correct?

Quote
> Why I can't use my pointer directly, but the objects have the same size?

Because that's not how things work.  It's not a matter of pointer or object
sizes.  You allocated memory in the constructor for an actual object, but
then you're turning around and assiging the pointer to point elsewhere, thus
losing the pointer to the original object.  Rather than modifying the
existing object, you are essentially telling the main component to go use a
completely different object instead.  Which, if that other object is freed
later on, your component is then pointing to invalid memory.  The original
object allocted in the constructor is left stranded in memory doing nothing
at all until it is freed when the main component itself is freed (since you
assigned the main component to be the Owner, so at least you don't have a
memory leak).  Your property setter method is supposed to be modifying the
original object so that the component continues using it.

If you really wanted your main component to use external objects, rather
than using it's own internal ones, then you went about your component design
all wrong.

Gambit

Re:Saving component's properties of an aggregated component


Remy Lebeau, many thanks for your help!

I've wiggled out using new own string property, the example of the "StringToComponent()" and published

    __property TpFIBDatabase* FIBDatabase  =
           {read=FFIBDatabase, write=SetFIBDatabase};

//--------
void __fastcall TConnectFIB_ADO::LoadFIBDatabase(TReader* Reader)
{
  trace("LoadFIBDatabase");
  FIBDatabase =
      (TpFIBDatabase*)StringToComponent(Reader->ReadString());

Quote
}

//--------
void __fastcall TConnectFIB_ADO::StoreFIBDatabase(TWriter * Writer)
{
  trace("StoreFIBDatabase");
    FFIBDatabase->Connected = false;
    Writer->WriteString(ComponentToString(FFIBDatabase));
Quote
}

//--------
void __fastcall TConnectFIB_ADO::DefineProperties(TFiler *Filer)
{
  TComponent::DefineProperties(Filer);
  trace("DefineProperties");
  Filer->DefineProperty(
            this->Name + "_" + FFIBDatabase->Name,
            LoadFIBDatabase,StoreFIBDatabase, true);
Quote
}

//--------

Quote
>The alternative to all of this would be to simply expose your inner
>components as published read/write properties of the main component, so that
>the default streaming mechanism can pick them up for you instead of you
>having to do it all yourself.  But I suspect that defeats the design you're
>trying to do with your component, correct?

Yes, this is not right way in my case...

Quote
>Because that's not how things work.  It's not a matter of pointer or object
>sizes.  You allocated memory in the constructor for an actual object, but
>then you're turning around and assiging the pointer to point elsewhere, thus
>losing the pointer to the original object.  Rather than modifying the
>existing object, you are essentially telling the main component to go use a
>completely different object instead.  Which, if that other object is freed
>later on, your component is then pointing to invalid memory.  The original
>object allocted in the constructor is left stranded in memory doing nothing
>at all until it is freed when the main component itself is freed (since you
>assigned the main component to be the Owner, so at least you don't have a
>memory leak).  Your property setter method is supposed to be modifying the
>original object so that the component continues using it.

>If you really wanted your main component to use external objects, rather
>than using it's own internal ones, then you went about your component design
>all wrong.

Ok I see. Thank you again for the intelligible explanations.

Andy

Re:Saving component's properties of an aggregated component


That is not a wise thing to do, as you just overwrote your pointer again and
lost the object that you allocated in the constructor.  This is like the
third time I've had to warn you about being careful about doing that.

Did using ReadComponent() as I demonstrated in my last reply not work
correctly for you?

Gambit

Quote
"Andy" <kua...@yahoo.co.uk> wrote in message

news:3ddbdc38$1@newsgroups.borland.com...
Quote
>   FIBDatabase =
>       (TpFIBDatabase*)StringToComponent(Reader->ReadString());

Other Threads