The Visual Component Library
By Xavier Pacheco
Delphi Developer Support
Borland International
VCL hierarchy overview
The figure below illustrates a sub-set of Delphi 2.0's VCL 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 unit and is
defined as:
Type
TObject = class
constructor Create;
destructor Destroy; virtual;
procedure Free;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
class procedure InitInstance(Instance: Pointer): TObject;
function ClassType: TClass;
class function ClassName: string;
class function ClassParent: TClass;
class function ClassInfo: Pointer;
class function InstanceSize: Word;
class function InheritsFrom(AClass: TClass): Boolean;
procedure DefaultHandler(var Message); virtual;
procedure Dispatch(var Message);
class function MethodAddress(const Name: string): Pointer;
class function MethodName(Address: Pointer): string;
function FieldAddress(const Name: string): Pointer;
end;
These methods are documented in Delphi 2.0's online help.
Note that some methods that are preceded by the keyword class.
These methods 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 methods. This functionality
was borrowed from C++'s static functions.
All components must descend from TComponent or from a TComponent
descendant. TComponent, being a descendant of TObject,
inherits TObjects methods, fields 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 Create constructor and Destroy
destructor allocate and de-allocate memory for the object's instance
respectively. The TObject.Create constructor returns a
reference to the object being created.
Most of TObjects methods are used internally by the VCL.
You will primarily call the Create and Free methods.
One caution: try to use the Free method instead of calling
the Destroy destructor directly when freeing an object
instance. Free is a bit safer to use because it first checks
to make sure the component instance is not Nil before destroying
it.
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 methods.
Method | 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 method is called. It is up to TPersistent descendant to override this method 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 Delphi 2.0's online help for additional information on this property.
|
ComponentStyle | A style that dictates the behavior of a component. Look up TComponentStyle in Delphi 2.0'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:
TComponent = class(TPersistent)
...
public
constructor Create(AOwner: TComponent); virtual;
destructor Destroy; override;
procedure DestroyComponents;
procedure Destroying;
function FindComponent(const AName: string): TComponent;
function HasParent: Boolean; dynamic;
procedure InsertComponent(AComponent: TComponent);
procedure RemoveComponent(AComponent: TComponent);
...
end;
The methods Destorying 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 methods directly.
The FindComponent method 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.
procedure TMainForm.Button1Click(Sender: TObject);
var
EditInstance: TEdit;
begin
EditInstance := FindComonent.('Edit1');
end;
Here, we are issuing the FindComponent of the main form;
therefore this code will work as long as it resides in a method
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(FindComponent('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 method returns a boolean
value indicating whether or not the component has been assigned
a parent. You would use this method before referring to a component's
parent. Note, that this method does not indicate whether or not
a component has an owner.
The InsertComponent method 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 properties, 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 Delphi 2.0 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,
methods 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.
Property | Purpose
|
Brush | Used for drawing patterns and shapes of the control.
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:
TWinControl = class(TControl)
...
public
procedure Broadcast(var Message);
function CanFocus: Boolean;
function ContainsControl(Control: TControl): Boolean;
function ControlAtPos(const Pos:
TPoint; AllowDisabled: Boolean): TControl;
procedure DisableAlign;
procedure EnableAlign;
function Focused: Boolean;
procedure GetTabOrderList(List: TList); dynamic;
function HandleAllocated: Boolean;
procedure HandleNeeded;
procedure InsertControl(AControl: TControl);
procedure Invalidate; override;
procedure PaintTo(DC: HDC; X, Y: Integer);
procedure RemoveControl(AControl: TControl);
procedure Realign;
procedure Repaint; override;
procedure ScaleBy(M, D: Integer);
procedure ScrollBy(DeltaX, DeltaY: Integer);
procedure SetBounds(ALeft, ATop,
AWidth, AHeight: Integer); override;
procedure SetFocus; virtual;
procedure Update; override;
...
end;
Broadcast is used to send a message to all child windows
of the TWinControl.
CanFocus returns a boolean 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, nil is returned.
The AllowDisabled parameter determines whether or not disabled
controls can be returned as well.
The DisableAlign and EnableAlign methods 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 procedures 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 method
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 method.
The SetBounds method sets the Left, Top,
Width, and Height properties of the TWinControl.
Using SetBounds is more efficient then 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 methods of interest to component designers are:
CreateParams, CreateWnd, CreateWindowHandle,
DestroyWnd, DestroyWindowHandle and RecreateWnd.
These methods have to do with the creation and destruction of
the window handle and window which the TWinControl encapsulates.
The CreateWnd procedure 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 Delphi'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 method 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