Search code examples
delphic++buildertpagecontrolttabsheet

Adding controls to TPageControl within TabSheet1Show


When I try to add controls to an already existing TTabSheet at runtime, these controls stay invisible when they are added within the OnShow event of TTabSheet.

Steps to reproduce:

  1. Add a TPageControl to a TForm in the designer
  2. Add 3 TTabSheet objects to this TPageControl in the designer.
  3. Set the first TTabSheet active (at design time).
  4. Run the code below:

Header file:

#ifndef Unit1H
#define Unit1H

#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ComCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // Von der IDE verwaltete Komponenten
    TPageControl *PageControl1;
    TTabSheet *TabSheet1;
    TTabSheet *TabSheet2;
    TTabSheet *TabSheet3;
    TButton *Button1;
    void __fastcall TabSheet1Show(TObject *Sender);
private:    // Benutzer-Deklarationen
    TButton *ButtonConstructor;
    TButton *ButtonOnTabShow;
public:     // Benutzer-Deklarationen
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Source File:

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    // Adding a TButton in the Form's constructor works

    TTabSheet *ts = this->TabSheet1;

    if (!this->ButtonConstructor)
    {
        ButtonConstructor = new TButton( ts );
        ButtonConstructor->Name = "ButtonConstructor";
        ButtonConstructor->Caption = "Construct";
        ButtonConstructor->Parent = ts;
    }


}
//---------------------------------------------------------------------------
void __fastcall TForm1::TabSheet1Show(TObject *Sender)
{
    // Adding a TButton in the OnShow Event of TTabSheet does NOT work:
    // The button stays invisible

    TTabSheet *ts = dynamic_cast< TTabSheet * >( Sender );
    // TTabSheet *ts = this->ButtonOnTabShow;   // does not make any difference

    if (!this->ButtonOnTabShow)
    {
        ButtonOnTabShow = new TButton( ts );
        ButtonOnTabShow->Name = "ButtonOnTabShow";
        ButtonOnTabShow->Caption = "Show";
        ButtonOnTabShow->Parent = ts;
        // Button should be placed below the other
        ButtonOnTabShow->Top = ButtonConstructor->Top + ButtonConstructor->Height + 2;
    }

    // The following 2 lines would make the Button visible
    // PageControl1->ActivePageIndex = 1;
    // PageControl1->ActivePageIndex = 0;
}

The Result is:

  • ButtonConstructor is visible
  • ButtonOnTabShow is not visible

If you click on TabSheet2 and then go back to TabSheet1, ButtonOnTabShow will also be visible.

Is this a bug which cannot be solved, or am I missing something?


Solution

  • I'm not sure exactly why it happens, but I can reproduce it. It probably has to do with the way TPageControl manages the visibility of its TTabSheet objects (since the Win32 API has no concept of tab sheets. TTabSheet is a wholly VCL invention to ease management of tabbed content). However, it is easy to work around using this code:

    #define WM_ENSUREBUTTONSHOWN (WM_APP+1)
    
    void __fastcall TForm1::TabSheet1Show(TObject *Sender)
    {
        // ...
    
        if (!this->ButtonOnTabShow)
        {
            ButtonOnTabShow = new TButton( ts );
            // ...
            PostMessage(Handle, WM_ENSUREBUTTONSHOWN, 0, 0);
        }
    }
    
    void __fastcall TForm1::WndProc(TMessage &Message)
    {
        TForm::WndProc(Message);
        if ((Message.Msg == WM_ENSUREBUTTONSHOWN) && (this->ButtonOnTabShow))
        {
            this->ButtonOnTabShow->Hide();
            this->ButtonOnTabShow->Show();
        }
    }
    

    Alternatively:

    #define WM_REFRESHTABSHEET (WM_APP+1)
    
    void __fastcall TForm1::TabSheet1Show(TObject *Sender)
    {
        // ...
    
        if (!this->ButtonOnTabShow)
        {
            ButtonOnTabShow = new TButton( ts );
            // ...
            PostMessage(Handle, WM_REFRESHTABSHEET, 0, 0);
        }
    }
    
    void __fastcall TForm1::WndProc(TMessage &Message)
    {
        TForm::WndProc(Message);
        if ((Message.Msg == WM_REFRESHTABSHEET) && (this->TabSheet1->Visible))
        {
            this->TabSheet1->Hide();
            this->TabSheet1->Show();
        }
    }