|  | Click Here!

Search the site:
EXPERT SEARCH

| Programming Languages
| Databases
| Security
| Web Services
| Network Services
| Middleware
| Components
| Operating Systems
| User Interfaces
| Groupware & Collaboration
| Content Management
| Productivity Applications
| Hardware
| Fun & Games

EarthWeb sites
Crossnodes
Datamation
Developer.com
DICE
EarthWeb.com
EarthWeb Direct
ERP Hub
Gamelan
GoCertify.com
HTMLGoodies
Intranet Journal
IT Knowledge
IT Library
JavaGoodies
JARS
JavaScripts.com
open source IT
RoadCoders
Y2K Info

| ## Charlie Calvert's C++ Builder Unleashed # - 22 - Creating Descendants of Existing Components ## Overview This chapter and the next two cover building components. Component development is one of the most important technologies in contemporary programming, and no C++ environment on the market makes them easier to use or to create than BCB. You build three types of components in this chapter: Descendants that change default settings in existing components. Descendants that add features to existing components. Tools built on top of abstract component base classes such as `TWinControl`, `TCustomControl`, `TComponent`, and `TGraphicControl`. You make descendants from these classes if you want to build your own components from the bottom up. This chapter presents material on building visual components, Chapter 23 focuses on building unique components that are polymorphic, and Chapter 24 explores building nonvisual components. Nonvisual components descend directly from `TComponent` and appear on the form only at design time. More specifically, the components built in this chapter fall into two categories: The first group is a set of `TEdit`, `TLabel`, and `TPanel` descendants that show how to change default colors, captions, and fonts. This section of the chapter also covers building components that consist of several different child components; that is, this section shows how to group components together to form new components. The specific example included with this book shows a panel that comes with two radio buttons. The second tool is a clock component that can be dropped on a form, and stopped and started at will. In Chapter 24, you will see a nonvisual control that knows how to iterate through subdirectories. You can use it to build programs that search for files, delete all files with a certain extension, and so on. Besides components, this chapter also briefly covers two related topics: Property editors are used to edit the properties of components. The classic examples are the common dialogs that pop up when you edit the `Color` or `Font` properties that belong to most visible components. The drop-down lists and string-editing capabilities found in the Object Inspector are also property editors. Component editors are associated not with a single property, but with an entire component. An example is the Fields Editor used with the `TTable` and `TQuery` components. The property editors and component editors are related to a broader topic called the Tools API. The Tools API consists of a series of interfaces to the BCB IDE that allows you to build experts, interfaces to version control systems, and similar utilities. The API for property editors and component editors and the Tools API are defined in files with names that end in `INTF`, which stands for "Interface." For example, the `TPropertyEditor` class is found in `DSGNINTF.HPP`. Components, properties, and events, along with their associated component editors and property editors are perhaps the most important topics in BCB programming. In many ways, this chapter and the next two are the keystones around which this entire book has been designed. The source for all the components created in this chapter is found in the `Chap22` and `Utils` directories on the CD that accompanies this book. The source on the CD compiles correctly, and all of these components work as described in this chapter. Because of the default behavior of the C++ linker, you might have to run a program called BuildObjs before installing some of these components. This program is in the `Utils` directory. A description of this program is in the readme file on the CD. If you have any trouble with the code in this chapter, turn to the readme! ## Component Theory BCB components have three outstanding strengths: They are native components, built in BCB's language, without recourse to a complicated API such as OLE. You therefore can write, debug, and test your components using the same rules you use in standard BCB programs. You don't need to learn a whole new programming model, as you do when you write ActiveX controls. In short, writing a BCB component is about 10 times easier than writing an OCX or VBX component. They are fully object-oriented, which means you can easily change or enhance existing components by creating descendant objects. They are small, fast, and light, and can be linked directly into your executables. Native BCB components are orders of magnitude smaller than most ActiveX controls, and they link directly into your programs. In particular, a native BCB component is just a particular kind of object, so it links into your programs naturally, just as any other object would, without any handwaving. Few people would claim that VBXs aren't groundbreaking, that ActiveX controls aren't going to be very important, or that OLE2 is not an enormously promising architecture. However, BCB components are relatively easy to create and come in a light, easy-to-use package. You can create BCB components that do nearly anything, from serial communications to database links to multimedia. These capabilities give BCB a big advantage over other tools that force you to use large, complex, and unwieldy component models. NOTE: Most publicly available components cost in the range of $50 to $150. Many of these tools encapsulate functionality that might cost tens of thousands of dollars to produce in-house. For example, a good communication library might take a year to build. However, if a company can sell the library in volume, it can afford to charge $100 or $200 for the same product. That's a real bargain. And most of these tools are easy to use. Building components is a great way for relatively small third-party companies to make money, and buying components is a great way to save time on big projects. These ground-breaking tools are changing everything about the way programs are constructed. BCB components are flexible tools easily built by anyone who knows OOP and the BCB language. In this package, you have explanations detailing all the prerequisite knowledge that component builders need, from a description of BCB itself, through a description of its language, and on to an overview of its implementation of OOP. From this foundation, you can easily begin building your own components. ## Creating Descendants of an Existing Component In this section, you will see how to create a series of custom `TEdit`, `TPanel`, and `TLabel` controls. The changes made to the standard `TEdit` and `TLabel` components involve tweaking their colors, as well as their fonts' colors, names, sizes, and styles. The goal is to show how to create a suite of custom controls that you can place on the Component Palette and use for special effects, or to define the look and feel of a certain set of applications belonging to a particular department or company. With projects like this, starting with one simple example is best, and then you can move on to more complex components. Listings 22.1 through 22.3 contain a sample component and a program that allows you to test the new component before you place it on the Component Palette. The component is stored in a module called `Unleash1`. The `Unleash1` source code is the first version of a unit that will be expanded later in the chapter. Scan through it and check out its basic structure. Once it is clear to you how to create and test the component, I will briefly discuss how to place it on the Component Palette. #### Listing 22.1. The header file for a simple component descending from TEdit.
    ///////////////////////////////////////

    // Unleash1.h

    // Simple example of creating a component

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef Unleash1H

    #define Unleash1H

    //--------------------------------------------------------------------------

    #include <vcl\sysutils.hpp>

    #include <vcl\controls.hpp>

    #include <vcl\classes.hpp>

    #include <vcl\forms.hpp>

    #include <vcl\StdCtrls.hpp>

    //--------------------------------------------------------------------------

    class TSmallEdit : public TEdit

    {

    private:

    protected:

    public:

      virtual __fastcall TSmallEdit(TComponent* Owner);

    __published:

    };

    //--------------------------------------------------------------------------
`#endif` #### Listing 22.2. The code for a simple component descending from TEdit.
    ///////////////////////////////////////

    // Unleash1.cpp

    // Simple example of creating a component

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Unleash1.h"

    //--------------------------------------------------------------------------

    static inline TSmallEdit *ValidCtrCheck()

    {

      return new TSmallEdit(NULL);

    }

    //--------------------------------------------------------------------------

    __fastcall TSmallEdit::TSmallEdit(TComponent* Owner)

      : TEdit(Owner)

    {

      Color = clBlue;

      Font->Color = clYellow;

      Font->Name = "Times New Roman";

      Font->Size = 12;

      Font->Style = TFontStyles() << fsBold << fsItalic;

    }

    //--------------------------------------------------------------------------

    namespace Unleash1

    {

      void __fastcall Register()

      {

        TComponentClass classes[1] = {__classid(TSmallEdit)};

        RegisterComponents("Unleash", classes, 0);

      }

    }
`//--------------------------------------------------------------------------` #### Listing 22.3. The main form for the TestUnleash1 program serves as a test bed for the Unleash1 unit.
    ///////////////////////////////////////

    // Main.cpp

    // Simple example of creating a component

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #include "Unleash1.h"

    #pragma link "unleash1"

    #pragma resource "*.dfm"

    TForm1 *Form1;

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

    }

    void __fastcall TForm1::Button1Click(TObject *Sender)

    {

      TSmallEdit *MyEdit = new TSmallEdit(this);

      MyEdit->Parent = this;

      MyEdit->Show();
`}` This program is designed to test the `TSmallEdit` control before you add it to the Component Palette. The test program has a single button that responds to clicks. If you click the program's button, the new component appears at the top left of the form, as shown in Figure 22.1. Once again, the goal of the program is to make sure the new edit control is meeting your expectations before trying to compile it as a component. Figure 22.1.At runtime, the `TestUnleash1` main form features a `TButton` and a custom `Edit` control called `TSmallEdit`. You can easily create this unit, test it, and compile it as a component that's merged in with the rest of the tools on the Component Palette. To start creating the component, choose File | New and select Component from the first page of the Object Repository. The dialog shown in Figure 22.2 then appears. Figure 22.2.The Component Wizard dialog. The Component Wizard is a simple code generator, of the type that any reader of this book who has made it this far should be able to write in an hour or two. It simply asks you for the name of the component you want to create and to then select its parent from a drop-down list. After you define the type of tool you want to create, you can select the page in the Component Palette where you want it to reside. You should fill in the blanks with the following information:
    Class Name: TSmallEdit

    Ancestor type: TEdit

    Palette Page: Unleash
For your efforts, the Component Expert churns out the code in Listings 22.4 and 22.5, in which everything is boilerplate except for the first line of the class declaration, the constructor, and the parameters passed to the `RegisterComponents` method. #### Listing 22.4. The header produced by the standard boilerplate output of the Component Expert.
    //--------------------------------------------------------------------------

    #ifndef Unleash1H

    #define Unleash1H

    //--------------------------------------------------------------------------

    #include <vcl\sysutils.hpp>

    #include <vcl\controls.hpp>

    #include <vcl\classes.hpp>

    #include <vcl\forms.hpp>

    #include <vcl\StdCtrls.hpp>

    class TSmallEdit : public TEdit

    {

    private:

    protected:

    public:

      virtual __fastcall TSmallEdit(TComponent* Owner);

    __published:

    };
`#endif` #### Listing 22.5. The implementation of TSmallEdit as produced by the standard boilerplate output of the Component Expert.
    //--------------------------------------------------------------------------

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Unleash1.h"

    //--------------------------------------------------------------------------

    static inline TSmallEdit *ValidCtrCheck()

    {

      return new TSmallEdit(NULL);

    }

    //--------------------------------------------------------------------------

    __fastcall TSmallEdit::TSmallEdit(TComponent* Owner)

      : TEdit(Owner)

    {

    }

    //--------------------------------------------------------------------------

    namespace Unleash1

    {

      void __fastcall Register()

      {

        TComponentClass classes[1] = {__classid(TSmallEdit)};

        RegisterComponents("Unleash", classes, 0);

      }

    }
`//--------------------------------------------------------------------------` The Component Expert starts by giving you `#include` directives designed to cover most of the bases you are likely to touch in a standard component:
    #include <vcl\sysutils.hpp>

    #include <vcl\controls.hpp>

    #include <vcl\classes.hpp>

    #include <vcl\forms.hpp>

    #include <vcl\StdCtrls.hpp>
The next step is to give you a basic class declaration, in which the name and parent are filled in with the choices you specified in the Component Expert dialog. All this business about the scoping directives is just for your convenience, and you can delete any portion of it that you don't think you'll need.
    class TSmallEdit : public TEdit

    {

    private:

      // Private declarations

    protected:

      // Protected declarations

    public:

      // Public declarations

      virtual __fastcall TSmallEdit(TComponent* Owner);

    __published:

      // Published declarations

    };
Before you can place a component on the Component Palette, you first must register it with the system:
    void __fastcall Register()

    {

      TComponentClass classes[1] = {__classid(TSmallEdit)};

      RegisterComponents("Unleash", classes, 0);

    }
Registering a class makes it known to the BCB Component Palette when the unit is compiled into the BCB component library. The `Register` procedure has no impact on programs compiled with this unit. Unless your program calls the `Register` procedure (which it should never do), the code for the `Register` procedure will never execute. This method first creates an array consisting of the types of components you want to register. Recall that `__classid` is one of the new pieces of syntax added to the language. It is dis- cussed in Chapter 2, "Basic Facts About C++Builder." After creating the array, you call `RegisterComponents`, which takes the name of the page you want to use in the Component Palette in the first parameter, the array of metaclasses in the second parameter, and the size of the array in the third parameter. If the page does not exist, it will be created automatically. Here is the declaration for `RegisterComponents`:
    extern void __fastcall RegisterComponents(const System::AnsiString Page,

      System::TMetaClass* const * ComponentClasses, const int ComponentClasses_Size);
Recall that `TMetaClass` is declared in `SysDefs.h` and that it represents a subset of `TObject`. You can get the metaclass of a VCL object by calling the object's `ClassType` method. If you look back at the source for the whole project, you will see that the `Register` method is placed in its own namespace. This happens because multiple declarations for methods with that name will occur throughout your projects and, indeed, inside BCB itself. As a result, the compiler's need for order must be satiated with a few discreetly placed namespaces. This whole subject will come up again in just a moment, when I talk about recompiling `CmpLib32.ccl`. After using the Component Expert, you should save the project. Proceed as you normally would by creating a directory for the project and saving `Main.pas` and `TestUnleash1.cpp` inside it. You should not save the new unit that you created into the same directory, however, but you should place it in the `Utils` directory where you store files such as `CodeBox`. This code is now going to come into play as part of your system, and as such you want a single path that leads to all related files of this type. If you have all your components in different subdirectories, you will end up with a source path that is long and unwieldy. NOTE: `ValidCtrCheck()` verifies that creating an instance of the object at compile time is possible. If you forget to override a pure virtual function, the compilation of the component will fail because the statement `"new foo(0)"` is now invalid. You can test this by creating a component derived from `TCustomGrid` (an abstract class) and installing it without doing any work to change the object. Because `ValidCtrCheck` is inline and never called, no code will be generated--so there is no runtime cost. The goal of this project is to give a component of type `TEdit` a new set of default behaviors so that it starts out with certain colors and certain fonts. To do so, you need to override the `Create` method and change the fonts inside it. The method declaration itself appears automatically:
    class TSmallEdit : public TEdit

    {

    public:

      virtual __fastcall TSmallEdit(TComponent* Owner);

    };
Notice that in the preceding declaration I have removed the `private`, __`published`, and `protected` directives created by the Component Expert. Making this change is neither here nor there; I do it just to keep the amount of code you need to look at as small as possible. The `Create` method for `TSmallEdit` is declared as `public`. If you think about the process of creating a component dynamically, you will see that the `Create` method has to be `public`. This is one method that must be exposed. Like all VCL constructors, `TSmallEdit` is declared `virtual` and `__fastcall`. `Create` is passed a single parameter of type `TComponent`, which is a base class that encapsulates the minimum functionality needed to be an owner of another component and to place a component on the Component Palette. In particular, whatever form you place a component on usually will be the owner of that component. The implementation of the `Create` method is simple:
    __fastcall TSmallEdit::TSmallEdit(TComponent* Owner)

      : TEdit(Owner)

    {

      Color = clBlue;

      Font->Color = clYellow;

      Font->Name = "Times New Roman";

      Font->Size = 12;

      Font->Style = TFontStyles() << fsBold << fsItalic;

    }
The code first calls `Create`, passing in the variable `AOwner`. As I stated previously, the owner of a component will often, though not always, be the form on which the component is to be displayed. In other words, the user will drop the component onto a form, and that form will become the owner of the component. In such a case, `AOwner` is a variable that points to the form. The VCL uses it to initialize the `Owner` property, which is one of the fields of all components. The next step is to define the color and font that you want to use, with `Font->Style` defined as follows:
    enum TFontStyle { fsBold, fsItalic, fsUnderline, fsStrikeOut };

    typedef Set<TFontStyle, fsBold, fsStrikeOut>  TFontStyles;
NOTE: Don't let all this gobbledygook confuse you! All that occurs in this declaration is that a simple `enum` type is declared, and then you see a `Set` declaration for the type that ranges from a low value of `fsBold` to a high value of `fsStrikeOut`. Piece of cake! If you want to add the underline and bold style to the text in the edit control, write the following:
    Font->Style = TFontStyles << fsBold << fsUnderline;
If you then want to add the italic style at runtime, write this:
    Font->Style = Font->Style << fsItalic;
To remove the style, write this line:
    Font->Style = Font->Style >> fsItalic;
At this stage, the code is ready to go on the Component Palette. However, most of the time when you write components, you should test them first to see whether they work. Even on a fast machine, the process of recompiling `CmpLib32.ccl` and adding your component to the Component Palette takes between 10 seconds and 2 minutes. Perhaps I'm a bit impatient, but that's a little too long a wait for me if all I need to tweak is a few aspects of my code. As a result, I test things first in a small program and then add the component to the IDE. To test the new class, drop a button on the program's main form, and create an `OnClick` handler:
    void __fastcall TForm1::Button1Click(TObject *Sender)

    {

      TSmallEdit *MyEdit = new TSmallEdit(this);

      MyEdit->Parent = this;

      MyEdit->Show();

    }
Don't forget to use View | Project Manager and add the unit to the project. This ensures that the `USEUNIT` macro is placed in your project source. This code creates the component and shows it on the main form. `this`, of course, is the way that `TForm1` refers to itself from inside one of its own methods. The owner of the new component is `Form1`, which will be responsible for disposing of the component when finished with it. This process happens automatically. You never need to worry about disposing of a visible component shown on a form. The parent of the component is also `Form1`. The `Parent` variable is used by Windows when it is trying to decide how to display the form on the screen. If you place a panel on a form and drop a button on the panel, the owner of that button is the form, but the parent is the panel. Ownership determines when and how the component is deallocated, and parental relationships determine where and how the component is displayed. Ownership is fundamentally a BCB issue, whereas parental relationships are primarily concerns of Windows. NOTE: The next paragraph in this section explains how to recompile `CmpLib32.ccl`. Remember that you should always keep a backup copy of `CmpLib32.ccl` on your hard drive. `CmpLib32.ccl` is stored in the `..\CBUILDER\BIN` directory. Beneath this directory, I have created another directory called `BACK` where I keep a backup copy of `CmpLib32.ccl`. If worse comes to worst, you can always copy the version of `CmpLib32.ccl` on your installation CD back into the `BIN` subdirectory. My personal experience is that everyone, sooner or later, makes a tasty Mulligan's Stew out of a copy of `CmpLib32.ccl`. The worst-case scenario is that you're on the road when this mess happens, and that you don't have access to your installation disk. Don't let this situation happen to you. Keep a backup copy of `CmpLib32.ccl` on your hard drive at all times! ?FIGURE 17.16. Creating SQL selection criteria in ReportSmith. CCL stands for C++Builder Component Library. After you run the program and test the component, the next step is to put it up on the Component Palette. To do so, choose Components | Install and then click the Add button. Browse through the directories until you find `Unleash1.Cpp`, select OK, and then close the Install Components dialog by clicking OK. At this point, a project called `CmpLib32.cpp` is compiled. This project creates a huge DLL called `CmpLib32.ccl`, which contains all the components in the Component Palette, all the component and property editors associated with those components, the form designer part of the IDE, the experts, and other support modules. After `CmpLib32.ccl` finishes recompiling, you can start a new project, turn to the newly created Unleash page, and drop your new component onto a form. It will have a blue background, default to Times New Roman, and have its font style set to bold and its font color to yellow. Notice that all the properties of `TEdit` have been inherited by `TSmallEdit`. That's OOP in action. You can see an example of the form that uses this control in Figure 22.3. Figure 22.3.Using the `TSmallEdit` control on a standard VCL form. ## Understanding the Project Source for CmpLib32 You can save the `CmpLib32.cpp` file used during compilation of `CmpLib32.ccl` to disk if you choose Options | Environment | Library | Save Library Source Code. After choosing this option and recompiling `CmpLib32.ccl`, you can go to the `\CBUILDER\BIN` directory and view your copy of `CmpLib32.cpp`. Here is an example of the source code produced by the IDE for use when compiling `CmpLib32.ccl`:
    //--------------------------------------------------------------------------

    // Component Palette

    // Copyright (c) 1996, 1996 by Borland International, All Rights Reserved

    //

    // Module generated by C++Builder to rebuild the Component

    // Palette Library (CmpLib32.ccl).

    //--------------------------------------------------------------------------

    //--------------------------------------------------------------------------

    // In order to be totally Delphi compatible, the Component palette must be

    // built with a namespaced version of the VCL library. Using namespace allows

    // us to have multiple functions all called `Register'.

    //--------------------------------------------------------------------------

    #if !defined(BCB_NAMESPACES)

    #define BCB_NAMESPACES

    #endif

    //--------------------------------------------------------------------------

    // Include DSTRING.H - Defines AnsiString support class

    //--------------------------------------------------------------------------

    #include <vcl\dstring.h>

    //--------------------------------------------------------------------------

    // The following are expanded inline to avoid pulling in the headers

    // and lengthen the compilation time when rebuilding the palette.

    //--------------------------------------------------------------------------

    typedef void* HINSTANCE;

    #if !defined(WINAPI)

    #define WINAPI __stdcall

    #endif

    #if !defined(DLL_PROCESS_ATTACH)

    #define DLL_PROCESS_ATTACH 1

    #endif

    #if !defined(DLL_THREAD_ATTACH)

    #define DLL_THREAD_ATTACH  2

    #endif

    #if !defined(DLL_THREAD_DETACH)

    #define DLL_THREAD_DETACH  3

    #endif

    #if !defined(DLL_PROCESS_DETACH)

    #define DLL_PROCESS_DETACH 0

    #endif

    namespace Libmain {

    typedef void __fastcall (*TRegisterProc)(void);

    extern  void __fastcall RegisterModule(const System::AnsiString Name,

      TRegisterProc RegisterProc);

    }

    using namespace Libmain;

    namespace System {

    extern void __cdecl ProcessAttachTLS(void);

    extern void __cdecl ProcessDetachTLS(void);

    extern void __cdecl ThreadAttachTLS(void);

    extern void __cdecl ThreadDetachTLS(void);

    }

    using namespace System;

    //--------------------------------------------------------------------------

    // Prototype for each component's `Register routine. Followed

    // by instruction to have linker pull in the OBJ. module which

    // implements the Register routine.

    //

    //  Each component is expected to provide a routine with the

    //  following signature:

    //

    //       extern void __fastcall Register(void);

    //

    //  This routine must be in a namespace which matches the

    //  name of the component itself. Therefore, the routine is

    //  actually prototyped as:

    //

    //       namespace Componentname {

    //           extern void __fastcall Register(void);

    //       };

    //

    //  Note The namespace must be in all lowercase characters

    //        except for the first one. i.e. Namespacename.

    //--------------------------------------------------------------------------

    namespace Stdreg { extern void __fastcall Register(void); }

    namespace Dbreg { extern void __fastcall Register(void); }

    namespace Isp { extern void __fastcall Register(void); }

    namespace Sysreg { extern void __fastcall Register(void); }

    namespace Quickrep { extern void __fastcall Register(void); }

    namespace Ocxreg { extern void __fastcall Register(void); }

    namespace Olereg { extern void __fastcall Register(void); }

    namespace Ddereg { extern void __fastcall Register(void); }

    namespace Chartfx { extern void __fastcall Register(void); }

    namespace Vcfimprs { extern void __fastcall Register(void); }

    namespace Vcfrmla1 { extern void __fastcall Register(void); }

    namespace Vcspell { extern void __fastcall Register(void); }

    namespace Graphsvr { extern void __fastcall Register(void); }

    namespace Ibreg { extern void __fastcall Register(void); }

    namespace Win31reg { extern void __fastcall Register(void); }

    namespace Sampreg { extern void __fastcall Register(void); }

    namespace Unleash1 { extern void __fastcall Register(void); }

    // (Search Path for Components (.CPP, .PAS, .OBJ & .LIB)

    //  => g:\bcb\LIB;g:\bcb\LIB\OBJ;g:\srcc\punleash\utils

    //

    // Added to search paths: g:\bcb\LIB

    // Added to search paths: g:\bcb\LIB\OBJ

    // Added to search paths: g:\srcc\punleash\utils

    // Added to project: g:\bcb\bin\cmplib32.cpp FileType: SRC

    // Added to project: bcbmm.lib FileType: LIB

    #pragma resource   "StdReg.dcr"  // Link dcr of standard module "StdReg"

    // Added to project: g:\bcb\LIB\OBJ\DBReg.obj (From SearchPath) FileType: OBJ

    // Added to project: g:\bcb\LIB\OBJ\DBReg.dcr (From SearchPath) FileType: RES

    #pragma resource      "ISP.dcr"  // Link dcr of standard module "ISP"

    #pragma resource   "SysReg.dcr"  // Link dcr of standard module "SysReg"

    #pragma resource "Quickrep.dcr"  // Link dcr of standard module "Quickrep"

    #pragma resource   "OLEReg.dcr"  // Link dcr of standard module "OLEReg"

    #pragma resource   "DDEReg.dcr"  // Link dcr of standard module "DDEReg"

    #pragma resource  "ChartFX.dcr"  // Link dcr of standard module "ChartFX"

    #pragma resource "VCFImprs.dcr"  // Link dcr of standard module "VCFImprs"

    #pragma resource "VCFrmla1.dcr"  // Link dcr of standard module "VCFrmla1"

    #pragma resource  "VCSpell.dcr"  // Link dcr of standard module "VCSpell"

    #pragma resource "GraphSvr.dcr"  // Link dcr of standard module "GraphSvr"

    #pragma resource    "IBReg.dcr"  // Link dcr of standard module "IBReg"

    #pragma resource "Win31Reg.dcr"  // Link dcr of standard module "Win31Reg"

    #pragma resource  "SampReg.dcr"  // Link dcr of standard module "SampReg"

    // Added to project: g:\srcc\punleash\utils\Unleash1.cpp

    //  (From SearchPath) FileType: SRC

    //--------------------------------------------------------------------------

    // Routine which registers the various modules implementing components

    //--------------------------------------------------------------------------

    bool

    InitCmpLib()

    {

      RegisterModule("StdReg", Stdreg::Register);

      RegisterModule("DBReg", Dbreg::Register);

      RegisterModule("ISP", Isp::Register);

      RegisterModule("SysReg", Sysreg::Register);

      RegisterModule("Quickrep", Quickrep::Register);

      RegisterModule("OCXReg", Ocxreg::Register);

      RegisterModule("OLEReg", Olereg::Register);

      RegisterModule("DDEReg", Ddereg::Register);

      RegisterModule("ChartFX", Chartfx::Register);

      RegisterModule("VCFImprs", Vcfimprs::Register);

      RegisterModule("VCFrmla1", Vcfrmla1::Register);

      RegisterModule("VCSpell", Vcspell::Register);

      RegisterModule("GraphSvr", Graphsvr::Register);

      RegisterModule("IBReg", Ibreg::Register);

      RegisterModule("Win31Reg", Win31reg::Register);

      RegisterModule("SampReg", Sampreg::Register);

      RegisterModule("Unleash1", Unleash1::Register);

      return true;

    }

    //--------------------------------------------------------------------------

    // Library's entry point

    //--------------------------------------------------------------------------

    extern "C"

    int WINAPI

    DllEntryPoint(HINSTANCE /*hInstance*/, unsigned long reason, void*)

    {

      switch (reason) {

        case DLL_PROCESS_ATTACH:

           ProcessAttachTLS();

           InitCmpLib();

           break;

        case DLL_PROCESS_DETACH:

           ProcessDetachTLS();

           break;

        case DLL_THREAD_ATTACH:

           ThreadAttachTLS();

           break;

        case DLL_THREAD_DETACH:

           ThreadDetachTLS();

           break;

      }

      return 1;

    }
To understand the unit, start at the bottom, with the `DllEntryPoint` function. This routine is called when Windows first loads the DLL into memory and each time it is accessed by a BCB thread. `ProcessAttachTLS` and related routines are all part of the internal system code. This code is none of our business, so we can safely "pay no attention to the man behind the curtain." `InitCmpLib` calls the `Register` method for each of the modules used in the project. If you look at the bottom of the list, you will see where the `Register` method for `Unleash1` is listed. You might think that we don't have enough calls in this section to create the over 100 components found on the Component Palette. The issue here is that some of the `Register` methods register 20 or 30 different components at one shot. Here, for example, is the `Register` method from `StdReg.pas`:
    procedure Register;

    begin

      RegisterComponents(LoadStr(srStandard), [TMainMenu, TPopupMenu, TLabel,

        TEdit, TMemo, TButton, TCheckBox, TRadioButton, TListBox, TComboBox,

        TScrollBar, TGroupBox, TRadioGroup, TPanel]);

      RegisterComponents(LoadStr(srAdditional), [TBitBtn, TSpeedButton,

        TMaskEdit, TStringGrid, TDrawGrid, TImage, TShape, TBevel,

        TScrollBox]);

      RegisterComponents(LoadStr(srWin95), [TTabControl, TPageControl,

        TTreeView, TListView, TImageList, THeaderControl, TRichEdit,

        TStatusBar, TTrackBar, TProgressBar, TUpDown, THotKey]);

      RegisterClasses([TTabSheet]);

      RegisterNoIcon([TMenuItem]);

      RegisterComponentEditor(TMenu, TMenuEditor);

      RegisterComponentEditor(TImage, TGraphicEditor);

      RegisterComponentEditor(TPageControl, TPageControlEditor);

      RegisterComponentEditor(TTabSheet, TPageControlEditor);

      RegisterComponentEditor(TImageList, TImageListEditor);

      RegisterPropertyEditor(TypeInfo(string),

        TCustomMaskEdit, `EditMask', TMaskProperty);

      RegisterPropertyEditor(TypeInfo(string),

        TCustomMaskEdit, `Text', TMaskTextProperty);

      RegisterPropertyEditor(TypeInfo(TTabSheet), TPageControl, `ActivePage',

        TActivePageProperty);

      RegisterPropertyEditor(TypeInfo(TStatusPanels), nil, `',

        TStatusPanelsProperty);

      RegisterPropertyEditor(TypeInfo(THeaderSections), nil, `',

        THeaderSectionsProperty);

      RegisterPropertyEditor(TypeInfo(TListColumns), nil, `',

        TListColumnsProperty);

      RegisterPropertyEditor(TypeInfo(TListItems), nil, `',

        TListItemsProperty);

      RegisterPropertyEditor(TypeInfo(TTreeNodes), nil, `',

        TTreeNodesProperty);
    end;
Sorry about bringing Object Pascal code into the book again, but I think it helps to see what is happening behind the scenes in cases like this one. I will talk about `RegisterPropertyEditor` later in this chapter. Notice that the VCL calls `RegisterComponents` just as you do. When you recompile `Cmplib`, you are rebuilding part of BCB itself and simultaneously redefining the way the VCL works. The DCR files listed in `CmpLib32.cpp` are a series of standard Windows resources that contain bitmaps that are placed in the Component Palette. The bitmaps representing each component come from here. In the case of `TSmallEdit`, it descends from `TEdit`, and so it inherits `TEdit`'s bitmap, unless I create a DCR file for the component. I will create and discuss DCR files later in this chapter and in the next chapter. The key point to grasp about DCR files is that they are standard Windows resources containing a bitmap. You can build them with the Image Editor that ships with BCB. ## Extending the Unleash Unit In the second version of the `Unleash` unit, a new edit control is added, along with two labels and two panels. The additional edits and labels show how quickly you can build on an idea or object when you understand where you're headed. One of the panels shows how you can get rid of the annoying label that always shows up in the middle of a panel, and the other shows a preliminary take on how you can create a single component that contains other components. Specifically, it shows how to create a panel that comes already equipped with two radio buttons. The code for the new version of the `Unleash2.cpp` unit is shown in Listings 22.6 and 22.7, and its test bed appears in Listing 22.8. Run `TestBed` first, and make sure the new components are working correctly. If all is well, then add the new components to the Component Palette. If you have two units on your system, both of which contain instances of `TSmallEdit`, uninstalling the first instance before trying to install the new instance is probably best. For instance, you may have two files on your system, one called `Unleash1.cpp` and the second called `Unleash2.cpp`. Both might contain an instance of `TSmallEdit`. Under such circumstances, it's best to use Component | Install | Remove to remove the first version of `TSmallEdit` before replacing it with a second version. A second alternative is just to rename the second version of the component to `TSmallEditTwo`, which is what I do with the source found on the CD that accompanies this book. If you have only one version of `TSmallEdit`, and you just want to update it, you don't need to remove the first instance before installing the updated instance. #### Listing 22.6. The header for the second version of the Unleash2.cpp unit contains a class that comes equipped with two radio buttons.
    ///////////////////////////////////////

    // Unleash2.h

    // Simple components

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef Unleash2H

    #define Unleash2H

    //--------------------------------------------------------------------------

    #include <vcl\sysutils.hpp>

    #include <vcl\controls.hpp>

    #include <vcl\classes.hpp>

    #include <vcl\forms.hpp>

    #include <vcl\StdCtrls.hpp>

    //--------------------------------------------------------------------------

    class TSmallEditTwo : public TEdit

    {

    public:

      virtual __fastcall TSmallEditTwo(TComponent* Owner);

    };

    class TBigEdit : public TSmallEditTwo

    {

    public:

      virtual __fastcall TBigEdit(TComponent* Owner)

        :TSmallEditTwo(Owner) { Width = 250; Font->Size = 24; }

    };

    class TSmallLabel : public TLabel

    {

    public:

      virtual __fastcall TSmallLabel(TComponent* Owner);

    };

    class TBigLabel : public TSmallLabel

    {

    public:

      virtual __fastcall TBigLabel(TComponent *Owner)

        :TSmallLabel(Owner) { Font->Size = 24; }

    };

    class TEmptyPanel : public TPanel

    {

    protected:

      void __fastcall Loaded(void);

    public:

      virtual __fastcall TEmptyPanel(TComponent *Owner);

      virtual void __fastcall SetParent(TWinControl *AParent);

    };

    class TRadio2Panel : public TEmptyPanel

    {

    private:

      TRadioButton *FRadio1;

      TRadioButton *FRadio2;

    public:

      virtual __fastcall TRadio2Panel(TComponent *Owner);

      __property TRadioButton *Radio1={read=FRadio1};

      __property TRadioButton *Radio2={read=FRadio2};

    };

    //--------------------------------------------------------------------------
`#endif` #### Listing 22.7. The second version of the Unleash unit, called Unleash2, contains a class that comes equipped with two radio buttons.
    ///////////////////////////////////////

    // Unleash2.cpp

    // Simple components

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Unleash2.h"

    ///////////////////////////////////////

    // TSmallEditTwo //////////////////////

    ///////////////////////////////////////

    static inline TSmallEditTwo *ValidCtrCheck()

    {

      return new TSmallEditTwo(NULL);

    }

    __fastcall TSmallEditTwo::TSmallEditTwo(TComponent* Owner)

      : TEdit(Owner)

    {

      Color = clBlue;

      Font->Color = clYellow;

      Font->Name = "Times New Roman";

      Font->Size = 12;

      Font->Style = TFontStyles() << fsBold << fsItalic;

    }

    ///////////////////////////////////////

    // TSmallLabel ////////////////////////

    ///////////////////////////////////////

    __fastcall TSmallLabel::TSmallLabel(TComponent* Owner)

      : TLabel(Owner)

    {

      Color = clBlue;

      Font->Color = clYellow;

      Font->Name = "Times New Roman";

      Font->Size = 12;

      Font->Style = TFontStyles() << fsBold;

    }

    ///////////////////////////////////////

    // TEmptyPanel ////////////////////////

    ///////////////////////////////////////

    __fastcall TEmptyPanel::TEmptyPanel(TComponent *Owner)

       :TPanel(Owner)

    {

    }

    void __fastcall TEmptyPanel::Loaded(void)

    {

      TPanel::Loaded();

      Caption = "";

    }

    void __fastcall TEmptyPanel::SetParent(TWinControl *AParent)

    {

      TPanel::SetParent(AParent);

      Caption = "";

    }

    ///////////////////////////////////////

    // TRadio2Panel ///////////////////////

    ///////////////////////////////////////

    __fastcall TRadio2Panel::TRadio2Panel(TComponent *Owner)

      :TEmptyPanel(Owner)

    {

      Width = 175;

      Height = 60;

      FRadio1 = new TRadioButton(this);

      FRadio1->Parent = this;

      FRadio1->Caption = "Radio1";

      FRadio1->Left = 20;

      FRadio1->Top = 10;

      FRadio1->Show();

      FRadio2 = new TRadioButton(this);

      FRadio2->Parent = this;

      FRadio2->Caption = "Radio2";

      FRadio2->Left = 20;

      FRadio2->Top = 32;

      FRadio2->Show();

    }

    namespace Unleash2

    {

      void __fastcall Register()

      {

        TComponentClass classes[6]={__classid(TSmallEditTwo),

          __classid(TBigEdit), __classid(TSmallLabel), __classid(TBigLabel),

          __classid(TEmptyPanel), __classid(TRadio2Panel)};

        RegisterComponents("Unleash", classes, 5);

      }

    }
`//--------------------------------------------------------------------------` #### Listing 22.8. The test bed for the Unleash2 unit.
    ///////////////////////////////////////

    // Main.cpp

    // Simple components: TestUnleash2

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #include "Unleash2.h"

    #pragma link "Unleash1"

    #pragma link "Unleash2"

    #pragma resource "*.dfm"

    TForm1 *Form1;

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

       TComponentClass classes[1]={__classid(TRadioButton)};

       RegisterClasses(classes, 0);

    }

    void __fastcall TForm1::TestComponentsBtnClick(TObject *Sender)

    {

      TestComponentsBtn->Enabled = False;

      TBigEdit *BigEdit = new TBigEdit(this);

      BigEdit->Parent = this;

      BigEdit->Show();

      TEmptyPanel *E = new TEmptyPanel(this);

      E->Parent = this;

      E->Top = BigEdit->Height + 5;

      E->Show();

      TRadio2Panel *P = new TRadio2Panel(this);

      P->Parent = this;

      P->Top = E->Top + E->Height + 5;

      P->Radio1->Caption = "As you from crimes would pardon'd be ";

      P->Radio1->Width = Canvas->TextWidth(P->Radio1->Caption) + 20;

      P->Radio2->Caption = "Let your indulgence set me free ";

      P->Radio2->Width = Canvas->TextWidth(P->Radio2->Caption) + 20;

      P->Width = max(P->Radio1->Width, P->Radio2->Width) + P->Radio1->Left + 20;

      P->Show();
`}` You can find this program in the directory called `TestUnleash2` on the CD that accompanies this book. Figure 22.4 shows the program in action. Figure 22.4.The TestUnleash2 program demonstrates how to test a series of components before placing them on the Component Palette. After you've created a component that does something you like, you can easily create children of it. Class `TBigEdit` descends from `TSmallEditTwo`:
    class TBigEdit : public TSmallEditTwo

    {

    public:

      virtual __fastcall TBigEdit(TComponent* Owner)

        :TSmallEditTwo(Owner) { Width = 250; Font->Size = 24; }

    };
It inherits its font nearly unchanged from `TSmallEditTwo`, except that it sets `Font->Size` to `24`, a hefty figure that helps the control live up to its name. This elegant syntax is a good example of how OOP can save you time and trouble while still allowing you to write clear code. The label controls shown in this code work in exactly the same way the edit controls do, except that they descend from `TLabel` rather than from `TEdit`. The `TEmptyPanel` component rectifies one of the petty issues that sometimes annoys me: Every time you put down a panel, it gets a caption. Most of the time, the first thing you do is delete the caption so that you can place other controls on it without creating a mess! At first, it would appear that you can change the caption of `TPanel` by overriding its constructor. All you would need to do is set the `Caption` property to an empty string:
    class TEmptyPanel : public TPanel

    {

    public:

      virtual __fastcall TEmptyPanel(TComponent *Owner)

       :TPanel(Owner) { Caption = ""; }

    };
This code should work correctly, but in the first version of C++Builder the caption does not get changed. There are two interesting workarounds for this problem that illustrate interesting facts about the VCL. The first workaround is to override the `Loaded` method:
    void __fastcall TEmptyPanel::Loaded(void)

    {

      TPanel::Loaded();

      Caption = "";

    }
The code shown in this overridden `Loaded` method will change the caption of the component at runtime, but not at design time. The `Loaded` method is called after a component has been loaded from a stream but before it is shown to the user. The method exists so that you can set the value of properties that rely on the value of other properties. A second technique involves overriding the `SetParent` method:
    void __fastcall TEmptyPanel::SetParent(TWinControl *AParent)

    {

      TPanel::SetParent(AParent);

      Caption = "";

    }
This will fix the problem at both design time and at runtime. In this example, I am not using `SetParent` for its intended purpose. To understand the method's real purpose, you need to remember that a component is always shown on the surface of its parent. The primary purpose of the `SetParent` method is to give you a chance to change the parent of a component just as it is being made visible to the user. This allows you to store a component in a DFM file with one parent, and to change that parent at runtime. It's unlikely you will ever need to change the parent of a component, but there are occasions when it is useful, and so the VCL gives you that ability. It is obviously useful to know that you can override the `SetParent` and `Loaded` methods in order to change properties at two different stages in the process of allocating memory for a component. You should note, however, that in this case it is a bit strange that you have to do so, because merely overriding the constructor should have turned the trick. The last new component in `Unleash2.cpp` enables you to drop down a panel that comes equipped with two radio buttons. This way, you can make a single control out of a set of components that are often combined. You could create other controls that contain three, four, or more radio buttons. Or you could even create a panel that would populate itself with a specific number of radio buttons. The declaration for this new radio button is fairly simple:
    class TRadio2Panel : public TEmptyPanel

    {

    private:

      TRadioButton *FRadio1;

      TRadioButton *FRadio2;

    public:

      virtual __fastcall TRadio2Panel(TComponent *Owner);

      __property TRadioButton *Radio1={read=FRadio1 };

      __property TRadioButton *Radio2={read=FRadio2 };
    };
The actual radio buttons themselves are declared as private data, and access to them is given by the `Radio1` and `Radio2` properties. Write access to the radio button properties is not needed because you can modify without it. The following statement performs one read of `RP->Radio1` and one write to the `Caption` property of that radio button:
    P->Radio1->Caption = "Control one";
You don't want write access to the properties either because that would allow the user to assign them garbage (or `NULL`). The constructor for the `Radio2Panel` begins by setting the width and height of the panel:
    __fastcall TRadio2Panel::TRadio2Panel(TComponent *Owner)

      :TEmptyPanel(Owner)

    {

      Width = 175;

      Height = 60;

      FRadio1 = new TRadioButton(this);

      FRadio1->Parent = this;

      FRadio1->Caption = "Radio1";

      FRadio1->Left = 20;

      FRadio1->Top = 10;

      FRadio1->Show();

      FRadio2 = new TRadioButton(this);

      FRadio2->Parent = this;

      FRadio2->Caption = "Radio2";

      FRadio2->Left = 20;

      FRadio2->Top = 32;

      FRadio2->Show();

    }
The next step is to create the first radio button. Notice that the code passes `this` as the owner and sets the parent to the panel itself. The rest of the code in the `Create` method is too trivial to merit comment. Under some circumstances, you may need to register `TRadioButton:`
    TComponentClass classes[1]={__classid(TRadioButton)};

    RegisterClasses(classes, 0);
This event would normally occur when you drop a component on a form. However, in this case a `TRadioButton` is not necessarily ever dropped explicitly on a form. As a result, the safe thing to do is register the component, possibly in the constructor for the class. When you're ready to test the `TRadio2Panel` object, you can write the following code in the test-bed program to take it through its paces:
    TRadio2Panel *P = new TRadio2Panel(this);

    P->Parent = this;

    P->Top = E->Top + E->Height + 5;

    P->Radio1->Caption = "As you from crimes would pardon'd be ";

    P->Radio1->Width = Canvas->TextWidth(P->Radio1->Caption) + 20;

    P->Radio2->Caption = "Let your indulgence set me free ";

    P->Radio2->Width = Canvas->TextWidth(P->Radio2->Caption) + 20;

    P->Width = max(P->Radio1->Width, P->Radio2->Width) + P->Radio1->Left + 20;

    P->Show();
Note that each radio button that belongs to the panel acts exactly as you would expect a normal radio button to act, except you have to qualify it differently before you access it. I use the `TextWidth` property of `Canvas` to discover the width needed for the string, and then add 20 to take into account the button itself. NOTE: If you want, you can surface `Radio1` and `Radio2` in the Object Inspecter as published properties of `TRadio2Panel`. However, when you first do so, they will have no property editors available because BCB has no built-in property editors for `TRadioButtons`. To build your own, you can refer to the `DsgnIntf.pas` unit that ships with BCB, as well as the upcoming discussion of the `Clock` component and the Tools API. The following code is used to register the objects in the `Unleash2` unit so they can be placed on the Component Palette:
    namespace Unleash2

    {

      void __fastcall Register()

      {

        TComponentClass classes[6]={__classid(TSmallEditTwo),

          __classid(TBigEdit), __classid(TSmallLabel), __classid(TBigLabel),

          __classid(TEmptyPanel), __classid(TRadio2Panel)};

        RegisterComponents("Unleash", classes, 5);

      }

    }
Notice how I register multiple components in this example at once by creating an array of type `TComponentClass`. After registering the objects, you can place them on the Component Palette and use them in a program, as shown in Figures 22.5 and 22.6. Figure 22.5.The main form of a VCL application that uses some of the components from the `Unleash2` unit. Figure 22.6.The new components on the Component Palette, along with some other controls created in later chapters. NOTE: To create custom bitmaps for components shown on the Component Palette, you need to create a standard resource with the extension `.dcr`. The Image Editor component that ships with BCB is designed to handle this chore. I ship a few DCR files in the `Utils` directory on the CD that accompanies this book. You can study them to see how to proceed, or you can find the DCR files for other components that ship with BCB. To look at a DCR file, open it with the Image Editor. The DCR file should have the name of the unit that contains the controls, and each small 24x24 bitmap that you create in the file should be named after the component to which it belongs. For example, the DCR file for this unit would be called `Unleash2.dcr`, and the bitmaps inside it would have names like `TBIGEDIT` and `TEMPTYPANEL`. I will discuss this subject in more depth later in this chapter. Before closing this section, I'd like to add some notes about how BCB handles streaming chores. The good news is that most of the time you don't have to concern yourself with streaming at all. BCB handles most streaming chores automatically. In particular, it will automatically stream `published` properties that are simple types. Only under limited circumstances must you explicitly stream the fields of your object. If a property type is a `TComponent` or descendant, the streaming system assumes it must create an instance of that type when reading it in. If a property type is `TPersistent` but not `TComponent`, the streaming system assumes it is supposed to use the existing instance available through the property and read values into that instance's properties. The Object Inspector knows to expand the properties of `TPersistent` but not `TComponent` descendants. This expansion is not done for `TComponent` descendants because they are likely to have a lot more properties, which would make navigating the Object Inspector difficult. ## Building Components from Scratch In the previous examples, you created descendants of existing components. Now you're ready to see how to create entirely new components. The main idea to grasp here is that you can make a new component descend from three abstract objects. The term "abstract" can have a specific technical meaning, but here I'm using it to refer to any object that exists only so that you can create descendants of it. In short, the following three objects have built-in functionality that all components need to access, but you would never want to instantiate an instance of any of them: `TWinControl` and `TCustomControl` are base classes that can be used to produce a Windows control that can receive input focus and that has a standard Windows handle that can be passed to API calls. `TWinControl` descendants exist inside their own window. `TEdit`, `TListBox`, `TTabbedNotebook`, `TNotebook`, and `TPanel` are all examples of this type of control. Most components of this type actually descend from `TCustomControl`, which is in turn a descendant of `TWinControl`. The distinction between the two classes is that `TCustomControl` has a `Paint` method, and `TWinControl` does not. If you want to draw the display of your new component, you should make it inherit from `TCustomControl`. If the object already knows how to draw itself, inherit from `TWinControl`. `TGraphicControl` is for components that don't need to receive input focus, don't need to contain other components, and don't need a handle. These controls draw themselves directly on their parent's surface, thereby saving Windows resources. Not having a window handle eliminates a lot of Windows management overhead, and that translates into faster display updates. In short, `TGraphicControl`s exist inside their parent's window. They use their parent's handle and their parent's device context. They still have `Handle` and `Canvas` fields that you can access, but they actually belong to their parent. `TLabel` and `TShape` objects are examples of this type of component. The drawback with this system is that the component can never get the focus! `TComponent` enables you to create nonvisual components. If you want to make a tool such as the `TTable`, `TQuery`, `TOpenDialog`, or `TTimer` devices, this is the place to start. You can place these components on the Component Palette, but they perform some internal function that you access through code instead of appearing to the user at runtime. A tool such as `TOpenDialog` can pop up a dialog, but the component itself remains invisible. Create a `TWinControl` or `TCustomControl` descendant whenever the user needs to interact directly with a visible control. If the user doesn't need to interact with a visible component, create a `TGraphicControl` descendant. To get a handle on the issues involved here, you should place a `TShape` or `TLabel` control on a form and run the program. Clicking or attempting to type on these controls produces no noticeable result. These components don't ever receive the focus. Now place a `TEdit` control on the form. It responds to mouse clicks, gets the focus, and you can type in it. `TEdit` controls are descendants of `TWinControl`, and `TShape` is a descendant of `TGraphicControl`. NOTE: I should add one caveat to the rules about `TGraphicControl` explained previously. In one limited sense, the user can interact with a `TGraphicControl`. For example, these controls do receive mouse messages, and you can set the mouse cursor when the mouse flies over them. They just can't receive keyboard input focus. If an object can't receive focus, it usually seems inert to the user. If you're having trouble deciding whether you want to make descendants from `TWinControl` or `TCustomControl`, you should always go with `TCustomControl`. It has a real `Paint` method and some other functionality that is useful when creating a component of your own. If you want to wrap an existing Windows control inside a VCL object, you should start with `TWinControl`. Most BCB components that follow this path begin by creating intermediate custom objects, so the hierarchy of `TEdit` looks like this:
    TWinControl

    TCustomEdit

    TEdit
The hierarchy of `TListBox` looks like this:
    TWinControl

    TCustomListBox

    TListBox
Of course, BCB wraps all the major Windows controls for you, so you won't need to perform this operation unless you're working with a specialized third-party control of some sort. Following are the declarations for `TGraphicControl` and `TCustomControl`, as they appear in `Controls.hpp`:
    class __declspec(delphiclass) TGraphicControl;

    class __declspec(pascalimplementation) TGraphicControl : public TControl

    {

      typedef TControl inherited;

    private:

      Graphics::TCanvas* FCanvas;

      MESSAGE void __fastcall WMPaint(Messages::TWMPaint &Message);

    protected:

      virtual void __fastcall Paint(void);

      __property Graphics::TCanvas* Canvas = {read=FCanvas, nodefault};

    public:

      __fastcall virtual TGraphicControl(Classes::TComponent* AOwner);

      __fastcall virtual ~TGraphicControl(void);

    };

    class __declspec(delphiclass) TCustomControl;

    class __declspec(pascalimplementation) TCustomControl : public TWinControl

    {

      typedef TWinControl inherited;

    private:

      Graphics::TCanvas* FCanvas;

      MESSAGE void __fastcall WMPaint(Messages::TWMPaint &Message);

    protected:

      virtual void __fastcall Paint(void);

      virtual void __fastcall PaintWindow(HDC DC);

      __property Graphics::TCanvas* Canvas = {read=FCanvas, nodefault};

    public:

      __fastcall virtual TCustomControl(Classes::TComponent* AOwner);

      __fastcall virtual ~TCustomControl(void);

    public:

      /* CreateParented */ __fastcall TCustomControl(HWND ParentWindow) :

       Controls::TWinControl(ParentWindow) { }

    };
You can see that these objects are fairly simple. If you go back one step further in the hierarchy to `TControl` or `TWinControl`, you see huge objects. For example, the declaration for class `TWinControl` is nearly 200 lines long (not the implementation, mind you, just the type declaration). I'm showing you this source code because component builders should work directly with the source rather than use the online help or the docs. For simple jobs, you can easily create your own components without the source. However, if you have a big project, you have to get the source code if it did not ship with your product. The INT files that ship with all versions of BCB are very helpful, but you'll find no replacement for the actual source. The source is available with the client/server version of BCB and also as an up-sell from Borland. ## The Clock Component You now know enough to build a component from the ground up. The `CLOCK` unit, as shown in Figure 22.7, is a simple clock that you can pop onto a form, and activate and deactivate at will. In particular, you can start the clock running and then tell it to stop by changing the value of a Boolean property called `Running`. Figure 22.7.The `CLOCK` unit as it appears on its own test bed, before being placed on the Component Palette. When you're constructing class `TClock`, the first thing you need to decide is whether the clock is going to descend from `TWinControl` or `TGraphicControl`. If you've built a clock in Windows before, you know that one of the best ways to drive it is with a Windows timer. Timers require the presence of a handle so that they can be stopped and started; furthermore, they send their `WM_TIMER` messages to the window that owns them. Because a `TGraphicControl` descendant isn't a real window, it will not automatically get the messages. As a result, `TGraphicControl` is not an ideal choice for this type of object. Of course, the objections to using `TGraphicControl` raised in the preceding paragraph aren't insurmountable. If you really want to, you can still make your clock work this way. However, there's no point in expending effort that isn't strictly necessary, so I have opted for the simplest design possible and have made the class a descendant of `TCustomControl`. I chose `TCustomControl` rather than `TWinControl` because I needed a `Paint` method in which I could draw the clock. (See Figure 22.8.) Figure 22.8.The `TClock` and `TColorClock` controls shown on a form. The code, shown in Listings 22.9 through 22.11, also contains a special property editor, as well as a simple component editor. As you will see, neither of these tools is inherently difficult to build. NOTE: Before installing the clock components, you might need to run the BuildObjs program found in the `Utils` directory. See the readme file on the CD that accompanies this chapter for additional information. #### Listing 22.9. The header for the Clock component contains declarations for the clock and its property and component editors.
    ///////////////////////////////////////

    // Clock1.h

    // Clock Component

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef Clock1H

    #define Clock1H

    #include <vcl\sysutils.hpp>

    #include <vcl\controls.hpp>

    #include <vcl\classes.hpp>

    #include <vcl\forms.hpp>

    #include <vcl\dsgnintf.hpp>

    #include <vcl\messages.hpp>

    #include <vcl\extctrls.hpp>

    class TClockAttributes: public TPersistent

    {

    private:

      TColor FColor;

      TShapeType FShape;

      TComponent *FOwner;

      void __fastcall SetColor(TColor AColor);

      void __fastcall SetShape(TShapeType AShape);

    public:

      virtual __fastcall TClockAttributes() : TPersistent() { }

      virtual __fastcall TClockAttributes(TComponent *AOwner)

        : TPersistent() { FOwner = AOwner; }

    __published:

      __property TColor Color={read=FColor, write=SetColor};

      __property TShapeType  Shape={read=FShape, write=SetShape};

    };

    ////////////////////////////////////////

    // TMyClock ////////////////////////////

    ////////////////////////////////////////

    class TMyClock: public TCustomControl

    {

    private:

      int FTimer;

      Boolean FRunning;

      TClockAttributes *FClockAttributes;

      void __fastcall SetRunning(Boolean ARun);

    protected:

      virtual __fastcall ~TMyClock() { delete FClockAttributes; }

      virtual void __fastcall Paint(void);

      void __fastcall WMTimer(TMessage &Message);

      void __fastcall WMDestroy(TMessage &Message);

    public:

      virtual __fastcall TMyClock(TComponent* Owner);

    __published:

      __property Boolean Running={read=FRunning, write=SetRunning};

      __property Align;

      __property TClockAttributes *ClockAttributes=

        {read=FClockAttributes, write=FClockAttributes };

      __property Font;

    BEGIN_MESSAGE_MAP

      MESSAGE_HANDLER(WM_TIMER, TMessage, WMTimer);

      MESSAGE_HANDLER(WM_DESTROY, TMessage, WMDestroy);

    END_MESSAGE_MAP(TCustomControl);

    };

    ////////////////////////////////////////

    // TColorClock /////////////////////////

    ////////////////////////////////////////

    class TColorClock: public TMyClock

    {

    private:

      TColor FFaceColor;

      void __fastcall SetColor(TColor Color);

    protected:

      void virtual __fastcall Paint(void);

    public:

      virtual __fastcall TColorClock(TComponent *Owner)

        :TMyClock(Owner) { FFaceColor = clGreen; }

    __published:

      __property TColor FaceColor={read=FFaceColor, write=SetColor, nodefault};

    };

    ////////////////////////////////////////

    // TClockEditor ////////////////////////

    ////////////////////////////////////////

    class TClockEditor: public TComponentEditor

    {

    protected:

      virtual __fastcall void Edit(void);

    public:

      virtual __fastcall TClockEditor(TComponent *AOwner, TFormDesigner *Designer)

        : TComponentEditor(AOwner, Designer) {}

    };

    ////////////////////////////////////////

    // TColorNameProperty /////////////////

    ////////////////////////////////////////

    class TColorNameProperty: public TClassProperty

    {

    public:

      TColorNameProperty(): TClassProperty() {}

      TPropertyAttributes __fastcall GetAttributes(void);

    };
`#endif` #### Listing 22.10. The code for the Clock component should be kept in the Utils subdirectory where you store CodeBox and other utility units.
    ///////////////////////////////////////

    // Clock1.cpp

    // Clock Component

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Clock1.h"

    ///////////////////////////////////////

    // ValidControlCheck

    ///////////////////////////////////////

    static inline TMyClock *ValidCtrCheck()

    {

      return new TMyClock(NULL);

    }

    ///////////////////////////////////////

    // TColorsProperty

    ///////////////////////////////////////

    void __fastcall TClockAttributes::SetColor(TColor AColor)

    {

      FColor = AColor;

      ((TControl *)(FOwner))->Invalidate();

    };

    void __fastcall TClockAttributes::SetShape(TShapeType AShape)

    {

      FShape = AShape;

      ((TControl *)(FOwner))->Invalidate();

    };

    ///////////////////////////////////////

    // Constructor

    ///////////////////////////////////////

    __fastcall TMyClock::TMyClock(TComponent* Owner)

      : TCustomControl(Owner)

    {

      Width = 100;

      Height = 100;

      FTimer = 1;

      FClockAttributes = new TClockAttributes(this);

      FClockAttributes->Color = clBtnFace;

      FClockAttributes->Shape = stEllipse;

    }

    ///////////////////////////////////////

    // SetRunning

    ///////////////////////////////////////

    void __fastcall TMyClock::SetRunning(Boolean Run)

    {

      if (Run)

      {

        SetTimer(Handle, FTimer, 50, NULL);

        FRunning = True;

      }

      else

      {

        KillTimer(Handle, FTimer);

        FRunning = False;

      }

    }

    ///////////////////////////////////////

    // Paint

    ///////////////////////////////////////

    void __fastcall TMyClock::Paint(void)

    {

      Color = ClockAttributes->Color;

      switch (ClockAttributes->Shape)

      {

        int X;

        case stEllipse: Canvas->Ellipse(0, 0, Width, Height); break;

        case stRectangle: Canvas->Rectangle(0, 0, Width, Height); break;

        case stRoundRect:

          Canvas->RoundRect(0, 0, Width, Height, Width - 100, Height);

          break;

        case stSquare:

        {

          if (Width < Height)

          {

            X = Width / 2;

            Canvas->Rectangle(Width - X, 0, Width + X, Width);

          }

          else

          {

            X = Height / 2;

            Canvas->Rectangle((Width / 2) - X, 0, (Width / 2) + X, Height);

          }

          break;

        }

        case stCircle:

        {

          if (Width < Height)

          {

            X = Width / 2;

            Canvas->Ellipse(Width - X, 0, Width + X, Width);

          }

          else

          {

            X = Height / 2;

            Canvas->Ellipse((Width / 2) - X, 0, (Width / 2) + X, Height);

          }

          break;

        }

      }

    }

    ///////////////////////////////////////

    // WM_TIMER

    ///////////////////////////////////////

    void __fastcall TMyClock::WMTimer(TMessage &Message)

    {

      AnsiString S = TimeToStr(Time());

      Canvas->Font = Font;

      Canvas->TextOut(Width / 2 - Canvas->TextWidth(S) / 2,

                      Height / 2 - Canvas->TextHeight(S) / 2, S);

    }

    ///////////////////////////////////////

    // WM_DESTROY

    ///////////////////////////////////////

    void __fastcall TMyClock::WMDestroy(TMessage &Message)

    {

      KillTimer(Handle, FTimer);

      FTimer = 0;

      TCustomControl::Dispatch(&Message);

    }

    //------------------------------------

    //-- TColorClock --------------------

    //------------------------------------

    ///////////////////////////////////////

    // WM_DESTROY

    ///////////////////////////////////////

    void __fastcall TColorClock::Paint()

    {

      Canvas->Brush->Color = FFaceColor;

      TMyClock::Paint();

    }

    ///////////////////////////////////////

    // SetColor

    ///////////////////////////////////////

    void __fastcall TColorClock::SetColor(TColor Color)

    {

      FFaceColor = Color;

      InvalidateRect(Handle, NULL, True);

    }

    //------------------------------------

    //-- TClockEditor --------------------

    //------------------------------------

    void __fastcall TClockEditor::Edit(void)

    {

      TColorDialog *C = new TColorDialog(Application);

      if (C->Execute())

        ((TColorClock *)(Component))->FaceColor = C->Color;

    }

    //------------------------------------

    //-- TColorNameProperty --------------

    //------------------------------------

    TPropertyAttributes __fastcall TColorNameProperty::GetAttributes(void)

    {

      return TPropertyAttributes() << paSubProperties;

    }

    ///////////////////////////////////////

    // Register

    ///////////////////////////////////////

    namespace Clock1

    {

      void __fastcall Register()

      {

        TComponentClass classes[2] = {__classid(TMyClock), __classid(TColorClock)};

        RegisterComponents("Unleash", classes, 1);

        RegisterComponentEditor(__classid(TColorClock), __classid(TClockEditor));

        RegisterPropertyEditor(__typeinfo(TClockAttributes),

          __classid(TMyClock), "Colors", __classid(TColorNameProperty));

      }
} #### Listing 22.11. The test bed for the clock component is stored in the TestClock subdirectory.
    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #include "Clock1.h"

    #pragma link "Clock1"

    #pragma link "sampreg"

    #pragma link "ClockProperty1"

    #pragma resource "*.dfm"

    TForm1 *Form1;

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

    }

    void __fastcall TForm1::Create1Click(TObject *Sender)

    {

      MyClock = new TColorClock(this);

      MyClock->Parent = this;

      MyClock->Show();

    }

    void __fastcall TForm1::ToggleRunning1Click(TObject *Sender)

    {

      MyClock->Running = !MyClock->Running;

    }

    void __fastcall TForm1::SetColor1Click(TObject *Sender)

    {

      if (ColorDialog1->Execute())

      {

        MyClock->FaceColor = ColorDialog1->Color;

      }
} To run this program, you should first select the menu item that creates the clock and makes it visible on the form. The next logical step is to start the clock running; then, if you'd like, you can also change its color. You get an `Access Violation` if you click the latter two buttons before selecting the first. The problem is that calling a method or property of the `TClock` object before the object itself has been created is an error. To prevent this error from happening, you could enable and disable the second two buttons. The code for the clock components uses inheritance, virtual methods, and properties. `TClock` has two pieces of private data:
    int FTimer;

    Boolean FRunning;
One is an identifier for the timer, and the other is a Boolean value that specifies whether the clock is running. Windows timers are managed by two Windows API calls. When you want to start a timer, use `SetTimer`; when you want to stop a timer, use `KillTimer`. `SetTimer` takes four parameters: `Handle` is the `HWND` of your current window. `IDEvent` is an integer identifier that uniquely identifies the timer inside the window that created it. You can make up this value off the top of your head, although I generally set the `IDTimer` for the first timer in a window to `1`, the second timer to `2`, and so on. Because you're going to have only one timer in each instance of a `TClock` window, you can set its `IDEvent` to `1`. `Elapse` is the length of time between calls to the timer, measured in milliseconds. `TimerFunc` is a callback function that is not used in this program. One of the developers' big goals was to create a Windows product that didn't need to use callbacks, and I see no reason to open that can of worms now if I can avoid it. (If you want to create a callback in BCB, you will be able to do so, but it's usually not necessary.) A typical call to `SetTimer` looks like this:
    SetTimer(Handle, FTimer, 50, NULL);
`50` specifies that the timer is called once every 50 milliseconds. `SetTimer` returns zero if the call fails. This is a real possibility, so programs that include error checking should inspect this value and put up a `MessageBox` if the call fails. `KillTimer` takes two parameters, the first being the handle of your window and the second being the unique identifier associated with that timer:
    KillTimer(Handle, FTimer);
When you're not using the callback function, timer events are sent to your window by way of messages:
    void __fastcall WMTimer(TMessage &Message);

    BEGIN_MESSAGE_MAP

      MESSAGE_HANDLER(WM_TIMER, TMessage, WMTimer);

      MESSAGE_HANDLER(WM_DESTROY, TMessage, WMDestroy);

    END_MESSAGE_MAP(TCustomControl);
This classic dynamic method is of the kind covered in Chapter 4, "Events." The response to this event is a simple procedure that calls `TextOut` and gets the time from the VCL:
    void __fastcall TMyClock::WMTimer(TMessage &Message)

    {

      AnsiString S = TimeToStr(Time());

      Canvas->TextOut(Width / 2 - Canvas->TextWidth(S) / 2,

                      Height / 2 - Canvas->TextHeight(S) / 2, S);
    }
The calls to `SetTimer` and `KillTimer` are managed primarily through a property called `Running`:
    __property Boolean Running={read=FRunning, write=SetRunning};
The write mechanism, a procedure called `SetRunning`, is a fairly straightforward tool:
    void __fastcall TMyClock::SetRunning(Boolean Run)

    {

      if (Run)

      {

        SetTimer(Handle, FTimer, 50, NULL);

        FRunning = True;

      }

      else

      {

        KillTimer(Handle, FTimer);

        FRunning = False;

      }

    }
If the user sets the `Running` property to `True`, this procedure is executed and a call is made to the `SetTimer` function. If the user sets `Running` to `False`, `KillTimer` is called and the clock immediately stops functioning. The final issue involving the timer concerns the case in which the user closes a form while the clock is still running. In such a case, you must be sure to call `KillTimer` before the application exits. If you don't make the call, the timer keeps running even after the application closes. Having the timer running wastes system resources and also uses up one of the dozen or so timers that are available to the system at any one time. The logical place to call `KillTimer` is the `Destroy` method for the `TClock` object. Unfortunately, the window associated with the clock has already been destroyed by the time this call is made, so no valid handle is available for use when you call `KillTimer`. As a result, you need to respond to `WM_DESTROY` messages to be sure the timer is killed before the `TClock` window is closed:
    void __fastcall TMyClock::WMDestroy(TMessage &Message)

    {

      KillTimer(Handle, FTimer);

      FTimer = 0;

      TCustomControl::Dispatch(&Message);

    }
Before leaving this description of the `TClock` object, I should briefly mention the `Paint` method. Here is the simplest possible version of the paint procedure for the program:
    void __fastcall TMyClock::Paint(void)

    {

      Canvas->Ellipse(0, 0, 100, 100);

    }
This procedure is called whenever the circle defining the circumference of the clock needs to be repainted. You never have to check for this circumstance, and you never have to call `Paint` directly. Windows keeps an eye on the `TClock` window, and if the window needs to be painted, Windows sends you a `WM_PAINT` message. Logic buried deep in the VCL converts the `WM_PAINT` message into a call to `Paint`, the same way `TClock` translates `WM_TIMER` messages into calls to `TCanvas->TextOut`. The actual paint procedure used in the program looks like this:
    ///////////////////////////////////////

    // Paint

    ///////////////////////////////////////

    void __fastcall TMyClock::Paint(void)

    {

      Color = ClockAttributes->Color;

      switch (ClockAttributes->Shape)

      {

        int X;

        case stEllipse: Canvas->Ellipse(0, 0, Width, Height); break;

        case stRectangle: Canvas->Rectangle(0, 0, Width, Height); break;

        case stRoundRect:

          Canvas->RoundRect(0, 0, Width, Height, Width - 100, Height);

          break;

        case stSquare:

        {

          if (Width < Height)

          {

            X = Width / 2;

            Canvas->Rectangle(Width - X, 0, Width + X, Width);

          }

          else

          {

            X = Height / 2;

            Canvas->Rectangle((Width / 2) - X, 0, (Width / 2) + X, Height);

          }

          break;

        }

        case stCircle:

        {

          if (Width < Height)

          {

            X = Width / 2;

            Canvas->Ellipse(Width - X, 0, Width + X, Width);

          }

          else

          {

            X = Height / 2;

            Canvas->Ellipse((Width / 2) - X, 0, (Width / 2) + X, Height);

          }

          break;

        }

      }
    }
This version of the `Paint` procedure takes into account the `ClockAttributes` property of `TMyClock`. `TClockAttributes` looks like this:
    class TClockAttributes: public TPersistent

    {

    private:

      TColor FColor;

      TShapeType FShape;

      TComponent *FOwner;

      void __fastcall SetColor(TColor AColor);

      void __fastcall SetShape(TShapeType AShape);

    public:

      virtual __fastcall TClockAttributes() : TPersistent() { }

      virtual __fastcall TClockAttributes(TComponent *AOwner)

        : TPersistent() { FOwner = AOwner; }

    __published:

      __property TColor Color={read=FColor, write=SetColor};

      __property TShapeType  Shape={read=FShape, write=SetShape};

    };
As you can see, it provides a shape and a background color for the clock. The most important aspect of the `TClockAttributes` type is the property editors I create for it. These editors let you access both properties of this class through a series of special techniques, described later in this chapter. For now, you might want to merely click the `ClockAttributes` property, open up its two fields, and see how it works. The `TColorClock` component, which is a descendant of `TClock`, adds color to the face of the control. I made `TColorClock` a separate object, instead of just adding a face color to `TClock`, for two different reasons (both of which are related to design): You might want to create a descendant of `TClock` that doesn't have color or one that implements color differently than `TColorClock` does. By creating two objects, one called `TClock` and the other called `TColorClock`, I enable programmers to have the greatest amount of freedom when creating descendants. This principle has only minimal weight in a simple object such as `TClock`, but it can become extremely important when you're developing large and complex hierarchies. In short, be careful of building too much functionality into one object! `TClock` and `TColorClock` also provide another example of inheritance and vividly demonstrate how this technology can be utilized to your advantage. `TColorClock` declares a private data store called `FColor` that is of type `TColor`. Users can set the `FColor` variable by manipulating the `Color` property:
    __property TColor Color={read=FColor, write=SetColor};
`SetColor` is a simple procedure that sets the value of `FColor` and calls the Windows API call `InvalidateRect`:
    void SetColor(TColor Color)

        { FColor = Color; InvalidateRect(Handle, NULL, True); }
Just to be sure this information makes sense to you, taking a paragraph or two to provide a refresher course on `InvalidateRect` is worthwhile. Here's the declaration for the routine:
    BOOL InvalidateRect(

        HWND  hWnd,            // handle of window with changed update region

        CONST RECT  * lpRect,  // address of rectangle coordinates

        BOOL  bErase           // erase-background flag

       );
`InvalidateRect` forces the window specified in the first parameter to redraw itself completely if the third parameter is set to `True`. If the third parameter is set to `False`, only the portions of the window that you specifically repaint are changed. The middle parameter is a pointer to the `RECT` structure that can be used to define the area that you want to redraw. Compare this function with the native BCB function called `Invalidate`. Calls to `InvalidateRect` naturally force calls to the `TColorClick->Paint` method:
    void __fastcall TColorClock::Paint()

    {

      Canvas->Brush->Color = FColor;

      TMyClock::Paint();

    }
`Paint` sets the brush associated with the window's device context to the color specified by the user; then it calls the `Paint` method defined in `TClock`. To add this object to the Component Palette, you must first register it:
    void __fastcall Register()

      {

        TComponentClass classes[2] = {__classid(TMyClock), __classid(TColorClock)};

        RegisterComponents("Unleash", classes, 1);

        ... // Code omitted here

    };
Here, I specify that the `TClock` and `TColorClock` objects should be placed in a group called `Unleash`. A number of properties implemented by `TCustomControl` and its ancestors do not surface automatically. To access these properties from the Component Palette, all you have to do is list them in the `__published` section of your class declaration:
    __property Align;

    __property Color;

    __property Font;
These simple declarations give your component the ability to align itself with the surface on which it resides or set its color and font. The color, in this case, is the color of the background of the form, not of the face of the form. In the actual finished version of the control, I ended up replacing the `Color` property with the `TClockAttributes` property. Once again, the primary reason for adding the `TClockAttributes` property was to show you how to work with property editors. There is, of course, a place where the `Align`, `Color`, and `Font` properties are declared replete with read and write parts. However, after you have declared the property, you can publish it simply by using the two-word declarations shown here. On the CD that accompanies this book, you will find a program called `GoldClock` that uses the `TClock` component to create a small application displaying the time. You can customize the fonts and colors in this program, and the settings you make will be written to the Registry so that they can be preserved for the next run of the program. The main form of the GoldClock application is shown in Figure 22.9. Figure 22.9.The main form of the GoldClock application. That's all I'm going to say about `TClock` and `TColorClock`. Overall, these components are fairly simple; they're interesting primarily because they show you how to go about constructing your own controls from scratch. This kind of exercise lies very much at the heart of BCB's architecture, and I expect many readers will be spending most of their time engaged almost exclusively in the business of building components. ## Tips on Creating Properties A standard technique used in component development features base classes that do not publish any properties. Consumers of the component can then inherit the functionality of the class without having to stick with the original developer's choice of published properties. After a property has been published, there is no good way to "unpublish" it. The solution to this potential problem is to create base classes that do not publish any properties. The functionality is present, but it is not surfaced in a way that it can be viewed in the Object Inspector. In many cases, the VCL creates classes that do nothing but publish the properties of their immediate ancestors. These classes sport no functions, no methods, nothing but a `__published` section full of declarations like the ones shown in the preceding section for the `Align`, `Color`, and `Font` properties. This architecture lets others inherit the capability of the class while still providing a choice regarding what will be published. The naming convention used for classes of this type usually involves the word `Custom`: `TCustomImageList`, `TCustomControl`, `TCustomMemoryStream`, `TCustomGroupBox`, `TCustomLabel`, `TCustomEdit`, `TCustomMemo`, `TCustomCheckBox`, and so on. Open `StdCtrls.hpp` to see more explicitly how this system works. Making descendants from `TEdit` or `TLabel` as I do in this chapter is a perhaps a bit too simplistic. If you really want to create a custom version of the Edit control for your own purposes, you should probably make descendants from `TCustomEdit`. Once again, you would not miss any functionality by doing so because `TEdit` does nothing but surface the "hidden" properties of `TCustomEdit`. It has no methods or data of its own; everything is inherited from `TCustomEdit`. All it does is publish 20-odd properties. You probably should not declare the read and write parts of a property in a published section because doing so forces consumers of the product to conform with your ideas for the component's interface. Instead, read and write declarations should go in the `public` section of a class that has `Custom` in the name. Then you should create a descendant of that class which actually publishes the property. ## Creating Icons for Components The icon associated with a component and placed in the Component Palette is defined in a file with a `.DCR` extension. If you do not provide this file, BCB uses the icon associated with the object's parent. If no icon is present anywhere in the component's ancestry, a default icon is used. NOTE: A DCR file is a Windows resource file with the extension changed from `.RES` to `.DCR`. The resource file contains a bitmap resource with the same name as your component. For example, the bitmap resource in a DCR file for a `TCOLOR` component would have a resource ID of `TColor`. This resource should be a 56x28 pixel (or smaller) bitmap that can be edited in the Image Editor. All you need to do is place this DCR file in the same directory as your component, and the images defined therein will show up on the Component Palette. Use the Image Editor to explore the DCR files that ship with BCB. They are stored in the `\CBUILDER\LIB` directory. I also ship some DCR files with this book in the `Utils` subdirectory where `CodeBox` and other files are kept. Follow these steps to associate your own bitmaps with a particular component. 1. Open the Image Editor from the Tools menu and choose New. 2. Chose File | New | Component Resource from the menu. A dialog called `Untitled1.dcr` appears. 3. Select Resource | New | Bitmap from the menu. A dialog called New Bitmap Attributes appears. 4. Set Colors to 16 colors, because this technology is available on nearly all Windows systems. Set Size in Pixels to 24x24. 5. Save the file as `Clock1.dcr`. Rename the bitmap you have created to `Tmyclock`. If you don't like the Image Editor, you can create a 24x24 bitmap in `Pbrush.exe` or some other bitmap editor and then create an RC file that looks like this:
    TMYCLOCK BITMAP clock.bmp
Save the file as `Clock1.rc`. Run the Borland Resource Compiler from the command line:
    brcc32 -r clock.rc
The resulting file will be called `Clock1.res`. Rename that file `Clock1.dcr`. ## Creating Help Files for Components BCB enables you to define help files that can ship with your components. These help files can be merged into the help that ships with BCB, so users of your product will feel as though it is a native part of BCB. The HLP file is a standard Windows help file that contains information about the properties, methods, and events implemented by your component. I strongly recommend that you obtain a third-party tool to aid in the construction of these help files. For example, you might buy a copy of FOREHELP from Borland, or you could turn to Blue Sky software, or some other third party that provides tools to help you create these files. As a last resort, you can use Word, WordPerfect, or even the `TRichEdit` component, to create RTF files, and then compile these RTF files into HLP files with the `HCW.EXE` help compiler that ships with BCB in the `CBUILDER\HELP\Tools` directory. You can also download this file from online sites such as the Microsoft Web site or CompuServe. (The copy of FOREHELP sold by Borland has not been upgraded for Windows 95, but it is still many times better than having no help tool at all. FOREHELP requires that you have a copy of `HC.EXE` or `HCP.EXE` on your system. These files ship with many compilers and are available royalty free on CompuServe and the Internet.) All the tools mentioned here play a peripheral role in the creation of components. They don't require a knowledge of the fascinating syntax used in component creation, but they help to make your tools attractive and easy to use. ## The Five Main Tools APIs Each of the five main Tools APIs is accessible through a separate set of routines that ship with BCB. These APIs enable you to write code that can be linked directly into the BCB IDE. Specifically, you can link your tools into `CmpLib32.ccl` the same way you link in components. Here is a list of the Tools APIs and the native BCB source files that define them. Experts: Enable you to write your own experts. `ExptIntf.hpp` `VirtIntf.hpp` `ToolIntf.hpp`: for enumerating the component pages and components installed, adding modules to the project, and so on Version Control: Enables you to write your own Version Control system or to link in a third-party system. `VcsIntf.pas`: Include this file in your project to generate the HPP file. The Pascal source might not ship with all versions of BCB. `VirtIntf.hpp`. `ToolIntf.hpp`: for opening and closing files in the editor. Component Editors: Create dialogs associated with a control at design time (for example, the DataSet Designer is a component editor). `DsgnIntf.hpp` Property Editors: Create editors for use in the Object Inspector. `DsgnIntf.hpp` Editor Interfaces: For use mostly by third-party editor vendors such as Premia, American Cybernetics, and so on. `EditIntf.hpp` `FileIntf.hpp` The letters `INTF` are an abbreviation for the word "Interface." This term was chosen because the Tools API is an interface between your own code and the BCB developers' code. Needless to say, most people will never use the Tools API. However, it will be important to a small minority of developers, and its existence means that everyone will be able to buy or download tools that extend the functionality of the IDE. ## Property Editors The Tools API for creating property editors is perhaps the most commonly used interface into the heart of the IDE. When you first use BCB and start becoming familiar with the Object Inspector, you are bound to think that it is a static element that never changes. However, by now it should come as no surprise to learn that you can change the functionality of the Object Inspector by adding new property editors to it. As I mentioned earlier, property editors control what takes place on the right side of the Properties page of the Object Inspector. In particular, when you click the `Color` property of `TEdit`, you can select a new color from a drop-down list, from a common dialog, or by typing in a new value. In all three cases, you're using a property editor. If you want to create a new property editor, you should create a descendant of `TPropertyEditor`, a class declared in `DsgnIntf.hpp`. `TPropertyEditor` has a number of children. Many of these are for handling simple types such as strings and integers. These editors are not very important to BCB programmers, because most simple types have no RTTI associated with them in C++. Instead of working with simple types, BCB programmers work with properties that are class types. The classic example of a class type of Property is `TFont`. Note, for instance, that the Font property of a form can be expanded to show the several fields of the `TFont` object. You can also pop up a dialog to edit the `TFont` object. This is a complex property editor of the type that will be developed through the remainder of this chapter. Here is the declaration for the property editor associated with the `ClockAttributes` property of the `TMyClock` component:
    class TColorNameProperty: public TClassProperty

    {

    public:

      TColorNameProperty(): TClassProperty() {}

      TPropertyAttributes __fastcall GetAttributes(void);

    };
`TColorNameProperty` doesn't descend from `TPropertyEditor` directly. Instead, it descends from a child of `TPropertyEditor` called `TClassEditor`. `TClassEditor` is designed for programmers who want to create editors for complex properties that are also classes. The `TColorNameProperty` editor doesn't pop up any dialogs, although it will in a version of this program shown at the end of this chapter. Instead of creating dialogs, it simply tells the Object Inspector that the `ClockAttributes` class can be edited as two properties. In particular, notice that the `ClockAttributes` class expands out to show a `Color` property and a `Shape` property. You need to click the plus sign in front of the `ClockAttributes` property before you can see these properties. The `GetAttributes` method is a way of defining what types of property editors you want to have associated with `TColorDialog`:
    TPropertyAttributes __fastcall TColorNameProperty::GetAttributes(void)

    {

      return TPropertyAttributes() << paSubProperties;

    }
In this case I have set the `paSubProperties` flag, which tells the Object Inspector to place the plus sign before the property so it can be opened up to reveal sub properties. A property editor that has the `paMultiSelect` flag remains active even if the user has selected more than one component of that type. For example, you can select 10 edit controls and change all their fonts in one step. BCB enables you to make this change because the `TEdit` control has the `paMultiSelect` flag set. The `paValueList` flag dictates that the property editor drops down a list of values from an enumerated or set type when the user clicks the arrow button at the far right of the editor. This functionality is built into BCB, and you need only set the flag to have it be supported by your property editor. Finally, `paDialog` states that the property editor pops up a dialog. Ultimately, the `paDialog` flag does little more than assure that the ellipsis button appears at the right of the property editor. NOTE: When you choose both `paDialog` and `paValuelist` in a single component, the property editor button always winds up being a combo drop-down list button. In other words, the dialog button is obscured, even though the functionality is still present. See, for example, the `Color` property of a `TForm` or `TEdit`. ## Streaming Properties of Type Class The `ClockAttributes` property will not work properly unless it is streamed to disk. In particular, effective use of `TClockAttributes` depends on whether the two key properties in `TClockAttributes` get written to the DFM file where the form is stored. To ensure that this happens, all you have to do is declare `ClockAttributes` as a published property, and then descend `TClockAttributes` from `TPersistent`, and make sure that the `Shape` and `Color` properties are published. Here is the declaration for the `TClockAttributes`:
    class TClockAttributes: public TPersistent

    {

    private:

      TColor FColor;

      TShapeType FShape;

      TComponent *FOwner;

      void __fastcall SetColor(TColor AColor);

      void __fastcall SetShape(TShapeType AShape);

    public:

      virtual __fastcall TClockAttributes() : TPersistent() { }

      virtual __fastcall TClockAttributes(TComponent *AOwner)

        : TPersistent() { FOwner = AOwner; }

    __published:

      __property TColor Color={read=FColor, write=SetColor};

      __property TShapeType  Shape={read=FShape, write=SetShape};

    };
The key part of this declaration is where the `Color` and `Shape` properties are designated as published. This means that they will have RTTI associated with them, and that they will be streamed out to the DFM file automatically. Here is a text version of the `TMyClock` component as it appears in a DFM file:
    object MyClock2: TMyClock

      Left = 112

      Top = 192

      Width = 100

      Height = 100

      ClockAttributes.Color = clBtnFace

      ClockAttributes.Shape = stEllipse

    end
Note that the two key properties of `ClockAttributes` have been included in the DFM file. I did not have to do anything special to make this happen. Instead, I just made sure that `ClockAttributes` itself was a published property, and that `Color` and `Shape` were published properties of `TClockAttributes`. Once I was sure the properties were being streamed out, the only thing left to do was tell the Object Inspector to show both values of `TClockAttributes`. To do this, I simply created the simple, do-nothing `TColorNameProperty` class. As you saw, this class descends from `TClassProperty`, which is declared in `Dsdintf.hpp`. ## Examining DsgnIntf.cpp The `DsgnIntf` unit is unusual in that it is very carefully documented by the developer who created it. For example, here are excerpts from that unit describing the two methods I call in the descendant of `TPropertyEditor`:
    Edit

          Called when the `...' button is pressed or the

          property is double-clicked. This can, for example,

          bring up a dialog to allow the editing of the component

          in some more meaningful fashion than by text

          (e.g. the Font property).

    GetAttributes

          Returns the information for use in the Object

          Inspector to be able to show the appropriate tools.

          GetAttributes return a set of type TPropertyAttributes.
These entries were written by the developers, and they extensively document this important interface to the core code inside the heart of the IDE. Here are declarations for `Edit` and `GetAttributes`, as well as the other key functions in `TPropertyEditor`:
    class __declspec(pascalimplementation) TPropertyEditor : public System::TObject

    public:

      __fastcall virtual ~TPropertyEditor(void);

      virtual void __fastcall Activate(void);

      virtual bool __fastcall AllEqual(void);

      virtual void __fastcall Edit(void);

      virtual TPropertyAttributes __fastcall GetAttributes(void);

      Classes::TComponent* __fastcall GetComponent(int Index);

      virtual int __fastcall GetEditLimit(void);

      virtual System::AnsiString __fastcall GetName(void);

      virtual void __fastcall GetProperties(TGetPropEditProc Proc);

      Typinfo::PTypeInfo __fastcall GetPropType(void);

      virtual System::AnsiString __fastcall GetValue(void);

      virtual void __fastcall GetValues(Classes::TGetStrProc Proc);

      virtual void __fastcall Initialize(void);

      void __fastcall Revert(void);

      virtual void __fastcall SetValue(const System::AnsiString Value);

      bool __fastcall ValueAvailable(void);

    }
Once again, all these methods are carefully documented in `DsgnIntf.hpp`. You should study that file carefully in order to add to the knowledge about property and component editors outlined in the remainder of this chapter. ## Registering the Property Editors You must register property editors with the system before compiling them into `CmpLib32.ccl`. To register a property, call `RegisterPropertyEditor` in the `Register` method for your component:
    RegisterPropertyEditor(__typeinfo(TClockAttributes),

    __classid(TMyClock), "Colors", __classid(TColorNameProperty));
The declaration for `RegisterPropertyEditor` looks like this:
    extern void __fastcall RegisterPropertyEditor(

      Typinfo::PTypeInfo PropertyType,

      System::TMetaClass* ComponentClass,

      const System::AnsiString PropertyName,

      System::TMetaClass* EditorClass);
The various parameters are as follow: `PropertyType`. The first parameter passed to this function states the type of data handled by the editor. In this case, it is `TClockAttributes`. BCB uses this information as the first in a series of checklists that determine which properties should be associated with this editor. As noted above, this type cannot be a simple value such as an `int` or `TColor`. It must be a class type. If you want to work with simple types, you should either wrap them in a class or switch to Object Pascal. `ComponentClass`. The second parameter further qualifies which components will use this editor. In this case, I have narrowed the range down to `TMyClock` and its descendants. If I had written `TComponent` instead of `TMyClock`, or if I had set this parameter to `NULL`, all properties of type `TClockAttributes` would start using that editor. You therefore can build a new editor for fonts or colors, install it on a customer's system, and it would work with all properties of that type. In other words, you don't have to create a component to be able to write an editor for it. `PropertyName`. The third parameter limits the scope to properties with the name passed in this string. If the string is empty, the editor is used for all properties that get passed the first two parameters. `EditorClass`. This parameter defines the class of editor associated with the properties defined in the first three parameters. If you want to find out more about this function, refer to the comments in `DsgnIntf.pas`. These comments may not be copied into `DsgnIntf.hpp`, so check both places. ## Working with Component Editors After you have learned how to build property editors, understanding component editors is easy. These tools are descendants of `TComponentEditor`, just as property editors are descendants of `TPropertyEditor`:
    class TClockEditor: public TComponentEditor

    {

    protected:

      virtual __fastcall void Edit(void);

    public:

      virtual __fastcall TClockEditor(TComponent *AOwner, TFormDesigner *Designer)

        : TComponentEditor(AOwner, Designer) {}

    };
The `TColorDialog` has a simple editor that pops up a dialog requesting that the user choose a new color:
    void __fastcall TClockEditor::Edit(void)

    {

      TColorDialog *C = new TColorDialog(Application);

      if (C->Execute())

        ((TColorClock *)(Component))->FaceColor = C->Color;

    }
In this case, I'm popping up the standard `TColorDialog` that appears when you click the ellipsis icon in the Object Inspector for a property of type `TColor`. The difference, of course, is that this is a component editor, not a property editor. In the more complex component editor example shown later in this chapter, you pop open a form that allows the user to make several changes to the component. It is easy to access the underlying component from inside a component editor. In particular, the component you are editing is available to you in a variable called `Component`. The `Component` variable referenced in this example is declared globally inside `TComponentEditor` objects. To access it, you merely have to typecast it as desired to get at the component you're currently editing. For example, in this example, I typecast `Component` as a `TColorClock`. I don't bother with a `dynamic_cast` in this example because the conversion has to work, by the very definition of the context of the call. This component editor, of course, is the simplest possible, but it gets you started working with these useful tools. Figure 22.10 shows the component editor in action. Figure 22.10.The first take on the component editor for the `TClock` component pops up a simple `TColorDialog`. NOTE: Two additional component editors, each one more complex than the last, will be shown later in the chapter. In particular, you will see how to pop up a component editor that is built on top of a VCL form. The `Register` method for `TClockEditor` looks like this:
      void __fastcall Register()

      {

        . . .

        RegisterComponentEditor(__classid(TMyClock), __classid(TClockEditor));

        . . .

      }
    }
The declaration for this procedure looks like this:
    extern void __fastcall RegisterComponentEditor(System::TMetaClass* ComponentClass,
                                                  System::TMetaClass* ComponentEditor);
Clearly, the first parameter specifies the class with which the editor is associated, and the second parameter specifies the class of the editor. In this section, I introduced you to property editors and component editors. These examples are important primarily because they help focus your attention on `DsgnIntf.hpp`, which is one of several files that ship with BCB that define the Tools API. If you want to extend the BCB IDE, you should get to know all the files that end in `INTF`. ## Extending the Component and Property Editors The original component and property editors for TMyClock and TColorClock components are very simple. Of course, using such simple component and property editors is very limiting. You may use a normal VCL form as an editor. Examples of this type of form are shown in Figures 22.11 and 22.12. Figure 22.11.The new property editor for `ClockAttributes` is a VCL form that gives the user considerable control over the appearance of the clock. Figure 22.12.This component editor for `TClock` uses a standard VCL form and can be as complex as you want. To create the form shown in Figure 22.12, I started a new project called `TestClock2`. I then copied the `Clock1` files into a new set of files called `Clock2` and changed all the names inside the files to reflect the change. For example, I renamed `TMyClock` to `TMyClock2`, `TColorClock` to `TColorClock2`, and so on. I then created a new form called `ClockEditor` and added several buttons to it, a `TColorDialog`, and a `TImage` control. I arranged the controls as shown in Figure 22.12. I then made a few changes to the Edit method of `TClockEditor2`, and added a button to my main form so that I could test the editor. The source code for this new program is shown in Listings 22.12 through 22.15. NOTE: Before installing the clock components, you might need to run the BuildObjs program found in the `Utils` directory. See the readme file on the CD that accompanies this chapter for additional information. In particular, BuildObjs makes sure that `ClockAttributes.cpp` and `ClockEditor.cpp` are both compiled into binary form as OBJ files. #### Listing 22.12. The header file for the main unit of the TestClock2 program.
    ///////////////////////////////////////

    // Main.h

    // Project: Testing example of Component editor

    // Copyright 1997 by Charlie Calvert

    //

    #ifndef MainH

    #define MainH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include "Clock2.h"

    #include "Clock1.h"

    class TForm1 : public TForm

    {

    __published:

      TButton *Button1;

      TColorClock2 *ColorClock21;

      void __fastcall Button1Click(TObject *Sender);

    private:

    public:

      __fastcall TForm1(TComponent* Owner);

    };

    extern TForm1 *Form1;
`#endif` #### Listing 22.13. The main module for the TestClock2 program.
    ///////////////////////////////////////

    // Main.cpp

    // Project: Testing example of Component editor

    // Copyright 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Main.h"

    #include "ClockEditor.h"

    #pragma link "Clock2"

    #pragma link "Clock1"

    #pragma resource "*.dfm"

    TForm1 *Form1;

    __fastcall TForm1::TForm1(TComponent* Owner)

      : TForm(Owner)

    {

    }

    void __fastcall TForm1::Button1Click(TObject *Sender)

    {

      RunClockEditorDlg(ColorClock21);
`}` #### Listing 22.14. The header for the ColorEditor dialog.
    ///////////////////////////////////////

    // ClockEditor.h

    // Project: Clock2 example of Component editor

    // Copyright 1997 by Charlie Calvert

    //

    #ifndef ClockEditorH

    #define ClockEditorH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include <vcl\Dialogs.hpp>

    #include "Clock2.h"

    #include <vcl\ExtCtrls.hpp>

    #include <vcl\Buttons.hpp>

    void RunClockEditorDlg(TColorClock2 *Clock);

    class TClockEditorDialog : public TForm

    {

    __published:

      TButton *SetFaceColorBtn;

      TColorDialog *ColorDialog1;

      TButton *SetBackColorBtn;

      TPanel *Panel1;

      TImage *Image1;

      TBitBtn *BitBtn1;

      void __fastcall SetFaceColorBtnClick(TObject *Sender);

      void __fastcall SetBackColorBtnClick(TObject *Sender);

    private:

      TColorClock2 *FClock;

    public:

      __fastcall TClockEditorDialog(TComponent* Owner);

      __property TColorClock2 *Clock={read=FClock, write=FClock};

    };

    extern TClockEditorDialog *ClockEditorDialog;
`#endif` #### Listing 22.15. The main module for the ColorEditor dialog.
    ///////////////////////////////////////

    // ClockEditor.cpp

    // Project: Clock2 example of Component editor

    // Copyright 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "ClockEditor.h"

    #include "clock2.h"

    #pragma resource "*.dfm"

    TClockEditorDialog *ClockEditorDialog;

    __fastcall TClockEditorDialog::TClockEditorDialog(TComponent* Owner)

      : TForm(Owner)

    {

    }

    void __fastcall TClockEditorDialog::SetFaceColorBtnClick(TObject *Sender)

    {

      if (ColorDialog1->Execute())

      {

        Clock->FaceColor = ColorDialog1->Color;

      }

    }

    void __fastcall TClockEditorDialog::SetBackColorBtnClick(TObject *Sender)

    {

      if (ColorDialog1->Execute())

      {

        Clock->ClockAttributes->Color = ColorDialog1->Color;

      }

    }

    void RunClockEditorDlg(TColorClock2 *Clock)

    {

      ClockEditorDialog = new TClockEditorDialog(Application);

      ClockEditorDialog->Clock = Clock;

      ClockEditorDialog->ShowModal();

      delete ClockEditorDialog;
} #### Listing 22.16. The header for the VCL form that serves a dialog for editing the ClockAttributes property.
    ///////////////////////////////////////

    // ClockAttributes.h

    // Clock Component with Component Editor

    // Copyright (c) 1997 by Charlie Calvert

    //

    #ifndef ClockAttributesH

    #define ClockAttributesH

    #include <vcl\Classes.hpp>

    #include <vcl\Controls.hpp>

    #include <vcl\StdCtrls.hpp>

    #include <vcl\Forms.hpp>

    #include <vcl\ExtCtrls.hpp>

    #include <vcl\Buttons.hpp>

    #include "Clock2.h"

    #include <vcl\Dialogs.hpp>

    void RunClockAttributesDlg(TClockAttribute2 *AClockAttribute);

    class TClockAttributesDialog : public TForm

    {

    __published:

      TPanel *Panel1;

      TComboBox *ComboBox1;

      TBitBtn *GetColorsBtn;

      TBitBtn *BitBtn2;

      TColorDialog *ColorDialog1;

      TImage *Image1;

      void __fastcall GetColorsBtnClick(TObject *Sender);

      void __fastcall BitBtn2Click(TObject *Sender);

    private:

      TClockAttribute2 *FClockAttribute;

    public:

      __fastcall TClockAttributesDialog(TComponent* Owner);

      __property TClockAttribute2 *ClockAttribute =

        {read=FClockAttribute, write=FClockAttribute};

    };

    extern TClockAttributesDialog *ClockAttributesDialog;
`#endif` #### Listing 22.17. The main source file for the VCL form that serves a dialog for editing the ClockAttributes property.
    ///////////////////////////////////////

    // ClockAttributes.cpp

    // Clock Component with Component Editor

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "ClockAttributes.h"

    #pragma resource "*.dfm"

    TClockAttributesDialog *ClockAttributesDialog;

    __fastcall TClockAttributesDialog::TClockAttributesDialog(TComponent* Owner)

      : TForm(Owner)

    {

    }

    void RunClockAttributesDlg(TClockAttribute2 *AClockAttribute)

    {

      ClockAttributesDialog = new TClockAttributesDialog(Application);

      ClockAttributesDialog->ClockAttribute = AClockAttribute;

      ClockAttributesDialog->ComboBox1->ItemIndex =  (int)AClockAttribute->Shape;

      ClockAttributesDialog->ShowModal();

      delete ClockAttributesDialog;

    }

    void __fastcall TClockAttributesDialog::GetColorsBtnClick(TObject *Sender)

    {

      if (ColorDialog1->Execute())

      {

        FClockAttribute->Color = ColorDialog1->Color;

      }

    }

    void __fastcall TClockAttributesDialog::BitBtn2Click(TObject *Sender)

    {

      FClockAttribute->Shape = TShapeType(ComboBox1->ItemIndex);
} #### Listing 22.18. Here is the header for the new version of the clock components.
    ///////////////////////////////////////

    // Clock2.h

    // Clock Component

    // Copyright (c) 1997 by Charlie Calvert

    //

    //------------------------------------

    #ifndef Clock2H

    #define Clock2H

    #include <vcl\sysutils.hpp>

    #include <vcl\controls.hpp>

    #include <vcl\classes.hpp>

    #include <vcl\forms.hpp>

    #include <vcl\dsgnintf.hpp>

    #include <vcl\messages.hpp>

    class TClockAttribute2: public TPersistent

    {

    private:

      TColor FColor;

      TShapeType FShape;

      TComponent *FOwner;

      void __fastcall SetColor(TColor AColor);

      void __fastcall SetShape(TShapeType AShape);

    public:

      virtual __fastcall TClockAttribute2(TComponent *AOwner)

        : TPersistent() { FOwner = AOwner; }

    __published:

      __property TColor Color={read=FColor, write=SetColor};

      __property TShapeType  Shape={read=FShape, write=SetShape};

    };

    ////////////////////////////////////////

    // TMyClock ////////////////////////////

    ////////////////////////////////////////

    class TMyClock2: public TCustomControl

    {

    private:

      int FTimer;

      Boolean FRunning;

      TClockAttribute2 *FClockAttributes;

      void __fastcall SetRunning(Boolean ARun);

    protected:

      virtual void __fastcall Paint(void);

      void __fastcall WMTimer(TMessage &Message);

      void __fastcall WMDestroy(TMessage &Message);

    public:

      virtual __fastcall TMyClock2(TComponent* Owner);

    __published:

      __property Boolean Running={read=FRunning, write=SetRunning};

      __property Align;

      __property TClockAttribute2 *ClockAttributes=

        {read=FClockAttributes, write=FClockAttributes };

      __property Font;

    BEGIN_MESSAGE_MAP

      MESSAGE_HANDLER(WM_TIMER, TMessage, WMTimer);

      MESSAGE_HANDLER(WM_DESTROY, TMessage, WMDestroy);

    END_MESSAGE_MAP(TCustomControl);

    };

    ////////////////////////////////////////

    // TColorClock /////////////////////////

    ////////////////////////////////////////

    class TColorClock2: public TMyClock2

    {

    private:

      TColor FFaceColor;

      void __fastcall SetColor(TColor Color);

    protected:

      void virtual __fastcall Paint(void);

    public:

      virtual __fastcall TColorClock2(TComponent *Owner)

        :TMyClock2(Owner) { FFaceColor = clGreen; }

    __published:

      __property TColor FaceColor={read=FFaceColor, write=SetColor, nodefault};

    };

    ////////////////////////////////////////

    // TClockEditor ////////////////////////

    ////////////////////////////////////////

    class TClockEditor2: public TComponentEditor

    {

    protected:

      virtual __fastcall void Edit(void);

    public:

      virtual __fastcall TClockEditor2(TComponent *AOwner, TFormDesigner *Designer)

        : TComponentEditor(AOwner, Designer) {}

    };

    ////////////////////////////////////////

    // TColorNameProperty /////////////////

    ////////////////////////////////////////

    class TClockAttributeProperty: public TClassProperty

    {

    public:

      TClockAttributeProperty(): TClassProperty() {}

      TPropertyAttributes __fastcall GetAttributes(void);

      void virtual __fastcall Edit(void);

    };
`#endif` #### Listing 22.19. The main source file for the new version of the clock components.
    ///////////////////////////////////////

    // Clock2.cpp

    // Clock Component with Component Editor

    // Copyright (c) 1997 by Charlie Calvert

    //

    #include <vcl\vcl.h>

    #pragma hdrstop

    #include "Clock2.h"

    #include "ClockEditor.h"

    #include "ClockAttributes.h"

    #pragma link "ClockEditor.obj"

    #pragma link "ClockAttributes.obj"

    ///////////////////////////////////////

    // ValidControlCheck

    ///////////////////////////////////////

    static inline TMyClock2 *ValidCtrCheck()

    {

      return new TMyClock2(NULL);

    }

    ///////////////////////////////////////

    // TClockAttributes

    ///////////////////////////////////////

    void __fastcall TClockAttribute2::SetColor(TColor AColor)

    {

      FColor = AColor;

      ((TControl *)(FOwner))->Invalidate();

    };

    void __fastcall TClockAttribute2::SetShape(TShapeType AShape)

    {

      FShape = AShape;

      ((TControl *)(FOwner))->Invalidate();

    };

    ///////////////////////////////////////

    // Constructor

    ///////////////////////////////////////

    __fastcall TMyClock2::TMyClock2(TComponent* Owner)

      : TCustomControl(Owner)

    {

      Width = 100;

      Height = 100;

      FTimer = 1;

      FClockAttributes = new TClockAttribute2(this);

      FClockAttributes->Color = clBtnFace;

      FClockAttributes->Shape = stEllipse;

    }

    ///////////////////////////////////////

    // SetRunning

    ///////////////////////////////////////

    void __fastcall TMyClock2::SetRunning(Boolean Run)

    {

      if (Run)

      {

        SetTimer(Handle, FTimer, 50, NULL);

        FRunning = True;

      }

      else

      {

        KillTimer(Handle, FTimer);

        FRunning = False;

      }

    }

    ///////////////////////////////////////

    // Paint

    ///////////////////////////////////////

    void __fastcall TMyClock2::Paint(void)

    {

      Color = ClockAttributes->Color;

      switch (ClockAttributes->Shape)

      {

        int X;

        case stEllipse: Canvas->Ellipse(0, 0, Width, Height); break;

        case stRectangle: Canvas->Rectangle(0, 0, Width, Height); break;

        case stRoundRect:

          Canvas->RoundRect(0, 0, Width, Height, Width - 100, Height);

          break;

        case stSquare:

        {

          if (Width < Height)

          {

            X = Width / 2;

            Canvas->Rectangle(Width - X, 0, Width + X, Width);

          }

          else

          {

            X = Height / 2;

            Canvas->Rectangle((Width / 2) - X, 0, (Width / 2) + X, Height);

          }

          break;

        }

        case stCircle:

        {

          if (Width < Height)

          {

            X = Width / 2;

            Canvas->Ellipse(Width - X, 0, Width + X, Width);

          }

          else

          {

            X = Height / 2;

            Canvas->Ellipse((Width / 2) - X, 0, (Width / 2) + X, Height);

          }

          break;

        }

      }

    }

    ///////////////////////////////////////

    // WM_TIMER

    ///////////////////////////////////////

    void __fastcall TMyClock2::WMTimer(TMessage &Message)

    {

      AnsiString S = TimeToStr(Time());

      Canvas->Font = Font;

      Canvas->TextOut(Width / 2 - Canvas->TextWidth(S) / 2,

                      Height / 2 - Canvas->TextHeight(S) / 2, S);

    }

    ///////////////////////////////////////

    // WM_DESTROY

    ///////////////////////////////////////

    void __fastcall TMyClock2::WMDestroy(TMessage &Message)

    {

      KillTimer(Handle, FTimer);

      FTimer = 0;

      TCustomControl::Dispatch(&Message);

    }

    //------------------------------------

    //-- TColorClock2 --------------------

    //------------------------------------

    ///////////////////////////////////////

    // WM_DESTROY

    ///////////////////////////////////////

    void __fastcall TColorClock2::Paint()

    {

      Canvas->Brush->Color = FFaceColor;

      TMyClock2::Paint();

    }

    ///////////////////////////////////////

    // SetColor

    ///////////////////////////////////////

    void __fastcall TColorClock2::SetColor(TColor Color)

    {

      FFaceColor = Color;

      InvalidateRect(Handle, NULL, True);

    }

    //------------------------------------

    //-- TClockEditor --------------------

    //------------------------------------

    void __fastcall TClockEditor2::Edit(void)

    {

      RunClockEditorDlg(((TColorClock2 *)(Component)));

    }

    //------------------------------------

    //-- TColorNameProperty --------------

    //------------------------------------

    TPropertyAttributes __fastcall TClockAttributeProperty::GetAttributes(void)

    {

      return TPropertyAttributes() << paSubProperties << paDialog;

    }

    void __fastcall TClockAttributeProperty::Edit()

    {

      TClockAttribute2 *C = (TClockAttribute2 *)GetOrdValue();

      RunClockAttributesDlg(C);

      Modified();

    }

    ///////////////////////////////////////

    // Register

    ///////////////////////////////////////

    namespace Clock2

    {

      void __fastcall Register()

      {

        TComponentClass classes[2] = {__classid(TMyClock2), __classid(TColorClock2)};

        RegisterComponents("Unleash", classes, 1);

        RegisterComponentEditor(__classid(TColorClock2), __classid(TClockEditor2));

        RegisterPropertyEditor(__typeinfo(TClockAttribute2),

          __classid(TMyClock2), "ClockAttributes",

          __classid(TClockAttributeProperty));

      }
} When popping up a new form from inside a component editor, you have to create the actual instance of the form and assign it an owner. You need to do so because the form will not be auto-created in your program's project source file. To handle the chore of creating the form, I created a special method inside the `ClockEditor` unit. The routine looks like this:
    void RunClockEditorDlg(TColorClock2 *Clock)

    {

      ClockEditorDialog = new TClockEditorDialog(Application);

      ClockEditorDialog->Clock = Clock;

      ClockEditorDialog->ShowModal();

      delete ClockEditorDialog;

    }
This routine takes an instance of the clock that needs to be edited as its sole parameter. It then creates an instance of the `TClockEditorDialog` and assigns the clock to an internal data store of the dialog. Finally, the code shows the dialog and cleans up the memory when the user is through making the edits. To test this dialog, you can simply drop an instance of the clock onto the main form of the application and then call the dialog from a button click response method:
    void __fastcall TForm1::Button1Click(TObject *Sender)

    {

      RunClockEditorDlg(ColorClock21);

    }
After you're sure that everything is working properly, you can change the `TClockEditor2::Edit` method so that it calls this dialog when the user double-clicks the component in design mode:
    void __fastcall TClockEditor2::Edit(void)

    {

      RunClockEditorDlg(((TColorClock2 *)(Component)));
    }
Once again, you can see that the code takes advantage of the `Component` object that is declared ahead of time by the VCL. All you need to do is typecast the object to the proper type and then pass it on to the `ClockEditor` object. Inside the `TClockEditorDialog` itself, the act of changing the component is trivial:
    void __fastcall TClockEditorDialog::SetFaceColorBtnClick(TObject *Sender)

    {

      if (ColorDialog1->Execute())

      {

        Clock->FaceColor = ColorDialog1->Color;

      }

    }
This code simply pops up a color dialog and allows the user to choose a new face color. If the user makes a selection, the color is assigned to the relevant property of the `TColorClock2` object. I still need to address one subject in this project. The issue here is that the `TClock2` project now consists of two different modules. The first module contains the `TColorClock` itself, and the second module contains the `TClockEditorDialog`. When you add the `Clock2.cpp` module to the Component Palette, you also have to find a way to bring in `ClockEditor.cpp`; otherwise, the project will not compile. Unfortunately, you can't add `ClockEditor` directly to the `CmpLib32` project because `ClockEditor` does not have a `Register` method. Without the `Register` method, the component library won't know what to do with the file. The solution to this problem is provided by a `pragma` that can be added to `Clock2.cpp` to tell the compiler that it must include `ClockEditor.obj` in any projects that use this unit:
    #pragma link "ClockEditor.obj"
After you add this line, `CmpLib32` will know that it has to include `ClockEditor.obj` in the compilation. It will not, however, know to compile the unit from a CPP file into an OBJ file. NOTE: In the first shipping version of BCB, a problem can occur if you try to recompile `CmpLib32.ccl` and do not provide a precompiled copy of `ClockEditor.obj`. In particular, the compiler does not treat `ClockEditor` as part of the project it is making, so it will not know to make `ClockEditor.obj` from `ClockEditor.cpp`. One solution to this problem is to start a small project that will compile `ClockEditor.cpp` into `ClockEditor.obj`. For example, the simple `TestClock2` program will create `ClockEditor.obj` each time it is compiled. The BuildObjs file included on the CD will do the same thing. If you have created a copy of the OBJ file, and the compiler still complains, try touching the unit that uses `ClockEditor`. For example, make a small change to `Clock2.cpp`. Any change will do. You can just add a space to the unit and then remove it. The point is that you need to save the unit so that it has a new date and time. This time, when you recompile and link `CmpLib32`, it will find your new copy of `ClockEditor.obj`. That's all I'm going to say about component editors. As you have seen, this subject is not complicated, even when you create your own custom form for editing the object. Creating custom forms of this type can be very helpful for the user because it gives you a chance to step him or her through the process of initializing the object. You can even create a little expert that runs when the user double-clicks the component. The expert could take the user by the hand and ensure that he or she sets up your control properly. The property editor for the `ClockAttributes` property is very similar to the component editor. The primary difference is in the way you access the underlying value that you want to edit:
    void __fastcall TClockAttributeProperty::Edit()

    {

      TClockAttribute2 *C = (TClockAttribute2 *)GetOrdValue();

      RunClockAttributesDlg(C);

      Modified();

    }
Instead of having a ready-to-use variable called `Component`, you need to call the `GetOrdValue` method of `TPropertyEditor`. This function was originally intended to retrieve simply ordinal values. It can, however, do double-duty and retrieve pointer values. This is possible because a pointer and an integer both consist of 4 bytes. In this case, I typecast the value returned from `GetOrdValue` as a variable of type `TClockAttribute`. I can then pass this value into my VCL form and edit it as I please. The principle here is exactly the same as in the Component editor, only this time I access the underlying property that I want to edit through `GetOrdValue`, rather than through a variable named `Component`. Inside the ClockAttributes dialog you need to create a routine that will pop open the VCL form:
    void RunClockAttributesDlg(TClockAttribute2 *AClockAttribute)

    {

      ClockAttributesDialog = new TClockAttributesDialog(Application);

      ClockAttributesDialog->ClockAttribute = AClockAttribute;

      ClockAttributesDialog->ComboBox1->ItemIndex =  (int)AClockAttribute->Shape;

      ClockAttributesDialog->ShowModal();

      delete ClockAttributesDialog;

    }
Most of this code is very routine and does nothing more than allocate memory for a form, show it modally, and then destroy it. Note the line that sets the value of the selected item in the form's combo box:
    ClockAttributesDialog->ComboBox1->ItemIndex =  (int)AClockAttribute->Shape;
This line accesses one of the fields of the `TClockAttribute` class. It then sets the `ItemIndex` of the combo box to the currently selected shape. Here are the values displayed in the combo box: `Rectangle` `Square` `RoundRect` `RoundSquare` `Ellipse` `Circle` If the technique used here to select an item in the combo box does not make sense to you, you might want to refer to similar code from the ShapeDem2 program in Chapter 2. ## Summary In this chapter, you have learned about building components. Specifically, you learned how to create components that do the following: Change an ancestor's default settings. For example, you created `TEdit` descendants with new default colors and fonts. Add functionality not available in an ancestor object. For example, the `TColorClock` object does things that `TClock` does not. Are built up from scratch so that they can add new functionality to the IDE. For example, the `TClock` component brings something entirely new to BCB programming that does not exist in any other component that ships with the product. You also learned about tools and files associated with components, such as the DCR and KWF files. Finally, you learned about the Tools API and specifically about the art of making property editors and component editors.
| ©Copyright, Macmillan Computer Publishing. All rights reserved.

| Use of this site is subject certain Terms & Conditions. Copyright (c) 1996-1999 EarthWeb, Inc.. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of EarthWeb is prohibited. Please read our privacy policy for details.