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.

Note: The views and information expressed in this document represent those of its author(s) who is solely responsible for its content. Borland does not make or give any representation or warranty with respect such content.

Introduction

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. It discusses the purposes of the common properties, methods and events that appear at the different component levels in the VCL hierarchy.

This paper does not discuss individual components and classes that make up the VCL. Its goal is to further the reader's understanding of how various components and classes are built.

This paper assumes that you have a general working knowledge of the C++ language and C++Builder's component usage.

The History of VCL
The Visual Component Library, introduced in Delphi 1.0, is a framework consisting of classes and components that you use to create Delphi applications. VCL was updated to 32 bit with Delphi 2. Delphi has been very successful in the RAD market, however, many users have expressed the interest in a C++ RAD tool, which eventually was born as C++Builder. C++Builder uses an unmodified version of Delphi 2 Visual Component Library. This paper concentrates on the usage of VCL in C++Builder, although the concepts also apply to Delphi users.

This VCL is designed so that you can manipulate these classes within C++Builder's visual environment, at design-time, while you create your application. This differs from many other development environments, where the behavioral and visual characteristics of your application are handled at run-time. In C++Builder, you modify the behavioral and visual characteristics of your components as you develop your application visually-although you can modify component behavior and appearance at run-time as well.

You should have a good working knowledge of the VCL. The depth of knowledge that you require depends on how you intend on using the VCL. Therefore, you should realize that there are two types of C++Builder developers: Applications Developers and Component Writers.

The VCL to Applications Developers
Applications developers create complete applications by interacting with the C++Builder visual environment (as mentioned earlier, this is a concept nonexistent in many other frameworks). These people use the VCL to create their user-interface and the other elements of their application: database connectivity, data validation, business rules, etc..

Applications developers should know which properties, events, and methods each makes available. Additionally, by understanding the VCL architecture, Applications Developers will be able to easily identify where they can improve their applications by extending components or creating new ones. Then they can maximize the capabilities of these components, and create better applications.

Because C++Builder and Delphi 2.0 are built upon the same Borland Visual Component Library, third party components written in Delphi will work in C++Builder as well. Application developers should be aware of the mass third party components market and look for proper third party tools when new functionality is needed.

The VCL to Component Writers
Component writers expand on the existing VCL, either by developing new components, or by increasing the functionality of existing ones. Many component writers make their components available for Applications Developers to use.

A component writer must take their knowledge of the VCL a step further than that of the Application Developer. For example, they must know whether to write a new component or to extend an existing one when the need for a certain characteristic arises. This requires a greater knowledge of the VCL's inner workings.

A component writer can choose the tool he prefers, C++Builder or Delphi, to create new components. He also needs to ensure that the components works in both environments.

The VCL is made up of components
Components are the building blocks that developers use to design the user-interface and to provide some non-visual capabilities to their applications. To an Application Developer, a component is an object most commonly dragged from the Component and placed onto a form. Once on the form, one can manipulate the component's properties and add code to the component's various events to give the component a behavior. To a Component Writer, components are objects in C++ or Object Pascal code. Some components encapsulate the behavior of elements provided by the system, as the standard Windows95 controls. Other objects introduce entirely new visual non-visual elements, in which case the component's code makes up the entire behavior of the component.

Image 1

The complexity of different components varies widely. Some might be simple while others might encapsulate an elaborate task. There is no limit to what a component can do or be made up of. You can have a very simple component like a TLabel, or a much more complex component which encapsulates the complete functionality of a spreadsheet.

Component Types, Structure, and VCL hierarchy
Components are really just special types of objects. In fact, a component's structure is on the rules that apply to C++ with few exceptions. There are three fundamental keys to understanding the VCL.

First, you should know the special characteristics of the four basic component types: standard controls, custom controls, graphical controls and non-visual components.

Second, you must understand the VCL structure with which components are built. This really ties into your understanding of C++'s implementation.

Third, you should be familiar with the VCL hierarchy and you should also know where the four component types previously mentioned fit into the VCL hierarchy. The following paragraphs will discuss each of these keys to understanding the VCL.

Component Types

As a component writer, there are four primary types of components that you will work with in C++Builder: standard controls, custom controls, graphical controls, and non-visual components. Although these component types are primarily of interest to component writers, it's not a bad idea for applications developers to be familiar with them. They are the foundations on which applications are built.

Standard Components
Some of the components provided by C++Builder encapsulate the behavior of the standard Windows controls: TButton, TListbox and TEdit, for example. You will find these components on the Standard page and Win95 page of the Component Palette. These components are Windows' common controls with VCL wrappers around them.

Each standard component looks and works like the Windows' common control which it encapsulates. The VCL wrappers simply make the control available to you in the form of a C++Builder component-it doesn't define the common control's appearance or functionality, but rather, surfaces the ability to modify a control's appearance/functionality in the form of methods and properties. If you have the VCL source code, you can examine how the VCL wraps these controls in the file STDCTRLS.PAS.

If you want to use these standard components unchanged, there is no need to understand how the VCL wraps them. If, however, you want to extend or change one of these components, then you must understand how the Window's common control is wrapped by the VCL into a component.

For example, the Windows class LISTBOX can display the list box items in multiple columns. This capability, however, isn't surfaced by C++Builder's TListBox component (which encapsulates the Windows LISTBOX class). (TListBox only displays items in a single column.) Surfacing this capability requires that you override the default creation of the TListBox component.

This example also serves to illustrate why it is important for Applications Developers to understand the VCL. Just knowing this tidbit of information helps you to identify where enhancements to the existing library of components can help make your life easier and more productive.

Custom components
Unlike standard components, custom components are controls that don't already have a method for displaying themselves, nor do they have a defined behavior. The Component Writer must provide to code that tells the component how to draw itself and determines how the component behaves when the user interacts with it. Examples of existing custom components are the TPanel and TStringGrid components.

It should be mentioned here that both standard and custom components are windowed controls. A "windowed control" has a window associated with it and, therefore, has a window handle. This will be discussed in detail later in the paper in TWinControl section. Windowed controls have three characteristics: they can receive the input focus, they use system resources, and they can be parents to other controls. (Parents is related to containership, discussed later in this paper.) An example of a component which can be a container is the TPanel component.

Graphical components
Graphical components are visual controls which cannot receive the input focus from the user. They are non-windowed controls. Graphical components allow you to display something to the user without using up any system resources; they have less "overhead" than standard or custom components. Graphical components don't require a window handle-thus, they cannot can't get focus. Some examples of graphical components are the TLabel and TShape components.

Graphical components cannot be containers of other components. This means that they cannot own other components which are placed on top of them.

Non-visual components
Non-visual components are components that do not appear on the form as controls at run-time. These components allow you to encapsulate some functionality of an entity within an object. You can manipulate how the component will behave, at design-time, through the Object Inspector. Using the Object Inspector, you can modify a non-visual component's properties and provide event handlers for its events. Examples of such components are the TOpenDialog, TTable, and TTimer components.

Structure of a component
Traditionally, a class definition of a C++ class contains data members which holds data of interest to the object, and methods which define the behavior of the object. This concept applies to other Object-oriented languages too, including Smalltalk and Java. Subclassing is used to add new behaviors.

In VCL, the same concept applies. Each object contains data members and methods, and the programmer can still use methods to manipulate the object. However, VCL takes this one step further - it uses the concept of component, properties, and events. Each component consists of common elements that allow developers to manipulate its appearance and function via properties and events, in addition to methods. The following sections in this paper will discuss these common elements as well as talk about a few other characteristics of components which don't apply to all components.

Component properties
Properties provide an extension of an object's data members. Unlike data members, properties do not store data: they provide other capabilities. For example, properties may use methods to read or write data to an object data member to which the user has no access. This adds a certain level of protection as to how a given data member is assigned data. Properties also cause "side effects" to occur when the user makes a particular assignment to the property. Thus what appears as a simple data member assignment to the component user could trigger a complex operation to occur behind the scenes.

C++Builder keyword: __property

C++Builder uses the keyword "__property" to identify properties. The syntax is as follows:

__property type_identifier identifier = property_specifiers;

property_specifiers contains read specifier, write specifier, stored specifier, and default specifier. The next section of this paper illustrates the use of the __property keyword.

Properties provide access to data members
There are two ways that properties provide access to internal data members of components: directly or through getter/setter functions. Examine the code below which illustrates this process.

class __declspec(delphiclass) TCustomEdit;

class __declspec(pascalimplementation) TCustomEdit : public Controls::TWinControl

{

private:

System::Integer FMaxLength;

void __fastcall SetMaxLength(System::Integer Value);

protected:

__property System::Integer MaxLength =

{read=FMaxLength, write=SetMaxLength, default=0};

};

The code above is snippet of the TCustomEdit component class. TCustomEdit is the base class for edit boxes and memo components such as TEdit, and TMemo.

TCustomEdit has an internal data member FMaxLength of type Integer which specifies the maximum length of characters which the user can enter into the control. The user doesn't directly access the FMaxLength data member to specify this value. Instead, a value is added to this data member by making an assignment to the MaxLength property.

The property MaxLength provides the access to the data member FMaxLength. The property definition is comprised of the property name, the property type, a read declaration, a write declaration and optional default value.

The read declaration specifies how the property is used to read the value of a data member. For instance, the MaxLength property has direct read access to FMaxLength. The write declaration for MaxLength shows that assignments made to the MaxLength property results in a call to a setter function which is responsible for assigning a value to the FMaxLength data member. This setter function is SetMaxLength.

Getter and setter functions
Setter functions, take a single parameter of the same type as the property. One of the primary reasons for setter functions is to cause some side-effect to occur as a result of an assignment to a property. Setter functions also provide a function layer over assignments made to a component's data members. Instead of the component user making the assignment to the data member directly, the property's setter function will assign the value to the data member if the property refers to a particular data member.

For example, examine the implementation of the SetMaxLength function below. The original source code is written in Delphi, it has been translated into C++ for this sample.

void TCustomEdit::SetMaxLength(int Value)

{

if (FMaxLength != Value)

{

FMaxLength = Value;

if (HandleAllocated)

SendMessage(Handle, EM_LIMITTEXT, Value, 0);

}

}

The code in the SetMaxLength method checks if the user is assigning the same value as that which the property already holds. This is done as a simple optimization. The function then assigns the new value to the data member, FMaxLength. Additionally, the method then sends an EM_LIMITTEXT Windows message to the window which the TCustomEdit encapsulates. The EM_LIMITTEXT message places a limit on the amount of text that a user can enter into an edit control. This last step is what is referred to as a side-effect when assigning property values. Side effects are any additional actions that occur when assigning a value to a property and can be quite sophisticated.

It is also possible to have getter functions for the read access of a property. The getter function might return a type which is different from that of a property's data member. For instance, it could return the string representation of a data member. For example, TField component has AsString, AsFloat, AsInteger properties, all of them are pointing to the same internal storage. Providing access to data members through setter/getter functions offers the advantage that the Component Writer can modify the implementation of a class without modifying the interface.

Another fundamental reason for properties is that properties are accessible for modification at run-time through C++Builder's Object Inspector. This occurs whenever the declaration of the property appears in the published section of a component's declaration.

C++Builder keyword: __published

The properties comply with the same visibility rules as normal data member and methods. The rules for private, protected, and public keywords still apply. C++Builder's keyword: __published supports visibility rules that are identical to those of public members. The only difference is that Delphi-style run-time type information(RTTI) is generated for published data members and properties, and C++Builder Object Inspector uses RTTI to display them.

In the example above, the MaxLength property is defined in the protected section and thus can only be accessed by TCustomEdit's subclasses. TEdit re-declare MaxLength in __published to enable access from outside of the classes, and enable the property to be modified in the Object Inspector window.


Server Response from: ETNASC04