Most of the C++Builder programs you write will contain a main form supported by one or more secondary forms. The way these forms communicate with each other and share information can not only add to the strength of your object-oriented design, but also improve the overall cohesiveness and efficiency of your application.
In this article, we'll focus on ways to open up communication between forms. We'll discuss some techniques you can use to centralize widely used objects, exchange data, and make calls to methods implemented in other forms within your application. Finally, we'll show you a short sample application that makes use of these techniques.
| Note: About Application |
|---|
| For those of you new to C++Builder, Application is an instance of the TApplication object and an integral part of each C++Builder program. It encapsulates the methods and attributes prescribed by Windows to qualify as a Windows application, including the processing of Windows messages. Application is defined globally in all C++Builder programs and can provide you with many useful values (check out TApplication's properties in the online VCL help for more information). |
Let's look at a short example that shows you how to declare and set a pointer to your main form. You can follow along by creating a new C++Builder application and adding a second blank form. We'll assume that the main form is called TForm1 and that it's declared and implemented in Unit1.cpp and Unit1.h (these are the defaults when you create a new C++Builder application, anyway). Assume also that you want to access the main form (TForm1) from a secondary form called TForm2 (implemented in Unit2.cpp and Unit2.h). To do this, you'll acquire a pointer to TForm1 from
Application->MainFormBefore you can declare a pointer to a TForm1 instance inside of TForm2, however, TForm2's header file (Unit2.h) must include TForm1's header file. You can do this by adding
#include "Unit1.h"to the list of #include statements already present in Unit2.h, as follows:
//---------------------------------------- #ifndef Unit2H #define Unit2H //---------------------------------------- #includeYou can now safely declare a pointer to TForm1 within your TForm2 class declaration:#include #include #include #include "Unit1.h" // Include Unit1 defs
class TForm2 : public TForm
{
__published: // IDE-managed Components
void __fastcall FormShow(
TObject *Sender);
private: // User declarations
// TForm1 pointer
TForm1* myAppsMainForm;
public: // User declarations
__fastcall TForm2(TComponent* Owner);
};
Next, you can set the TForm1 pointer to point to the application's main form when you need it. The code looks like this:myAppsMainForm =
(TForm1*) Application->MainForm;You can now call any public method or access any public attributes defined in TForm1 from within TForm2 using the myAppsMainForm pointer. You might use the following code, for example, to call a method in the main form asking for identification of the application's user:String userName = myAppsMainForm->getUsrName();To be able to use this call, you'd simply need to add to TForm1 a getUserName() public method that returns a String variable, like this:
class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner); // Get the user name
String getUserName(void) { return "Dilbert"; }
};
So far, you've seen how to generate a pointer to an application's main form. You've also used that pointer to call a method and retrieve a value located in the main form. In this case, the method you were calling was contained in the main form's object. Suppose for a moment, however, that the TForm1 object didn't have direct knowledge of the user name, but rather contained a pointer to another object that did. Let's invent this new object and call it UserIdent (you can place this code before TForm1's class definition inside Unit1.h for simplicity):class UserIdent
{
public:
UserIdent(String x) { name = x; }
~UserIdent() { }
char* getUserName(void)
{ return name.c_str(); }
private:
String name;
};
Now take a look at the changes to the TForm1 class definition:class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
public: // User declarations
// Pointer to UserIdent object
UserIdent* userInfo;
__fastcall TForm1(TComponent* Owner);
};
We've removed the getUserName() method, adding in its place a new attribute called userInfo that points to an instance of UserIdent. Somewhere within TForm1, you need to instantiate UserIdent and set the userInfo pointer to the new object so you can call it later from TForm2. Here's an example that does just that, shown inside TForm1's constructor:__fastcall TForm1::TForm1( TComponent* Owner) : TForm(Owner)
{
userInfo = new UserIdent("Dilbert");
}
Now that you've hidden the getUserName() method inside the UserIdent class, you can no longer access it directly through myAppsMainForm as you did previously. Instead, you now have to reference the userInfo object inside of TForm1 to get the information. That call is as follows:userName = myAppsMainForm->userInfo->getUserName()Keep in mind when using this approach that userInfo is a public member of TForm1-meaning that TForm2 can create chaos by changing where userInfo is pointing. A safer approach would be to privatize userInfo and add an accessor to TForm1 to return a pointer to userInfo. Here's a short piece of code showing the private userInfo and a public getUserInfo() method returning the pointer:
Class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
// PRIVATE userInfo
UserIdent* userInfo;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
// Get the pointer to userInfo
UserIdent* getUserInfo(void)
{ return userInfo; }
};
You now need to call the getUserInfo() method of myAppsMainForm to retrieve the pointer to the UserIdent object before calling getUserName(). A quick modification to TForm2 would have the getUserName() method call working again:userName = myAppsMainForm->getUserInfo()->getUserName();
class TForm1 : public TForm
{
__published: // IDE-managed Components
private: // User declarations
UserIdent* userInfo;// PRIVATE userInfo
public: // User declarations
__fastcall TForm1(TComponent* Owner); // Return the pointer
UserIdent* getUserInfo(void) { return userInfo; }
// Set the pointer
void setUserInfo(UserIdent* x) { userInfo = x; }
};
Notice that we've added to TForm1's public area a method called setUserInfo(), which accepts a UserIdent pointer and stores it in userInfo. Once TForm2 has collected the user's name and created the UserIdent object, it must call setUserInfo() to send the UserIdent pointer back to TForm1. Here is a snippet from TForm2 that does just that:void __fastcall TForm2::Button1Click(
TObject *Sender)
{
UserIdent *uid =
new UserIdent(Edit1->Text);
myAppsMainForm =
(TForm1*) Application->MainForm;
myAppsMainForm->setUserInfo(uid);
}
The code is an event handler for a button that retrieves the user's name from a text box, instantiates the UserIdent object, and sets TForm1's pointer. Note that you shouldn't delete the UserIdent pointer before you exit TForm2. That responsibility has been shifted to the main form (a task which you'd most likely do in TForm1's OnClose event handler).| Note: A word of caution about pointers |
|---|
| When declaring a pointer that you may have to delete, always set it to NULL somewhere during initialization (in an object's constructor, for example). Calling delete on a NULL pointer is safe…but calling delete on a pointer that you haven't initialized could be disastrous. |
Listing A: SampleAppForm
// Unit1.h //---------------------------------------- #ifndef Unit1H #define Unit1H //---------------------------------------- #includeAfter SampleAppForm initializes, it opens an instance of IdentifyUserForm, as shown in Figure A.#include #include #include //---------------------------------------- class UserIdent { public: UserIdent(String x) { name = x; } ~UserIdent() { } char* getUserName(void) { return name.c_str(); } private: String name; }; //---------------------------------------- class AppConfig { public: AppConfig() { exeName = Application->ExeName; exePath = ExtractFileDir(exeName); exeDrive = ExtractFileDrive(exeName); datPath = exePath + "\\dat"; bkpPath = exePath + "\\backup"; } ~AppConfig() { } char *getExeName(void) { return exeName.c_str(); } char *getExePath(void) { return exePath.c_str(); } char *getExeDrive(void) {return exeDrive.c_str(); } char *getDatPath(void) { return datPath.c_str(); } char *getBkpPath(void) { return bkpPath.c_str(); } private: String exeName, exePath, exeDrive, datPath, bkpPath; }; //--------------------------------------- class TSampleAppForm : public TForm { __published: // IDE-managed Components TButton *Demo; TButton *Quit; void __fastcall FormShow(TObject *Sender); void __fastcall DemoClick(TObject *Sender); void __fastcall QuitClick(TObject *Sender); void __fastcall FormClose(TObject *Sender, TCloseAction &Action); private: // User declarations // Pointer to UserIdent object to be // supplied by IdentifyUserForm UserIdent* userInfo; // Pointer to AppConfig object to be // created in SampleAppForm's constructor AppConfig* appInfo; public: // User declarations __fastcall TSampleAppForm(TComponent* Owner); // Get and set UserIdent pointers UserIdent* getUserInfo(void) { return userInfo; } void setUserInfo(UserIdent* x) { userInfo = x; } // Get the AppConfig pointer AppConfig* getAppConfig(void) { return appInfo; } }; //---------------------------------------- extern PACKAGE TSampleAppForm *SampleAppForm; //---------------------------------------- #endif // Unit1.cpp //---------------------------------------- #include #pragma hdrstop #include "Unit1.h" #include "Unit2.h" #include "Unit3.h" //---------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TSampleAppForm *SampleAppForm; //--------------------------------------- __fastcall TSampleAppForm::TSampleAppForm(TComponent* Owner) : TForm(Owner) { appInfo = new AppConfig(); // Next line sets userInfo to NULL. If we try to delete // appInfo without having set it, the app could fail. userInfo = NULL; // Just for safety } //--------------------------------------- void __fastcall TSampleAppForm::FormShow(TObject *Sender) { // Before we see the main form, collect user info and // create TForm1's UserIdent object IdentifyUserForm->ShowModal(); } //-------------------------------------- void __fastcall TSampleAppForm::DemoClick(TObject *Sender) { // Show off the objects we're accessing from the main form. DemoForm->ShowModal(); } //-------------------------------------- void __fastcall TSampleAppForm::QuitClick(TObject *Sender) { Close(); } //-------------------------------------- void __fastcall TSampleAppForm::FormClose(TObject *Sender, TCloseAction &Action) { // Delete the memory we used delete userInfo; delete appInfo; } //--------------------------------------
Figure A: The main form opens the subform IdentifyUserForm.
Listing B contains the source code for IdentifyUserForm. It collects a name, creates a UserIdent object, and then sets SampleAppForm's UserIdent pointer to that object by calling setUserInfo(). Once IdentifyUserForm closes, the user can open DemoForm by clicking the Demo button located on SampleAppForm, as shown in Figure B.
Figure B: You can click the Demo button on the main form to open DemoForm.
Listing B: IdentifyUserForm
// Unit2.h //--------------------------------------- #ifndef Unit2H #define Unit2H //--------------------------------------- #include#include #include #include #include "Unit1.h"
//----------------------------------------
class TIdentifyUserForm : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TButton *Ok;
TLabel *Label1;
void __fastcall OkClick(TObject *Sender);
private: // User declarations
// Local UserIdent pointer. Before this form closes, we'll
// pass this pointer to the main form.
UserIdent *uid;
TSampleAppForm* sampleAppForm; // Main form
public: // User declarations
__fastcall TIdentifyUserForm(TComponent* Owner);
};
//---------------------------------------extern PACKAGE TIdentifyUserForm *IdentifyUserForm; //---------------------------------------
#endif // Unit2.cpp //--------------------------------------- #include#pragma hdrstop #include "Unit2.h" //---------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TIdentifyUserForm *IdentifyUserForm; //--------------------------------------- __fastcall TIdentifyUserForm::TIdentifyUserForm( TComponent* Owner) : TForm(Owner) { } //----------------------------------------
void __fastcall TIdentifyUserForm::OkClick(TObject *Sender)
{
uid = new UserIdent(Edit1->Text);
sampleAppForm = (TSampleAppForm*)Application->MainForm;
sampleAppForm->setUserInfo(uid);
Close();
}
//---------------------------------------
Listing C contains the source code for DemoForm. The form contains one text area and several buttons that retrieve information from one of the objects contained in the main form. As you can see in Figure C, each button's purpose is clearly labeled. The User
Name button, for example, calls upon TForm1's UserIdent object to supply the user's name. The Exe Drive button calls the TForm1 AppConfig object's getExeDrive() method to retrieve the letter of the drive where the executable resides.
Figure C: DemoForm gets information from objects on the main form.
Listing C: DemoForm
// Unit3.h //---------------------------------------- #ifndef Unit3H #define Unit3H //----------------------------------------
#include#include #include #include #include "Unit1.h" //----------------------------------------
class TDemoForm : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TButton *UserName;
TButton *ExePath;
TButton *ExeDrive;
TButton *DatPath;
TButton *Done;
void __fastcall FormShow(TObject *Sender);
void __fastcall UserNameClick(TObject *Sender);
void __fastcall ExePathClick(TObject *Sender);
void __fastcall ExeDriveClick(TObject *Sender);
void __fastcall DatPathClick(TObject *Sender);
void __fastcall DoneClick(TObject *Sender);
private: // User declarations
TSampleAppForm* sampleAppForm; // Main form
public: // User declarations
__fastcall TDemoForm(TComponent* Owner);
};
//----------------------------------------extern PACKAGE TDemoForm *DemoForm; //----------------------------------------
#endif // Unit3.cpp //---------------------------------------- #include#pragma hdrstop #include "Unit3.h" //---------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TDemoForm *DemoForm; //--------------------------------------
__fastcall TDemoForm::TDemoForm(TComponent* Owner) : TForm(Owner)
{
}
//----------------------------------------void __fastcall TDemoForm::FormShow(TObject *Sender)
{
// Set up the pointer to our main form
sampleAppForm = (TSampleAppForm*) Application->MainForm;
}
//----------------------------------------void __fastcall TDemoForm::UserNameClick(TObject *Sender)
{
Edit1->Text = sampleAppForm->getUserInfo()->getUserName();
}
//----------------------------------------void __fastcall TDemoForm::ExePathClick(TObject *Sender)
{
Edit1->Text = sampleAppForm->getAppConfig()->getExeName();
}
//----------------------------------------void __fastcall TDemoForm::ExeDriveClick(TObject *Sender)
{
Edit1->Text = sampleAppForm->getAppConfig()->getExeDrive();
}
//----------------------------------------void __fastcall TDemoForm::DatPathClick(TObject *Sender)
{
Edit1->Text = sampleAppForm->getAppConfig()->getDatPath();
}
//----------------------------------------void __fastcall TDemoForm::DoneClick(TObject *Sender)
{
Close();
}
//----------------------------------------