C++Builder, 1, Borland C++Builder White Paper: C++Builder and VCL

By: Borland Staff

Abstract: This paper discusses C++Builder's Visual Component Library (VCL)-the framework from which all C++Builder applications are built. It discusses the VCL hierarchy and explains the purpose of the key levels within the hierarchy.

C++Builder and VCL
Written by Xavier Pacheco. Revised by Xavier Pacheco and John Huang.

Types of properties

Properties can be of the standard data types defined by the C++ rules. Property types also determine how they are edited in C++Builder's Object Inspector. The table below shows the different property types as they are defined in C++Builder's online help.

Property typeObject Inspector treatment
SimpleNumeric, character, and string properties appear in the Object Inspector as numbers, characters, and strings, respectively. The user can type and edit the value of the property directly.
EnumeratedProperties of enumerated types (including bool) display the value as defined in the source code. The user can cycle through the possible values by double-clicking the value column. There is also a drop-down list that shows all possible values of the enumerated type.
SetProperties of set types appear in the Object Inspector looking like a set. By expanding the set, the user can treat each element of the set as a Boolean value: True if the element is included in the set or False if it's not included.
ObjectProperties that are themselves objects often have their own property editors. However, if the object that is a property also has published properties, the Object Inspector allows the user to expand the list of object properties and edit them individually. Object properties must descend from TPersistent.
ArrayArray properties must have their own property editors. The Object Inspector has no built-in support for editing array properties.

For more information on properties, refer to the "Component Writers Guide" which ships with C++Builder.

Set
There are some data types which are built-in in Delphi that are not available in C++. C++Builder declared several template classes to handle these situations. One data type extensively used by Delphi is Set. Examples are the declaration of TFontStyle and TFontStyles below.

enum TFontStyle { fsBold, fsItalic, fsUnderline, fsStrikeOut };

typedef Set<TFontStyle, fsBold, fsStrikeOut> TFontStyles;

TFontStyle is an enumerated type. TFontStyles is defined as a set, unordered collection of TFontStyle. In the Object Inspector, It is show as an array of booleans which you can turn on or off.

Image 2

Member functions
Since components are really just objects, they can have methods. We will discuss some of the more commonly used functions later in this paper when we discuss the different levels of the VCL hierarchy.

Events
Events provide a means for a component to notify the user of some pre-defined occurrence within the component. Such an occurrence might be a button click or the pressing of a key on a keyboard. They can be accessed from the event page of the Object Inspector window.


Components contain special properties called events to which the component user assigns code. This code will be executed whenever a certain event occurs. For instance, if you look at the events page of a TEdit component, you'll see such events as OnChange, OnClick and OnDblClick.

When the user of a component assigns code to one of those events, the user's code is referred to as an event handler. For example, by double clicking on the events page for a particular event causes C++Builder to generate a method and places you in the Code Editor where you can add your code for that method. An example of this is shown in the code below, which is an OnClick event for a TButton component.

Unit1.hpp:

class TForm1 : public TForm

{

__published: // IDE-managed Components

TButton *Button1;

void __fastcall Button1Click(TObject *Sender);

private: // User declarations

public: // User declarations

virtual __fastcall TForm1(TComponent* Owner);

};

Unit1.cpp

void __fastcall TForm1::Button1Click(TObject *Sender)

{

/* Your code goes here */

}

It becomes clearer that events are function pointers when you assign an event handler to an event programmatically. The above example lists C++Builder generated code. To link your own event handler to a TButton's OnClick event at run time you must first create a method that you will assign to this event. Since this is a method, it must belong to an existing object. This object can be the form which owns the TButton component although it doesn't have to be. In fact, the event handlers which C++Builder creates belong to the form on which the component resides. The code below illustrates how you would create an event handler function.

// Unit1.hpp:

class TForm1 : public TForm

{

__published: // IDE-managed Components

TButton *Button1;

private: // User declarations

void __fastcall Button1Click(TObject *Sender); // Your function declaration

public: // User declarations

virtual __fastcall TForm1(TComponent* Owner);

};

// Unit1.cpp

void __fastcall TForm1::Button1Click(TObject *Sender)

{

/* Your code goes here */

}

The MyOnClickEvent method becomes the event handler for Button1->OnClick when it is assigned to Button1->OnClick in code as shown below.

Button1->OnClick = MyOnClickEvent;

This assignment can be made anytime at runtime, such as in the form's OnCreate event handler. This is essentially the same thing that happens when you create an event handler through C++Builder's Object Inspector except that C++Builder generates the function declaration, and saves the relationship between the event and event handler into the .dfm file. The .dfm file is compiled into the target application's resource. When an application built with C++Builder starts up, VCL reads the form from the resource and assign properties and events of the components on the form.

When you define methods for event handlers, these methods must be defined as the same type as the event property and the data member to which the event property refers. For instance, the OnClick event refers to an internal data member, FOnClick. Both the property OnClick, and data member FOnClick are of the type TNotifyEvent. TNotifyEvent is a function type as shown below:

typedef void __fastcall (__closure *TNotifyEvent)(System::TObject *Sender);

Therefore, if you are creating a method for an OnClick event, it must be defined with the same type and number of parameters as shown below.

class TForm1 : public TForm

{

...

void __fastcall Button1Click(TObject *Sender); // Your function declaration

...

};

Events and Windows messages

For experienced Windows C programmers, C++Builder events are similar to the window messages. The following is a brief list of events for TForm and the Windows messages you would use in a regular C program using Windows API.
Windows messages VCL event
WM_CREATE OnCreate
WM_DESTROY OnClose
WM_SIZE OnReSize
WM_ACTIVATE OnActivate, OnDeactivate
WM_SHOWWINDOW OnShow, OnHide
WM_KEYDOWN OnKeyDown
WM_KEYUP OnKeyUp
WM_KEYDOWN OnKeyDown
WM_LBUTTONDOWN, WM_RBUTTONDOWN OnMouseDown
WM_LBUTTONUP, WM_RBUTTONUP OnMouseUp
WM_MOUSEMOVE OnMouseMove
WM_LBUTTONDBLCLK, WM_RBUTTONDBLCLK OnDblClick
WM_PAINT OnPaint

Not all Windows messages have a corresponding VCL event. For example, in a Windows API C program, WM_COMMAND is used to handle button clicks and menu selections. In C++Builder, this is handled in TButton::OnClick and TMenuItem::OnClick events. Another example is the TTime component, whose OnTimer event replaces the functionality of WM_TIMER Windows message.

In addition. There are VCL events which extend the built-in functionality of Windows. In the Object Inspector, you can see TForm has OnDragOver and OnDragDrop events. They make adding drag-and-drop abilities to your VCL programs simple and direct. Most of the components on "Standard" and "Win95" are VCL wrappers around windows controls. The components on other pages are new components for different functional areas and you will find completely new events for them.


Extend VCL with Windows messages
The VCL components on the Standard page and the Win95 page encapsulate Windows controls. The interaction between the user and the computer, which used to be handled by responding to Windows messages, now reside in event handlers for VCL components. However, there are cases one needs to trap Windows messages, because VCL is not handling it, or the handling is not satisfactory. In this case, VCL provides a syntax similar to OWL or MFC to enable responding to Windows messages directly.

First, a method which takes a pointer to a TMessage as the parameter has to be defined. This is the function being called when the visual component receives the Windows message. TMessage is declared in vclmessages.hpp. There are other structures which slice TMessage different ways to give the fields different meanings.

Second, in the class declaration of the component which responds to the message, macros BEGIN_MESSAGE_MAP, MESSAGE_HANDLER, and END_MESSAGE_MAP are used. The following is an example of the usage. It triggers a function when the form receives WM_USER + 2000.

// in the .hpp file

class TForm1 : public TForm

{

...

MESSAGE void __fastcall HandleWMUser(TMessage *Message);

BEGIN_MESSAGE_MAP

MESSAGE_HANDLER(WM_USER + 2000, TMessage, HandleWMUser);

END_MESSAGE_MAP(TForm);

...

};

// in the .cpp file

...

MESSAGE void __fastcall TForm1::HandleWMUser(TMessage *Message)

{

/* your code goes here */

}

Rarely does one need to use this technique. Many developers coming from traditional C/C++ background will tend to overuse this method because this is close to how OWL and MFC handle Windows messages. However, check the component to see if there is an existing event you can use instead first.

Containership

Some components in the VCL can own other components as well as be parents to other components. These two concepts have a different meaning as will be discussed in the section to follow.

Ownership
All components may be owned by other components but not all components can own other components. A component's Owner property contains a reference to the component which owns it.

The basic responsibility of the owner is one of resource management. The owner is responsible for freeing those components which it owns whenever it is destroyed. Typically, the form owns all components which appear on it, even if those components are placed on another component such as a TPanel. At design-time, the form automatically becomes the owner for components which you place on it. The ownership hierarchy for a C++Builder application looks something like this:

Image 4

Notice that ownership applies not only to visual components, like forms and panels, but also non-visual components, like TTimer and TDataSource.

At run-time, when you create a component, you pass the owner as a parameter to the component's constructor. For instance, the code below shows how to create a TButton component at run-time and passes the form's implicit "this" variable to the TButton's constructor. TButton will then assign whatever is passed to it, in this case "this" or rather the form, and assign it to the button's Owner property.

MyButton = new TButton(this);

When the form that now owns this TButton component gets freed, MyButton will also be freed.

You can create a component without an owner by passing 0 to the component's constructor, however, you must ensure that the component is freed when it is no longer needed. The code below shows you how to do this for a TTable component.

TTable * MyTable = new TTable (0)

/* Do stuff with MyTable */

delete MyTable;

The Components property of a component is an array property which contains a list of the components which it owns. For instance, the code below shows how to loop through a form's components and then shows their class name.

void __fastcall TForm1::Button1Click(TObject *Sender)

{

for (int i = 0; i < ComponentCount; i ++)

{

ShowMessage(Components[i]->ClassName());

}

}

Parenthood
Parenthood is a much different concept from ownership. It applies only to windowed components, which can be parents to other components. Later, when we discuss the VCL hierarchy, you will see the level in the hierarchy which introduces windowed controls.

Parent components are responsible for the display of other components. They call the appropriate functions internally that cause the children components to draw themselves. The Parent property of a component refers to the component which is its parent. Also, a component's parent does not have to be it's owner. Although the parent component is mainly responsible for the display of components, it also frees children components when it is destroyed. The visual hierarchy of a form may look like the following chart. Notice that only visual components are contained in the hierarchy.


Many properties of visual components, such as Left, Width, Top, Height are relative to the parent control. Other properties, such as ParentColor and ParentFont, enable child controls to use properties of parents.

Windowed components are controls which are visible user interface elements such as edit controls, list boxes and memo controls. In order for a windowed component to be displayed, it must be assigned a parent on which to display itself. This task is done automatically by C++Builder's design-time environment when you drop a component from the Component Palette onto your form. When creating a component at run-time, however, you must explicitly make this assignment, otherwise the component will not be displayed. An example of creating a TEdit component at run-time is shown below:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

MyEdit = new TEdit (this); // Pass this as the owner

MyEdit->Parent = this; // Pass this as the parent

}

Streamability
Another characteristic of a component is that it can be streamed. Streaming is a way to store a component and information regarding its properties' values to a file or to an area in memory. For example, the .DFM file created by C++Builder, is a resource file with information about a form and the components residing on the form. This information was streamed to that resource file.

Component Writers must understand the streaming mechanism of the VCL if they intend for their components to stream special data, which is not done automatically by the VCL.

Server Response from: ETNASC01