Board index » cppbuilder » Re: Dynamicly create a TPanel which has to be the same as another TPanel

Re: Dynamicly create a TPanel which has to be the same as another TPanel


2004-01-14 05:27:40 PM
cppbuilder75
Julien Moorrees < XXXX@XXXXX.COM >wrote:
Quote
[...] I have put the controls I want to create dynamicly on
a TPanel which is available on my form.

TPanel *fileTransferPanels[40];
int fileTransferPanelCount;
This is not exception safe. The user could click more than 40
times which would cause an Access Violation (at the least).
Without knowing how you're dealing with the panels, I can't
advise on the best solution because you may not even need to
save pointers to the new panels.
For example, If one of the buttons is used to delete the panel,
you don't need to save the pointer and in the case where the
user closes the form, as long as you set the Owner, the Owner
will delete the panel when the Owner is deleted.
If you do need to save the pointer to the panel, a dynamic
array such as a vector is best suited for your needs.
void __fastcall Tsk_discuss::Button1Click(TObject *Sender)
{
TPanel* pPanel = pnlFileTransfer;
TPanel* newPanel = new TPanel( this );
newPanel->Parent = pPanel;
// set the position and dimensions just to confirm the new panel
newPanel->Left = 5;
newPanel->Top = 5;
newPanel->Width = pPanel->Width - 10;
newPanel->Height = pPanel->Height - 10;
TForm* pForm = this;
for( int x = 0; x < pForm->ComponentCount; ++x )
{
TControl* pControl = reinterpret_cast<TControl*>( pForm->Components[ x ] );
if( pControl->Parent == pPanel && !pForm->Components[ x ]->ClassNameIs( "TPanel" ) )
{
TControl* newControl;
if( pForm->Components[ x ]->ClassNameIs( "TLabel" ) )
{
TLabel* pLabel = reinterpret_cast<TLabel*>( pControl );
TLabel* newLabel = new TLabel( this );
newLabel->Caption = pLabel->Caption;
newControl = reinterpret_cast<TControl*>( newLabel );
}
else if( pForm->Components[ x ]->ClassNameIs( "TEdit" ) )
{
TEdit* pEdit = reinterpret_cast<TEdit*>( pControl );
TEdit* newEdit = new TEdit( this );
newEdit->Text = pEdit->Text;
newControl = reinterpret_cast<TControl*>( newEdit );
}
else if( pForm->Components[ x ]->ClassNameIs( "TButton" ) )
{
TButton* pButton = reinterpret_cast<TButton*>( pControl );
TButton* newButton = new TButton( this );
newButton->Caption = pButton->Caption;
newButton->OnClick = pButton->OnClick;
newControl = reinterpret_cast<TControl*>( newButton );
}
else newControl = NULL;
if( newControl )
{
newControl->Parent = newPanel;
newControl->Left = pControl->Left;
newControl->Top = pControl->Top;
newControl->Height = pControl->Height;
newControl->Width = pControl->Width;
}
}
}
}
About the casting in the sample: Many will say that you should
use dynamic_cast instead of reinterpret_cast because dynamic_cast
will return NULL if it fails so you can check for success. However,
because the block of code uses ClassNameIs, all you're doing is
adding extra overhead to the code.
Now, if you do have a button the panel that closes (deletes)
the panel, you can forget about saving the pointer and use
The following. It uses a custom message to delete the panel
because you can't directly delete the panel with the button
click because when you delete the panel, it also deletes the
button because the button is owned by the panel and you can't
delete the button from within one of it's own events:
void __fastcall Tsk_discuss::DeletePanelClick(TObject *Sender)
{
TButton* pButton = static_cast<TButton*>( Sender );
PostMessage( Handle, APPWM_DELETE_PANEL, DELETE_PANEL_MAGIC_NUMBER, reinterpret_cast<int>( pButton->Parent ) );
}
Now you need to add some definitions in the unit's header:
#define APPWM_DELETE_PANEL (WM_APP + 100)
#define DELETE_PANEL_MAGIC_NUMBER 0xAFAFAFAF
#pragma pack(push, 1)
struct TWMDeletePanel
{
unsigned Msg;
unsigned MagicNum;
TControl* pPanel;
int Result;
};
#pragma pack(pop)
//-------------------------------------------------------------
class Tsk_discuss : public TForm
{
__published: // IDE-managed Components
void __fastcall Button1Click(TObject *Sender);
void __fastcall DeletePanelClick(TObject *Sender);
protected: // User declarations
private: // User declarations
void __fastcall AppDeletePanel(TWMDeletePanel &Message);
public: // User declarations
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(APPWM_DELETE_PANEL, TWMDeletePanel, AppDeletePanel)
END_MESSAGE_MAP(TForm)
__fastcall Tsk_discuss(TComponent* Owner);
};
//-------------------------------------------------------------
And in the unit cpp - the code to actually delete the panel:
void __fastcall Tsk_discuss::AppDeletePanel(TWMDeletePanel &Message)
{
if( Message.MagicNum == DELETE_PANEL_MAGIC_NUMBER )
{
TPanel* pPanel = reinterpret_cast<TPanel*>( Message.Sender );
delete pPanel;
Message.Result = TRUE;
}
else TForm::Dispatch(&Message);
}
The last thing that you need to consider is that the user could
eventually close (delete) the original panel which would make
it impossible for you to produce more copies of the panel. You
can combat that problem a number of ways. One is to Use the
Tag property of the original panel's close button by setting
it to something other than zero and then in the delete click:
void __fastcall Tsk_discuss::DeletePanelClick(TObject *Sender)
{
TButton* pButton = static_cast<TButton*>( Sender );
if( pButton->Tag != 0 )
{
PostMessage( Handle, APPWM_DELETE_PANEL, DELETE_PANEL_MAGIC_NUMBER, reinterpret_cast<int>( pButton->Parent ) );
}
}
~ JD
 
 

Re:Re: Dynamicly create a TPanel which has to be the same as another TPanel

"JD" < XXXX@XXXXX.COM >wrote:
Quote

I use 'this' as the owner of the new components when I should
have used the new panel:
Quote
TLabel* newLabel = new TLabel( this );
....
TEdit* newEdit = new TEdit( this );
....
TButton* newButton = new TButton( this );
should read:
TLabel* newLabel = new TLabel( newPanel );
TEdit* newEdit = new TEdit( newPanel );
TButton* newButton = new TButton( newPanel );
because you want the sub-components owned by the new panel so
that
when you delete the new panel, it also deletes the sub-components.
~ JD
 

Re:Re: Dynamicly create a TPanel which has to be the same as another TPanel

JD wrote:
Quote
Julien Moorrees < XXXX@XXXXX.COM >wrote:

>[...] I have put the controls I want to create dynamicly on
>a TPanel which is available on my form.
>
>TPanel *fileTransferPanels[40];
>int fileTransferPanelCount;


This is not exception safe. The user could click more than 40
times which would cause an Access Violation (at the least).
I know :) this was just a shortcutted example code.
Quote
If you do need to save the pointer to the panel, a dynamic
array such as a vector is best suited for your needs.
Vector indeed is usefull :)
Quote
The last thing that you need to consider is that the user could
eventually close (delete) the original panel which would make
it impossible for you to produce more copies of the panel.
In my app this is not possible.
p.s. thanks for your info!
 

{smallsort}

Re:Re: Dynamicly create a TPanel which has to be the same as another TPanel

JD wrote:
Quote
If you do need to save the pointer to the panel, a dynamic
array such as a vector is best suited for your needs.
Can you give a vector Example wich stores TPanel object? I can't seem to
get it work properly.