C++Builder and VCL
Written by Xavier Pacheco. Revised by Xavier Pacheco and John Huang.
VCL class hierarchy overview
The figure below illustrates a sub-set of C++Builder
2.0's VCL class hierarchy. The objects shown in the hierarchy
the key classes from which components descend. Each object introduces
a certain set of methods, events and properties and has a special
purpose.
Earlier in this paper we discussed four types of
components: standard controls, custom controls, graphical controls
and non-visual components. The following paragraphs discusses
how these different types of components relate to the objects
shown in the hierarchy.
Non-visual components are descendants of TComponent.
Whereas TObject is the base class from which all classes descend,
TComponent is the base class from which all components descend.
This paper will discuss the TComponent class in more detail shortly.
The TGraphicControl class provides the capability
to have controls which are not windowed controls (they have no
window handle). Therefore graphical controls descend from TGraphicControl.
TWinControl is the base class from which all windowed
controls descend. It is at the TWinControl level that the window
handle is introduced. Both standard controls and custom controls
which are windowed and are therefore descendants of TWinControl.
Custom controls, however, are not likely to descend directly from
TWinControl and will descend from TCustomControl.
TObject
TObject is the base class from which all other classes
descend. Since all classes descend from TObject, every class inherits
the methods which are defined by TObject. This gives all classes
certain functionality. For example, every class can tell you its
name, its type, and even it's ancestry.
TObject's definition comes from the SYSTEM.HPP and
is defined as:
class __declspec(delphiclass) TObject
{
public:
__fastcall TObject() {}
__fastcall Free();
TClass __fastcall ClassType();
void __fastcall CleanupInstance();
void * __fastcall FieldAddress(const ShortString
&Name);
static TObject * __fastcall InitInstance(TClass
cls, void *instance);
static ShortString __fastcall ClassName(TClass
cls);
static bool __fastcall ClassNameIs(TClass cls,
const AnsiString string);
static TClass __fastcall ClassParent(TClass cls);
static void * __fastcall ClassInfo(TClass cls);
static long __fastcall InstanceSize(TClass cls);
static bool __fastcall InheritsFrom(TClass cls,
TClass aClass);
static void * __fastcall MethodAddress(TClass
cls, const ShortString &Name);
static ShortString __fastcall MethodName(TClass
cls, void *Address);
ShortString __fastcall ClassName()
{
return ClassName(ClassType());
}
bool __fastcall ClassNameIs(const AnsiString
string)
{
return ClassNameIs(ClassType(), string);
}
TClass __fastcall ClassParent()
{
return ClassParent(ClassType());
}
void * __fastcall ClassInfo()
{
return ClassInfo(ClassType());
}
long __fastcall InstanceSize()
{
return InstanceSize(ClassType());
}
bool __fastcall InheritsFrom(TClass aClass)
{
return InheritsFrom(ClassType(), aClass);
}
void * __fastcall MethodAddress(const ShortString
&Name)
{
return MethodAddress(ClassType(), Name);
}
ShortString __fastcall MethodName(void *Address)
{
return MethodName(ClassType(), Address);
}
virtual void __fastcall Dispatch(void *Message);
virtual void __fastcall DefaultHandler(void*
Message);
private:
virtual TObject* __fastcall NewInstance(TClass
cls);
public:
virtual void __fastcall FreeInstance();
virtual __fastcall ~TObject() {}
};
These methods are documented in C++Builder's online
help.
Note that some methods that are preceded by the keyword
static. These functions can be called like a normal procedure
or function from the class type. This means that you don't have
to have an instance of this class in order to call such functions.
All components must descend from TComponent or from
a TComponent descendant. TComponent, being a descendant of TObject,
inherits TObjects data members, methods and properties.
Objects which descend from objects higher than TComponent
in the VCL hierarchy are non-component classes. Some useful non-component
classes are TStringList, TIniFile and TPrinter. You can look up
these classes in the online help if you're unfamiliar with them.
TObject's constructor and destructor allocate and
de-allocate memory for the object's instance respectively. The
TObject constructor returns a pointer to the object being created.
TPersistent
The TPersistent class descends directly from TObject.
The special characteristic of TPersistent is that it is an abstract
class that defines the methods that allow it to be streamed. TPersistent
defines no special properties or events, but does define certain
methods of use to the Component Writer. The table below shows
these functions.
| Member function | Purpose
|
| Assign | This public method allows a component to assign the data associated with another component to itself.
|
| AssignTo | This protected method allows a component to override the implementation of the Assign method. TPersistent itself, raises an exception when this function is called. It is up to TPersistent descendant to override this function to define its implementation. The TClipboard class is an object that does this, for example.
|
| DefineProperties | This protected method that allows component writers to define how the component stores extra or unpublished properties. By default, a component automatically stores published properties.
|
TComponent
The TComponent class is a direct descendant of TPersistent. As
we said earlier, all components are TComponent descendants. TComponent's
special characteristics are that its properties are streamable
and can be manipulated at design-time through the Object Inspector.
TComponent can also own other components.
Certain non-visual components that descend from TComponent are
also capable of being manipulated at design time. One such is
the TTimer component. TTimers are not visual controls but still
are available on the Component palette.
TComponent defines several properties
and methods that give it its special functionality. It's properties
are defined in the table below.
| Component Name
| Purpose |
| Owner |
Refers to the component's owner.
|
| ComponentCount
| Number of components owned.
|
| ComponentIndex
| The position of this component in its owners list of components.
The first component in this list has the value of zero.
|
| Components
| A property array containing a list of components that are owned by this component.
|
| ComponentState
| The current state of a component. Look up TComponentState in C++Builder's online help for additional information on this property.
|
| ComponentStyle
| A style that dictates the behavior of a component. Look up TComponentStyle in C++Builder's online help for additional information on this property.
|
| Name |
The component's name.
|
| Tag | An integer property which has no defined meaning and can therefore be used at the developer's discretion to hold any user defined data. Since this value is an integer type, pointers to data structures, or even object instances may be referred to by this property.
|
| DesignInfo
| Internally used by the Form's Designer. Do not access this property.
|
The methods defined by TComponent have to do with TComponent's
capability to own other components and its accessibility in the
Object Inspector. TComponent's method definitions are shown below:
class __declspec(pascalimplementation) TComponent : public TPersistent
{
public:
__fastcall virtual TComponent(TComponent *AOwner);
__fastcall virtual ~TComponent(void);
void __fastcall DestroyComponents(void);
void __fastcall Destroying(void);
TComponent *__fastcall FindComponent(const System::AnsiString
AName);
virtual TComponent *__fastcall GetParentComponent(void);
virtual System::Boolean __fastcall HasParent(void);
void __fastcall FreeNotification(TComponent *AComponent);
void __fastcall InsertComponent(TComponent *AComponent);
void __fastcall RemoveComponent(TComponent *AComponent);
};
The methods Destroying and DestroyComponents sets the component
and its owned components to a state indicating that they are being
destroyed. You'll probably never have to deal with these functions
directly.
The FindComponent function is handy when you want to refer to
a component of which you only know the name but to which you don't
have a reference. For example, suppose you know that the main
form has a TEdit component named "Edit1". To get a pointer
to Edit1's instance use the following code.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TEdit * EditInstance = FindComonent("Edit1");
}
Here, we are issuing the FindComponent of the main form; therefore
this code will work as long as it resides in a function of the
main form. EditInstance must be a TEdit type. You can access properties,
and methods of the returned instance of FindComponent as shown
below as well.
(TEdit *)(FindComonent("Edit1"))->Text = "Hello";
It is necessary to typecast FindComponent's return reference because
the return reference is of the type TComponent. By typecasting
it to the type of the component to which it refers, you can access
the special properties and methods of that component type.
A component's HasParent function returns a boolean value indicating
whether or not the component has been assigned a parent. You would
use this function before referring to a component's parent. Note,
that this function does not indicate whether or not a component
has an owner.
The InsertComponent function adds the component passed as a parameter
as an owned component and RemoveComponent removes a component
from the list of owned components.
TControl
TControls defines data members, methods, and events common to
visual components. TControl for example has the capability to
display itself. Therefore, some of its properties have to do with
its size and position: Top, Left, Width and Height. Other properties
are ClientRect, ClientWidth ClientHeight.
TControl also introduces various properties having to do with
its appearance and accessibility, such as: Visible, Enabled and
Color. The Font property lets you specify a particular font for
the control. Also you can set the text for the control through
TControl's Text and Caption properties.
TControl contains several mouse and drag-drop events required
of visual controls. Such events are: OnClick, OnDblClick, OnMouseDown,
OnMouseMove, OnMouseUp, OnDragOver, OnDragDrop, and OnEndDrag.
One interesting note about these events is that they are declared
in the protected section of TControl. This is because Tcontrol
is most likely to be descended from rather than being used directly.
Declaring TControl's properties and events in the protected section
allows writers of descendant components to determine which properties
and/or events to make public or published.
Another important characteristic of TControl is that it may have
a parent. This parent must be a descendant of TWinControl since
parent controls must be windowed controls. Since TControl introduces
the concept of having a parent it introduces the Parent property
which refers to its parent.
Most of the C++Builder controls are descendants of either TWinControl
or TGraphicControl both of which are discussed next.
TWinControl
The TWinControl class encapsulates a window-controls with a window
handle. Certain descendants of TWinControl such as TEdit, TListBox
and TComboBox encapsulate the standard Windows controls such as
edit controls, list boxes, combo boxes respectively. Since these
descendant components encapsulate the functionality of standard
controls, you don't have to manipulate them through Win32 API
functions. Instead you manipulate them through properties and
methods provided by the various control components.
TWinControls have three basic characteristics: they have a window
handle, they receive input focus, and they can be parents to other
controls. Therefore, many of TWinControl's properties, functions
and events have something to do with focus changing, keyboard
events and the displaying of child controls.
TWinControl's various properties having
to do with focus changing and control appearance are shown in
the table below.
Used for drawing patterns and shapes
of the control.
| Property |
Purpose |
| Brush |
Color and pattern the canvas uses for filling graphical shapes and backgrounds.
|
| Controls |
Maintains a list of controls to which the said TWinControl is parent.
|
| ControlCount
| The number of children controls.
|
| Ctl3d |
Specifies whether or not to draw the control using a three-dimensional appearance.
|
| Handle |
Refers to the handle of the Win32 object which the said TWinControl
encapsulates. You would pass this property to Win32 API functions
where a windows handle is required as a parameter.
|
| HelpContext
| A help context number corresponding to a help screen in a help file.
This property makes it possible to provide context-sensitive help for individual controls.
|
| Showing |
Specifies if the control is visible or not.
|
| TabStop |
Specifies if the user can tab to the control.
|
| TabOrder |
Specifies where in the parents list of tabbed controls the said control is
positioned.
|
TWinControl's methods mainly have to
do with window creation, focus control, message dispatching, and
positioning.
TWinControl's methods are of interest to Component
Writers; their definitions are shown below:
class __declspec(pascalimplementation) TWinControl
: public TControl
{
public:
__fastcall virtual TWinControl(Classes::TComponent
*AOwner);
__fastcall virtual ~TWinControl(void);
void __fastcall Broadcast(void *Message);
System::Boolean __fastcall CanFocus(void);
System::Boolean __fastcall ContainsControl(TControl
*Control);
TControl *__fastcall ControlAtPos(const Windows::TPoint
&Pos, System::Boolean AllowDisabled);
void __fastcall DisableAlign(void);
void __fastcall EnableAlign(void);
System::Boolean __fastcall Focused(void);
virtual void __fastcall GetTabOrderList(Classes::TList
*List);
System::Boolean __fastcall HandleAllocated(void);
void __fastcall HandleNeeded(void);
void __fastcall InsertControl(TControl *AControl);
virtual void __fastcall Invalidate(void);
void __fastcall PaintTo(System::Integer DC, System::Integer
X, System::Integer Y);
void __fastcall RemoveControl(TControl *AControl);
void __fastcall Realign(void);
virtual void __fastcall Repaint(void);
void __fastcall ScaleBy(System::Integer M, System::Integer
D);
void __fastcall ScrollBy(System::Integer DeltaX,
System::Integer DeltaY);
virtual void __fastcall SetBounds(System::Integer
ALeft, System::Integer ATop,
System::Integer AWidth, System::Integer AHeight);
virtual void __fastcall SetFocus(void);
virtual void __fastcall Update(void);
};
Broadcast is used to send a message to all child
windows of the TWinControl.
CanFocus returns a bool value that determines whether
or not the TWinControl can get focus. A control cannot get focus
is when it's Visible property is set to false, for example.
ContainsControl determines whether or not a given
control is contained within the TWinControl. This is not the same
as determining whether or not the control is a child to the TWinControl
but rather is contained by the TWinControl. For example, a TWinControl
may be a parent to another TWinControl. This child control may
itself be a parent to other controls. These other controls are
still contained by the outer TWinControl.
It is possible to search for a child control at a
given position relative to the parent control by using the ControlAtPos
function. This function takes the client coordinates of the parent
control as a parameter. If a child control appears at these coordinates
a reference to that control is returned, otherwise, 0 is returned.
The AllowDisabled parameter determines whether or not disabled
controls can be returned as well.
The DisableAlign and EnableAlign functions are used
to temporarily disable and enable the alignment of controls within
a TWinControl. Usually, this happens when performing scaling operations
or reading a form file.
The Focused function returns true if the TWinControl
as focus and is therefore the ActiveControl of the form on which
it sits.
HandleAllocated returns true if the control has a
valid handle. Whenever you access a TWinControl's Handle property
directly, a new handle is created automatically if one was not
yet created. Therefore, you would use HandleAllocated to query
if a control has a handle without causing one to be created. HandleNeeded
is the procedure that creates a windows handle for the TWinControl
if it is needed.
The InsertControl procedure adds the control passed
as a parameter to the TWinControl's Controls array. This makes
the added control a child to the TWinControl.
RemoveControl removes the control from the TWinControl's
Controls array. Normally, if you are adding a control as a child
to another control at run-time, you would just make the assignment
of the controls Parent property to specify its parent rather than
calling InsertControl.
The Invalidate and Repaint functions cause the control
to repaint itself. Repaint causes Windows to process any paint
messages as well by calling the Update method which in turn calls
the Win32 API function, UpdateWindow. PaintTo can be used to paint
the contents of a TWinControl to the device context of another
control. The ReAlign functions forces the TWinControl to realign
the controls within it. ScaleBy is used to scale the TWinControl
to a percentage of its original size. The ScrollBy method may
be used if you do not want to use the default scrolling logic
of a TWinControl. Normally, you'll have no need to use this function.
The SetBounds function sets the Left, Top, Width,
and Height properties of the TWinControl. Using SetBounds is more
efficient than setting these properties individually since setting
them individually causes the control to repaint itself for each
property assignment.
SetFocus makes the TWinControl the ActiveControl.
Some protected functions of interest to component
designers are: CreateParams, CreateWnd, CreateWindowHandle, DestroyWnd,
DestroyWindowHandle and RecreateWnd. These member functios have
to do with the creation and destruction of the window handle and
window which the TWinControl encapsulates.
The CreateWnd functions creates the window control
encapsulated by the TWinControl by first calling CreateParams
and the CreateWindowHandle.
CreateParams initializes any windows creation parameters.
CreateParams may be overriden to change any of the default window
creation parameters.
CreateWindowHandle creates the window handle by calling
the Win32 API function CreateWindowEx. CreateWindowHandle is passed
the window creation parameters set up in CreateParams.
DestroyWnd destroys the encapsulated windows control
by calling DestroyWindowHandle, which in turn calls the Win32
API function DestroyWindow. DestroyWnd performs some additional
logic to save the windows text and to free any device contexts
associated with the windowed control.
TWinControl has events for keyboard interaction and
focus change. These events are:
OnKeyDown, OnKeyPress, OnKeyUp, OnEnter and OnExit.
All of these events are documented in C++Builder's online help.
TGraphicControl
TGraphicControls differ from TWinControls in that
they do not have a window handle and cannot receive input focus.
Also, they cannot be parents to other controls
TGraphicControls are used when you want to display
something on the form, without the functionality(ies) of a regular
windowed controls. Two key advantages to this strategy are:
TGraphicControls don't use up system resources since
they don't require a window handle. They are a bit faster at drawing
then their TWinControl counterparts since their painting is not
subject to the Windows message dispatching system. Instead, they
piggyback on their parent's paint process.
TGraphicControls can respond to mouse events and
therefore have mouse event handlers.
Since TGraphicControl allows you to paint the itself,
it contains a TCanvas property, Canvas, and a Paint method which
TGraphicControl descendants must override.
TCustomControl
The standard controls which descend from TWinControl
like TEdit and TListbox already have default drawing capabilities,
provided by the encapsulated Windows control. What if you want
to create a windowed component that provides its own custom painting?
This is the purpose of TCustomControl.
TCustomControl is a descendant of TWinControl and
is therefore a windowed control. This means that TCustomControl
can also get input focus. Component writers create components
that can descend from TCustomControl. Like the TGraphicControl,
TCustomControl surfaces its Canvas property, which allows for
custom drawing to its canvas. In fact, TCustomControl's provides
a virtual Paint functions which you override to perform your custom
drawing.
Conclusion
Whether you plan use the Visual Component Library
as an Applications Developer, or expand the existing library as
a Component Writer, your understanding of the VCL will only make
it easier for you to successfully accomplish either task. The
VCL may seem very complex at first. However, by having a basic
understanding of the its hierarchy and by knowing the role played
by key objects within the VCL, you will be able to effectively
maximize your use of the VCL to create better applications and
more powerful components.
Connect with Us