Delphi components are classes, and the Visual Components Library (VCL) is the collection of all the classes defining Delphi components. You extend the VCL by writing new component classes in a package and installing it in Delphi. These new classes will be derived from one of the existing component-related classes or the generic TComponent class, adding new capabilities to those they inherit.
You can derive a new component from an existing component or from an abstract component class—one that does not correspond to a usable component. The VCL hierarchy includes many of these intermediate classes (often indicated with the TCustom prefix in their name) to let you choose a default behavior for your new component and change its properties.
Components are added to component packages. Each component package is basically a DLL (a dynamic link library) with a BPL extension (which stands for Borland Package Library).
Packages come in two flavors: design-time packages used by the Delphi IDE and run-time packages optionally used by applications. The design-only or run-only package option determines the package's type. When you attempt to install a package, the IDE checks whether it has the design-only or run-only flag, and decides whether to let the user install the package and whether it should be added to the list of run-time packages. Because there are two nonexclusive options, each with two possible states, there are four different kinds of component packages—two main variations and two special cases:
Design-only component packages can be installed in the Delphi environment. These packages usually contain the design-time parts of a component, such as its property editors and the registration code. Often they also contain the components themselves, although this is not the most professional approach. The code of a design-only package's components is usually statically linked into the executable file, using the code of the corresponding Delphi Compiled Unit (DCU) files. Keep in mind, however, that it is also technically possible to use a design-only package as a run-time package.
Run-only component packages are used by Delphi applications at run time. They cannot be installed in the Delphi environment, but they are automatically added to the list of run-time packages when they are required by a design-only package you install. Run-only packages usually contain the code of the component classes, but no design-time support (this is done to minimize the size of the component libraries you ship with your executable file). Run-only packages are important because they can be freely distributed with applications, but other Delphi programmers won't be able to install them in the environment to build new programs.
Plain component packages (having neither the design-only nor the run-only option set) cannot be installed and will not be added to the list of run-time packages automatically. This option might make sense for utility packages used by other packages, but such packages are certainly rare.
Packages with both flags set can be installed and are automatically added to the list of run-time packages. Usually these packages contain components requiring little or no design-time support (apart from the limited component registration code).
The filenames of Delphi's own design-only packages begin with the letters DCL (for example, DCLSTD60.BPL); filenames of run-only packages begin with the letters VCL ( for example, VCL60.BPL). You can follow the same approach for your own packages, if you wish.
In Chapter 1, "Delphi 7 and Its IDE," we discussed the effect of packages on the size of a program's executable file. Now we'll focus on building packages, because this is a required step in creating or installing components in Delphi.
When you compile a run-time package, you produce both a DLL with the compiled code (the BPL file) and a file with only symbol information (a DCP file), including no compiled machine code. The Delphi compiler uses the latter file to gather symbol information about the units that are part of the package without having access to the unit (DCU) files, which contain both the symbol information and the compiled machine code. This process reduces compilation time and allows you to distribute just the packages without the precompiled unit files. The precompiled units are still required to statically link the components into an application. Distribution of precompiled DCU files (or source code) may make sense depending on the kind of components you develop. You'll see how to create a package after we've discussed some general guidelines and built a component.
DLLs are executable files containing collections of functions and classes, which can be used by an application or another DLL at run time. The typical advantage is that if many applications use the same DLL, only one copy needs to be on the disk or loaded in memory, and the size of each executable file will be much smaller. This is what happens with Delphi packages, as well. Chapter 10, "Libraries and Packages," looks at DLLs and packages in more detail.
Some general rules govern the writing of components. You can find a detailed description of most of them in the Delphi Component Writer's Guide Help file, which is required reading for Delphi component writers.
Here is my own summary of the rules for component writers:
Study the Delphi language with care. Particularly important concepts are inheritance, method overriding and overloading, the difference between public and published sections of a class, and the definition of properties and events. If you don't feel confident with the Delphi language or basic VCL concepts, you can refer to the overall description of the language and library presented in Part I of the book, particularly Chapters 2 ("The Delphi Programming Language") and 4 ("Core Library Classes").
Study the structure of the VCL class hierarchy and keep a graph of the classes at hand (such as the one included with Delphi).
Follow the standard Delphi naming conventions. There are several for components, as you will see, and following these rules makes it easier for other programmers to interact with your components and further extend them.
Keep components simple, mimic other components, and avoid dependencies. These three rules basically mean that a programmer using your components should be able to use them as easily as preinstalled Delphi components. Use similar property, method, and event names whenever possible. If users don't need to learn complex rules about the use of your component (that is, if the dependencies between methods or properties are limited) and can access properties with meaningful names, they'll be happy.
Use exceptions. When something goes wrong, the component should raise an exception. When you are allocating resources of any kind, you must protect them with try/ finally blocks or destructor calls as appropriate.
To complete a component, add a bitmap to it, which will be used by Delphi's Component Palette. If you intend for your component to be used by more than a few people, consider adding a Help file as well.
Be ready to write real code and forget about the visual aspects of Delphi. Writing components generally means writing code without visual support (although Class Completion can speed up the coding of plain classes). The exception to this rule is that you can use frames to write components visually.
You can also use a third-party component-writing tool to build your component or to speed up its development. The most powerful third-party tool I know of for creating Delphi components is the Class Developer Kit (CDK) from Eagle Software (www.eagle-software.com), but many others are available.
To build a new component, you generally begin with an existing one or with one of the VCL base classes. In either case, your component is in one of three broad component categories (introduced in Chapter 4), set by the three basic classes of the component hierarchy:
TWinControl is the parent class of any component based on a window. Components that descend from this class can receive the input focus and get Windows messages from the system. You can also use their window handle when calling API functions. When creating a brand-new window control, you'll generally inherit from the derived class TCustomControl, which has a few extra useful features (particularly some support for painting the control).
TGraphicControl is the parent class of visible components that have no Windows handle (which saves some Windows resources). These components cannot receive the input focus or respond to Windows messages directly. When creating a brand-new graphical control, you'll inherit directly from this class (which has a set of features very similar to TCustomControl).
TComponent is the parent class of all components (including the controls) and can be used as a direct parent class for nonvisual components.
In the rest of the chapter, you will build some components using various parent classes, and we'll look at the differences among them. Let's begin with components inheriting from existing components or classes at a low level of the hierarchy, and then look at examples of classes inheriting directly from the ancestor classes just mentioned.