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.

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.

Image 6

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 functionPurpose
AssignThis public method allows a component to assign the data associated with another component to itself.
AssignToThis 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.
DefinePropertiesThis 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.
TagAn 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.

Server Response from: ETNASC02