1

I am using RAD Studio 10 working on a Windows VCL application. I have two Forms, Form1 (the MainForm in Unit1.cpp) and a secondary Form2 (in Unit2.cpp). I managed to embed Form2 inside of Form1. This is just a setup to illustrate the problem. My real project has multiple Forms.

When closing Form2, the VCL triggers the Form2::OnClose() event. Knowing that Form2 was created dynamically in Form1 (the MainForm), is there a Form1 event that will fire upon Form2 being closed? Or something inside Form1 to know that Form2 is closing?

  • I was thinking of customizing an event handler like OnChildFormClose but I couldn't make it.
  • I tried to wrap the code that I wanted to execute on Form1 when Form2 is closed in a public function and call it in the Form2::OnClose() event, and it worked to some extent, but it is not a good approach if you have multiple Forms.
//FROM THE unit1.cpp
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "Unit2.h"
//-----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//-----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//-----------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
TForm2 *form2 = new TForm2(this);
form2->ManualDock(container);
form2->Show();
}
//FROM unit2.cpp
#include <vcl.h>
#pragma hdrstop
#include "Unit2.h"
//-----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm2 *Form2;
//-----------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
    : TForm(Owner)
{
}
//-----------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
Close();
}
//-----------------------------------------------------------------------

Can I implement something like an OtherFormsonClose(*Sender) event in Form1 with a Sender that we can dynamically cast to check if it is Form2, or maybe I am wrong? I would appreciate some guidance.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Aness
  • 610
  • 1
  • 7
  • 24

2 Answers2

3

You can declare a common event handler of type TCloseEvent, e.g. OtherFormClose(TObject *Sender, TCloseAction &Action); in the main form:

private:    // User declarations
   void __fastcall TForm1::OtherFormClose(TObject *Sender, TCloseAction &Action);

implementation

void __fastcall TForm1::OtherFormClose(TObject *Sender, TCloseAction &Action)
{
  Action = caFree;
  TForm2 *f2 = dynamic_cast<TForm2 *>(Sender);
  if (f2) {
  ShowMessage(String("Form2 closing")); //Do stuff
  }

}

(or use Sender to inspect which form)

Then when you create other forms in code, e.g. Form2, you assign

  TForm2 *form2 = new TForm2(this);
  form2->OnClose = OtherFormClose;
  // etc
Aness
  • 610
  • 1
  • 7
  • 24
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
  • Thanks This is Good, With your solution there's no more trouble with regards to conflicting messages. Perfect :) – Aness Jun 10 '19 at 16:26
1

Ok I found something interesting after reading this, this, this, and this.

So basically, a VCL Delphi/C++Builder application uses Windows Form Messages for comunication, and we can override the virtual function WndProc to catch a specific message, but it must be some unique message because VCL uses a lot of messages and if you don't tread carefully things might blow up; This will translate to a custom event handler on the main form.

So what I did is :

  • Passed the MainForm handle to Form2 in the constructor to be saved on a Form2 private var and to be used only for the messaging.
  • Generate a specific ID that I use to tag the message to make it stand out
  • overriding the WndProc and filtering messages with a specific ID so we know that Form2 is closing.

Test it and it worked, maybe someone have a better idea.

//From unit2.h---------------------------------------------------------
class TForm2 : public TForm
{
__published:    // IDE-managed Components
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:    // User declarations
    HWND mfhandle;
public:     // User declarations
    __fastcall TForm2(TComponent* Owner, HWND mainformhandle);

};
//From unit2.cpp---------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit2.h"

#pragma package(smart_init)

#pragma resource "*.dfm"

TForm2 *Form2;
const UINT uiMyCopyDataID = RegisterWindowMessage(TEXT("MyCopyDataID"));

__fastcall TForm2::TForm2(TComponent* Owner,HWND mainformhandle)
    : TForm(Owner)
{
mfhandle = mainformhandle;
}

void __fastcall TForm2::Button1Click(TObject *Sender)
{
Close();
}

void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
//Notify the mainForm and say Hey I am closing now
PostMessage(mfhandle, uiMyCopyDataID, 0, 0);
}
//From unit1.h---------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
TPanel *container;
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormUnDock(TObject *Sender, TControl *Client, TWinControl *NewTarget,
      bool &Allow);
private:    // User declarations
protected:
void __fastcall TForm1::WndProc(TMessage &Message);  //Added THIS
public:     // User declarations
__fastcall TForm1(TComponent* Owner);
};
//From unit1.cpp-------------------------------------------------------
const UINT uiMyCopyDataID = RegisterWindowMessage(TEXT("MyCopyDataID"));

void __fastcall TForm1::WndProc(TMessage &Message)
{
    if (Message.Msg == uiMyCopyDataID)
    {
    //Do SomeThing here
    ShowMessage("Form2 is closing");

    }

    TForm::WndProc(Message);
}

Ok, So far it works and custom messages MUST be in the WM_USER (0x0400 - 0x7FFF) range.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Aness
  • 610
  • 1
  • 7
  • 24