Copyright: Copyright © 1999 Inprise Corporation.                      All rights reserved.                                                 This Help file was compiled on   Wednesday, January 13, 1999 19:28:40.


Overview of component creation

Topic groups	See also

This set of topics provides an overview of component design and the process of writing components for C++Builder applications. The material here assumes that you are familiar with C++Builder and its standard components.

The main topics discussed are

Visual Component Library

Components and classes

How do you create components?

What goes into a component?

Creating a new component

Testing uninstalled components

Installing a component on the Component palette

For information on installing new components, see Installing component packages.


The Visual Component Library

Topic groups

C++Builder’s components are all part of a class hierarchy called the Visual Component Library (VCL). The following figure shows the relationship of selected classes that make up the VCL.For a more detailed discussion of class hierarchies and the inheritance relationships among classes, see Object-oriented programming for component writers

The TComponent class is the shared ancestor of every component in the VCL. TComponent provides the minimal properties and events necessary for a component to work in C++Builder. The various branches of the library provide other, more specialized capabilities.

When you create a component, you add to the VCL by deriving a new class from one of the existing class types in the hierarchy.


Components and classes

Topic groups

Because components are classes, component writers work with objects at a different level from application developers. Creating new components requires that you derive new classes.

Briefly, there are two main differences between creating components and using them in applications. When creating components,

You access parts of the class that are inaccessible to application programmers.

You add new parts (such as properties) to your components.

Because of these differences, you need to be aware of more conventions and think about how application developers will use the components you write.


How do you create components?

Topic groups

A component can be almost any program element that you want to manipulate at design time. Creating a component means deriving a new class from an existing one. You can derive a new component from any existing component, but the following are the most common ways to create components:

Modifying existing controls

Creating windowed controls

Creating graphic controls

Subclassing Windows controls

Creating nonvisual components

The following table summarizes the different kinds of components and the classes you use as starting points for each.

You can also derive classes that are not components and cannot be manipulated on a form. C++Builder includes many such classes, like TRegIniFile and TFont.


Modifying existing controls

Topic groups

The simplest way to create a component is to customize an existing one. You can derive a new component from any of the components provided with C++Builder.

Some controls, such as list boxes and grids, come in several variations on a basic theme. In these cases, the VCL includes an abstract class (with the word “custom” in its name, such as TCustomGrid) from which to derive customized versions.

For example, you might want to create a special list box that does not have some of the properties of the standard TListBox class. You cannot remove (hide) a property inherited from an ancestor class, so you need to derive your component from something above TListBox in the hierarchy. Rather than force you to start from the abstract TWinControl class and reinvent all the list box functions, the VCL provides TCustomListBox, which implements the properties of a list box but does not publish all of them. When you derive a component from an abstract class like TCustomListBox, you publish only the properties you want to make available in your component and leave the rest protected.

The section Creating propeties explains publishing inherited properties.The section Modifying an existing componentThe section Customizing a grid show examples of modifying existing controls.


Creating original controls

Topic groups

Windowed controls are objects that appear at runtime and that the user can interact with. Each windowed control has a window handle, accessed through its Handle property, that lets Windows identify and operate on the control. The handle allows the control to receive input focus and can be passed to Windows API functions.

All windowed controls descend from the TWinControl class. These include most standard Windows controls, such as pushbuttons, list boxes, and edit boxes. While you could derive an original control (one that’s not related to any existing control) directly from TWinControl, C++Builder provides the TCustomControl component for this purpose. TCustomControl is a specialized windowed control that makes it easier to draw complex visual images.

The section Customizing a grid presents an example of creating a windowed control.


Creating graphic controls

Topic groups

If your control does not need to receive input focus, you can make it a graphic control. Graphic controls are similar to windowed controls, but have no window handles, and therefore consume fewer system resources. Components like TLabel, which never receive input focus, are graphic controls. Although these controls cannot receive focus, you can design them to react to mouse messages.

C++Builder supports the creation of custom controls through the TGraphicControl component. TGraphicControl is an abstract class derived from TControl. Although you can derive controls directly from TControl, it is better to start from TGraphicControl, which provides a canvas to paint on and handles WM_PAINT messages; all you need to do is override the Paint method.

The section Creating a graphic component presents an example of creating a graphic control.


Subclassing Windows controls

Topic groups

In traditional Windows programming, you create custom controls by defining a new window class and registering it with Windows. The window class (which is similar to the objects or classes in object-oriented programming) contains information shared among instances of the same sort of control; you can base a new window class on an existing class, which is called subclassing. You then put your control in a dynamic-link library (DLL), much like the standard Windows controls, and provide an interface to it.

Using C++Builder,you can create a component “wrapper” around any existing window class. So if you already have a library of custom controls that you want to use in C++Builder applications, you can create C++Builder components that behave like your controls, and derive new controls from them just as you would with any other component.

For examples of the techniques used in subclassing Windows controls, see the components in the STDCTLS header file that represent standard Windows controls, such as TEdit.


Creating nonvisual components

Topic groups

Nonvisual components are used as interfaces for elements like databases (TDataSet) and system clocks (TTimer), and as placeholders for dialog boxes (TCommonDialog and its descendants). Most of the components you write are likely to be visual controls. Nonvisual components can be derived directly from TComponent, the abstract base class for all components.


What goes into a component?

Topic groups

To make your components reliable parts of the C++Builder environment, you need to follow certain conventions in their design. This section discusses the following topics:

Removing dependencies

Properties, methods, and events

Graphics encapsulation

Registration


Removing dependencies

Topic groups

One quality that makes components usable is the absence of restrictions on what they can do at any point in their code. By their nature, components are incorporated into applications in varying combinations, orders, and contexts. You should design components that function in any situation, without preconditions.

An excellent example of removing dependencies is the Handle property of TWinControl. If you have written Windows applications before, you know that one of the most difficult and error-prone aspects of getting a program running is making sure that you do not try to access a window or control until you have created it by calling the CreateWindow API function. C++Builder windowed controls relieve users from this concern by ensuring that a valid window handle is always available when needed. By using a property to represent the window handle, the control can check whether the window has been created; if the handle is not valid, the control creates a window and returns the handle. Thus, whenever an application’s code accesses the Handle property, it is assured of getting a valid handle.

By removing background tasks like creating the window, C++Builder components allow developers to focus on what they really want to do. Before passing a window handle to an API function, there is no need to verify that the handle exists or to create the window. The application developer can assume that things will work, instead of constantly checking for things that might go wrong.

Although it can take time to create components that are free of dependencies, it is generally time well spent. It not only spares application developers from repetition and drudgery, but it reduces your documentation and support burdens.


Properties, methods, and events

Topic groups

Aside from the visible image manipulated in the Form designer, the most obvious attributes of a component are its properties, events, and methods. Each of these has a section devoted to it in this file, but the discussion that follows explains some of the motivation for their use.

Properties

Properties give the application developer the illusion of setting or reading the value of a variable, while allowing the component writer to hide the underlying data structure or to implement special processing when the value is accessed.

There are several advantages to using properties:

Properties are available at design time. The application developer can set or change initial values of properties without having to write code.

Properties can check values or formats as the application developer assigns them. Validating input at design time prevents errors.

The component can construct appropriate values on demand. Perhaps the most common type of error programmers make is to reference a variable that has not been initialized. By representing data with a property, you can ensure that a value is always available on demand.

Properties allow you to hide data under a simple, consistent interface. You can alter the way information is structured in a property without making the change visible to application developers.

The section Overview of component creation explains how to add properties to your components.

Events

An event is a special property that invokes code in response to input or other activity at runtime. Events give the application developer a way to attach specific blocks of code to specific runtime occurrences, such as mouse actions and keystrokes. The code that executes when an event occurs is called an event handler.

Events allow application developers to specify responses to different kinds of input without defining new components.

The section Creating events explains how to implement standard events and how to define new ones.

Methods

Class methods are functions that operate on a class rather than on specific instances of the class. For example, every component’s constructor method is a class method. Component methods are functions that operate on the component instances themselves. Application developers use methods to direct a component to perform a specific action or return a value not contained by any property.

Because they require execution of code, methods can be called only at runtime. Methods are useful for several reasons:

Methods encapsulate the functionality of a component in the same object where the data resides.

Methods can hide complicated procedures under a simple, consistent interface. An application developer can call a component’s AlignControls method without knowing how the method works or how it differs from the AlignControls method in another component.

Methods allow updating of several properties with a single call.

The section Creating methods explains how to add methods to your components.


Graphics encapsulation

Topic groups

C++Builder simplifies Windows graphics by encapsulating various graphic tools into a canvas. The canvas represents the drawing surface of a window or control and contains other classes, such as a pen, a brush, and a font. A canvas is much like a Windows device context, but it takes care of all the bookkeeping for you.

If you have written a graphical Windows application, you are familiar with the requirements imposed by Windows’ graphics device interface (GDI). For example, GDI limits the number of device contexts available and requires that you restore graphic objects to their initial state before destroying them.

With C++Builder, you do not have to worry about these things. To draw on a form or other component, you access the component’s Canvas property. If you want to customize a pen or brush, you set its color or style. When you finish, C++Builder disposes of the resources. C++Builder also caches resources to avoid recreating them if your application frequently uses the same kinds of resource.

You still have full access to the Windows GDI, but you will often find that your code is simpler and runs faster if you use the canvas built into C++Builder components. Graphics features are detailed inThe section Using graphics in components.


Registration Overview

Topic groups

Before you can install your components in the C++Builder IDE, you have to register them. Registration tells C++Builder where you want your component to appear on the Component palette. You can also customize the way C++Builder stores your components in the form file. For information on registering a component, see Registering components.


Creating a new component

Topic groups

You can create a new component two ways:

Using the Component wizard

Creating a component manually

You can use either of these methods to create a minimally functional component ready to install on the Component palette.After installing, you can add your new component to a form and test it at both design time and runtime. You can then add more features to the component, update the Component palette, and continue testing.

There are several basic steps that you perform whenever you create a new component. These steps are outlined below; other examples in this document assume that you know how to perform them.

1	Creating a unit for the new component.

2	Deriving your component from an existing component type.

3	Adding properties, methods, and events.

4	Registering your component with C++Builder.

5	Creating a Help file for your component and its properties, methods, and events.

6	Creating a package (a special dynamic-link library) so that you can install your component in the C++Builder IDE.

When you finish, the complete component includes these files:

A package (.BPL) or package collection (.PCE) file

A library (.LIB) for the package

A Borland import library (.BPI) file for the package

A compiled unit (.OBJ) file

A compiled resource (.RES) file for the palette map

A Help (.HLP) file

Creating a help file to instruct component users on how to use the component is optional. Including a help file is mandatory only if one is created.


Using the Component wizard

Topic groups

The Component wizard simplifies the initial stages of creating a component. When you use the Component wizard, you need to specify only these things:

The class from which it is derived

The class name for the new component

The Component palette page you want it to appear on

The name of the unit in which the component is created

The search path where the unit is found

The name of the package in which you want to place the component

The Component wizard performs the same tasks you would when creating a component manually:

Creating a unit (a .CPP file and its associated header)

Deriving the component

Declaring a new constructor

Registering the component

The Component wizard cannot add new components to an existing unit (consisting of a .CPP file and an associated header file). If you want to add new components, you must add them to the unit manually.

To start the Component wizard, choose one of these two methods:

Choose Component|New Component.

Choose File|New and double-click on Component

Fill in the fields in the Component wizard:

1	In the Ancestor Type field, specify the class from which you are deriving your new component.

2	In the Class Name field, specify the name of your new component class.

3	In the Palette Page field, specify the page on the Component palette on which you want the new component to be installed.

4	In the Unit file name field, specify the name of the unit you want the component class declared in.

5	If the unit is not on the search path, edit the search path in the Search Path field as necessary.

To place the component in a new or existing package, click Component|Install and use the dialog box that appears to specify a package.

Warning:	If you derive a component from a VCL class whose name begins with “custom” (such as TCustomControl), do not try to place the new component on a form until you have overridden any abstract methods in the original component. C++Builder cannot create instance objects of a class that has abstract properties or methods.

After you fill in the fields in the Component wizard, choose OK. C++Builder creates a new unit consisting of a .CPP file and an associated header file.

The .CPP file appears in the Code editor. It contains a constructor for the component and the Register function that registers the component, informing C++Builder which component to add to the component library and on which page of the Component palette it should appear. The file also contains an include statement that specifies the header file that was created. For example,

#include <vcl\vcl.h>

#pragma hdrstop

#include "NewComponent.h"

#pragma package(smart_init);

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

static inline TNewComponent *ValidCtrCheck()

{

return new TNewComponent(NULL);

}

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

__fastcall TNewComponent::TNewComponent(TComponent* Owner)

: TComponent(Owner)

{

}

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

namespace Newcomponent

{

void __fastcall PACKAGE Register()

{

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

RegisterComponents("Samples", classes, 0);

}

}

To open the header file in the Code editor, place your cursor on the header file name, click your right mouse button to display the context menu, and choose Open File at Cursor on the menu.

The header file contains the new class declaration, including a constructor declaration, and several #include statements to support the new class. For example,

#ifndef NewComponentH

#define NewComponentH

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

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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

class PACKAGE TNewComponent : public TComponent

{

private:

protected:

public:

__fastcall TNewComponent(TComponent* Owner);

__published:

};

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

#endif

Save the .CPP file, with a meaningful name, before proceeding.


Creating a component manually

Topic groups

The easiest way to create a new component is to use the Component wizard. You can, however, perform the same steps manually.

To create a component manually, follow these steps:

1	Creating a unit file

2	Deriving the component

3	Declaring a new constructor

4	Registering the component


Creating a unit

Topic groups

A C++Builder unit is comprised of a .CPP file and a .H file combination that is compiled into an .OBJ file. C++Builder uses units for a number of purposes. Every form has its own unit, and most components (or logical groups of components) have their own units as well.

.When you create a component, you either create a new unit for the component or add the new component to an existing unit.

To create a unit for a component, choose one of these methods:

Choose File|New Unit.

Choose File|New to display the New Items dialog box, select the New tab, select Unit, and choose OK.

C++Builder creates a .CPP file and a header file and displays the .CPP file in the Code editor. Save the file with a meaningful name.

To open the header file, place your cursor on header file name in the Code editor, right-click, and choose Open File at Cursor from the pop-up menu.

To open an existing unit, choose File|Open and select the source code unit that you want to add your component to.

Note:	When adding a component to an existing unit, make sure that the unit contains only component code. For example, adding component code to a unit that contains a form causes errors in the Component palette.

Once you have either a new or existing unit for your component, you can derive the component class.


Deriving the component

Topic groups	Example

Every component is a class derived from TComponent, from one of its more specialized descendants (such as TControl or TGraphicControl), or from an existing component class. The section How do you create components? describes which class to derive different kinds of components from.

Deriving classes is explained in more detail in The section Defining new classes.

To derive a component class, add a class declaration to the header file.

A simple component class is a nonvisual component descended directly from TComponent.


Example: Deriving a component

To create a simple component class, add the following class declaration to your header file:

class PACKAGE TNewComponent : public TComponent

{

};

The PACKAGE macro expands to a statement that allows classes to be imported and exported. You should also add the necessary include statements that specify the .HPP files needed by the new component. These are the most common include statements you need:

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

So far the new component does nothing different from TComponent. You have created a framework on which to build your new component.


Declaring a new constructor

Topic groups

Each new component must have a constructor that overrides the constructor of the class from which it was derived. When you write the constructor for your new component, it must always call the inherited constructor.

Within the class declaration, declare a virtual constructor in the public section of the class.You can learn more about the public section in the section Controlling access.

For example,

class PACKAGE TNewComponent : public TComponent

{

public:

  virtual __fastcall TNewComponent(TComponent* AOwner);

};

In the .CPP file, implement the constructor:

__fastcall TNewComponent::TNewComponent(TComponent* AOwner): TComponent(AOwner)

{

}

Within the constructor, you add the code you want to execute when the component is created.


Registering the component

Topic groups	Example

Registration is a simple process that tells C++Builder which components to add to its component library, and on which pages of the Component palette they should appear. For a more detailed discussion of the registration process, see Making components available at design time

To register a component,

1	Add a function named Register to the unit’s .CPP file, placing it within a namespace. The namespace is the name of the file the component is in, minus the file extension, with all lowercase letters except the first letter.

For example, this code exists within a Newcomp namespace, whereas Newcomp is the name of the .CPP file:

namespace Newcomp

{

  void __fastcall PACKAGE Register()

  {

  }

}

2	Within the Register function, declare an open array of type TComponentClass that holds the array of components you are registering. The syntax should look like this:

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

In this case, the array of classes contains just one component, but you can add all the components you want to register to the array.

3	Within the Register function, call RegisterComponents for each component you want to register.

RegisterComponents is a function that takes three parameters: the name of a Component palette page, the array of component classes, and the size – 1 of the component classes. If you’re adding a component to an existing registration, you can either add the new component to the set in the existing statement, or add a new statement that calls RegisterComponents.

You can register multiple components with just one RegisterComponents call if all components go on the same page on the Component palette.


Example: Registering a component

To register a component named TNewComponent and place it on the Samples page of the Component palette, add the following Register function to the .CPP file of the unit that declares TNewComponent:

namespace Newcomp

{

  void __fastcall PACKAGE Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

This Register call places TNewComponent on the Samples page of the Component palette.

Once you register a component, you can test the component, and finally install the component onto the Component palette. This is described in more detail in the section Installing a component on the Component palette.


Testing uninstalled components

Topic groups	Example

You can test the runtime behavior of a component before you install it on the Component palette. This is particularly useful for debugging newly created components, but the same technique works with any component, whether or not it is on the Component palette.

Testing your components without installing has the added benefit of generating compile-time errors that are seen only when the class is instantiated. For example, trying to create an instance of an abstract class yields an error directing you to the pure virtual that must be overloaded.

You test an uninstalled component by emulating the actions performed by C++Builder when the component is selected from the palette and placed on a form.

To test an uninstalled component, do the following:

1	Create a new application or open an existing one.

2	Choose Project|Add to Project to add the component unit to your project.

3	Include the .H file of the component unit in the header file of a form unit.

4	Add a data member to the form to represent the component.

This is one of the main differences between the way you add components and the way C++Builder does it. You add the data member to the public part at the bottom of the form’s class declaration. C++Builder would add it above, in the published part of the class declaration that it manages.

You should never add data members to the C++Builder-managed part of the form’s class declaration. The items in that part of the class declaration correspond to the items stored in the form file. Adding the names of components that do not exist on the form can render your form file invalid.

5	Construct the component in the form’s constructor.

When you call the component’s constructor, you must pass a parameter specifying the owner of the component (the component responsible for destroying the component when the time comes). You nearly always pass this as the owner. In a method, this is a reference to the class that contains the method. In this case, in the form’s OnCreate handler, this refers to the form.

6	Assign the Parent property.

Setting the Parent property is always the first thing to do after constructing a control. The parent is the component that visually contains the control, which is most often the form, but might be a group box or panel. Normally, you’ll set Parent to this, that is, the form. Always set Parent before setting other properties of the control.

7	Set any other component properties as desired.


Example: Testing uninstalled components

Suppose you want to test a new component of class TNewControl in a unit named NewCtrl. Create a new project, then follow the steps to end up with a header file for the form that looks like this:

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

#ifndef TestFormH

#define TestFormH

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

#include <vcl\Classes.hpp>

#include <vcl\Controls.hpp>

#include <vcl\StdCtrls.hpp>

#include <vcl\Forms.hpp>

#include "NewCtrl.h"

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

class TForm1 : public TForm

{

__published:          // IDE-managed Components

private:              // User declarations

public:               // User declarations

    TNewControl* NewControl1;

    __fastcall TForm1(TComponent* Owner);

};

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

extern TForm1 *Form1;

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

#endif

The #include statement that includes the NEWCTRL.H file assumes that the component resides in the directory of the current project or in a directory that is on the include path of the project.

This is the .CPP file of the form unit:

#include <vcl\vcl.h>

#pragma hdrstop

#include "TestForm.h"

#include "NewCtrl.h"

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

#pragma package(smart_init);

#pragma resource "*.dfm"

TForm1 *Form1;

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

static inline TNewControl *ValidCtrCheck()

{

    return new TNewControl(NULL);

}

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

__fastcall TForm1::TForm1(TComponent* Owner)

    : TForm(Owner)

{

    NewControl1 = new TNewControl(this);

    NewControl1->Parent = this;

    NewControl1->Left = 12;

}

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

namespace Newctrl

{

    void __fastcall PACKAGE Register()

    {

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

        RegisterComponents("Samples", classes, 0);

    }

}


Installing a component on the Component palette

Topic groups

There are two parts to the process of adding a component to the Component Palette. The first is making the source files available and the second using the Install Component dialog to actually add the component.

These two steps are

Component file locations

Adding the component


Component file locations
Where to place your component

Topic groups

All source files used by a component should be located in the same directory. These files include source code files (.CPP and .PAS) and binary files (.DFM, .RES, .RC, and .DCR). Header files (.H and .HPP) should be located in the Include directory (or in a location on the search path for the IDE or a project).

The process of adding a component results in the creation of a number of files. These files are automatically put in directories specified in the IDE environment options (use the menu command Tools|Environment Options, navigate to the Library tab page). The .LIB files in the BPI/LIB output directory. If adding the component entails creating a new package (as opposed to installing it into an existing package), the .BPL file is put in the BPL output directory and .BPI files in the BPI/LIB output directory.


Adding the component
Adding a component

Topic groups

To add components to the component library,

1	Choose Component|Install Component.

The Install Component dialog box appears.

2	Elect to install the new component into an existing or a new package by selecting the applicable tab page.

3	Enter the name of the .CPP file containing the new component or choose Browse to find the unit.

4	Adjust the search path if the .CPP file for the new component is not in the default location shown.

5	Enter the name of the package into which to install the component or choose Browse to find the package.

6	If the component is installed into a new package, optionally enter a meaningful description of the package.

7	Choose OK to close the Install Component dialog box. This compiles/rebuilds the package and installs the component on the Component Palette.

Note:	Newly installed components initially appear on the page of the Component palette that was specified by the component writer. You can move the components to a different page after they have been installed on the palette with the Component|Configure Palette dialog box.


Object-oriented programming for component writers: Overview
Object-oriented programming for component writers

Topic groups

If you have written applications with C++Builder, you know that a class contains both data and code, and that you can manipulate classes at design time and at runtime. In that sense, you’ve become a component user.

When you create new components, you deal with classes in ways that application developers never need to. You also try to hide the inner workings of the component from the developers who will use it. By choosing appropriate ancestors for your components, designing interfaces that expose only the properties and methods that developers need, and following the other guidelines in this chapter, you can create versatile, reusable components.

Before you start creating components, you should be familiar with these topics, which are related to object-oriented programming (OOP):

Defining new classes

Ancestors, descendants, and class hierarchies

Controlling access

Dispatching methods

Abstract class members

Classes and pointers


Defining new classes

Topic groups

The difference between component writers and application developers is that component writers create new classes while application developers manipulate instances of classes.

A class is essentially a type. As a programmer, you are always working with types and instances, even if you do not use that terminology. For example, you create variables of a type, such as int. Classes are usually more complex than simple data types, but they work the same way: By assigning different values to instances of the same type, you can perform different tasks.

For example, it is quite common to create a form containing two buttons, one labeled OK and one labeled Cancel. Each is an instance of the class TButton, but by assigning different values to their Caption properties and different handlers to their OnClick events, you make the two instances behave differently.


Deriving new classes

Topic groups

There are two reasons to derive a new class:

To change class defaults to avoid repetition

To add new capabilities to a class

In either case, the goal is to create reusable objects. If you design components with reuse in mind, you can save work later on. Give your classes usable default values, but allow them to be customized.


Changing class defaults to avoid repetition

Topic groups

Most programmers try to avoid repetition. Thus, if you find yourself rewriting the same lines of code over and over, you place the code in a function, or build a library of routines that you can use in many programs. The same reasoning holds for components. If you find yourself changing the same properties or making the same method calls, you can create a new component that does these things by default.

For example, suppose that each time you create an application, you add a dialog box to perform a particular operation. Although it is not difficult to recreate the dialog each time, it is also not necessary. You can design the dialog once, set its properties, and install a wrapper component associated with it onto the Component palette. By making the dialog into a reusable component, you not only eliminate a repetitive task, but you encourage standardization and reduce the likelihood of errors each time the dialog is recreated.

 Modifying an existing componentshows an example of changing a component’s default properties.


Adding new capabilities to a class

Topic groups

A common reason for creating new components is to add capabilities not found in existing components. When you do this, you derive the new component from either an existing component or an abstract base class, such as TComponent or TControl.

Derive your new component from the class that contains the closest subset of the features you want. You can add capabilities to a class, but you cannot take them away; so if an existing component class contains properties that you donot want to include in yours, you should derive from that component’s ancestor.

For example, if you want to add features to a list box, you could derive your component from TListBox. However, if you want to add new features but exclude some capabilities of the standard list box, you need to derive your component from TCustomListBox, the ancestor of TListBox. Then you can recreate (or make visible) only the list-box capabilities you want, and add your new features.

Customizing a grid shows an example of customizing an abstract component class.


Declaring a new component class

Topic groups	Example

In addition to standard components, C++Builder provides many abstract classes designed as bases for deriving new components. The How do you create components? topic shows the classes you can start from when you create your own components.

To declare a new component class, add a class declaration to the component’s header file.


Example: Declaring a new component class

Here is the declaration of a simple graphical component:

class PACKAGE TSampleShape : public TGraphicControl

{

public:

    virtual __fastcall TSampleShape(TComponent* Owner);

};

Do not forget to include the PACKAGE macro (defined in SysDefs.h), which allows classes to be imported and exported.

A finished component declaration includes property, data member, and method declarations before the final brace, but an empty declaration is also valid, and provides a starting point for the addition of component features.


Ancestors and descendants

Topic groups

Application developers take for granted that every control has properties named Top and Left that determine its position on the form. To them, it may not matter that all controls inherit these properties from a common ancestor, TControl. When you create a component, however, you must know which class to derive it from so that it inherits the appropriate features. And you must know everything that your control inherits, so you can take advantage of inherited features without recreating them.

The class from which you derive a component is called its immediate ancestor. Each component inherits from its immediate ancestor, and from the immediate ancestor of its immediate ancestor, and so forth. All of the classes from which a component inherits are called its ancestors; the component is a descendant of its ancestors.

Together, all the ancestor-descendant relationships in an application constitute a hierarchy of classes. Each generation in the hierarchy contains more than its ancestors, since a class inherits everything from its ancestors, then adds new properties and methods or redefines existing ones.

If you do not specify an immediate ancestor, C++Builder derives your component from the default ancestor, TObject. TObject is the ultimate ancestor of all classes in the object hierarchy.

The general rule for choosing which object to derive from is simple: Pick the object that contains as much as possible of what you want to include in your new object, but which does not include anything you do not want in the new object. You can always add things to your objects, but you cannot take things out.


Controlling access

Topic groups

There are five levels of access control—also called visibility—on properties, methods, and data members. Visibility determines which code can access which parts of the class. By specifying visibility, you define the interface to your components.

The table below shows the levels of visibility, from most restrictive to most accessible:

Visibility	Meaning	Used for
private	Accessible only to code in the unit where the class is defined.	Hiding implementation details.
protected	Accessible to code in the unit(s) where the class and its descendants are defined.	Defining the component writer’s interface.
public	Accessible to all code.	Defining the runtime interface.
__automated	Accessible to all code. Automation type information is generated.	OLE automation only.
__published	Accessible to all code and from the Object Inspector.	Defining the design-time interface.


Hiding implementation details

Topic groups	Example

Declaring part of a class as private makes that part invisible to code outside the class unless the functions are friends of the class. Private parts of a class are mostly useful for hiding details of implementation from users of the class. Because users of the class cannot access the private parts, you can change the internal implementation of the class without affecting user code.

If you do not specify any access control on a data member, method, or property, that part is private.


Example: Hiding implementation details

Here is an example shown in two parts that illustrates how declaring a data member as private prevents users from accessing information.

The first part is a form unit made up of a header file and a .CPP file that assigns a value to a private data member in the form’s OnCreate event handler. Because the event handler is declared within the TSecretForm class, the unit compiles without error.

#ifndef HideInfoH

#define HideInfoH

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

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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

class PACKAGE TSecretForm : public TForm

{

__published:    // IDE-managed Components

    void __fastcall FormCreate(TObject *Sender);

private:

    int FSecretCode;                                  // declare a private data member

public:         // User declarations

  __fastcall TSecretForm(TComponent* Owner);

};

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

extern TSecretForm *SecretForm;

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

#endif

This is the accompanying .CPP file:

#include <vcl.h>

#pragma hdrstop

#include "hideInfo.h"

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

#pragma package(smart_init);

#pragma resource "*.dfm"

TSecretForm *SecretForm;

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

__fastcall TSecretForm::TSecretForm(TComponent* Owner)

  : TForm(Owner)

{

}

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

void __fastcall TSecretForm::FormCreate(TObject *Sender)

{

  FSecretCode = 42;                             // this compiles correctly

}

The second part of this example is another form unit that attempts to assign a value to the FSecretCode data member in the SecretForm form. This is the header file for the unit:

#ifndef TestHideH

#define TestHideH

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

#include <vcl\SysUtils.hpp>

#include <vcl\Controls.hpp>

#include <vcl\Classes.hpp>

#include <vcl\Forms.hpp>

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

class PACKAGE TTestForm : public TForm

{

__published:    // IDE-managed Components

    void __fastcall FormCreate(TObject *Sender);

public:         // User declarations

    __fastcall TTestForm(TComponent* Owner);

};

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

extern TTestForm *TestForm;

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

#endif

This is the accompanying .CPP file. Because the OnCreate event handler attempts to assign a value to a data member private to the SecretForm form, the compilation fails with the error message ‘TSecretForm::FSecretCode’ is not accessible.

#include <vcl.h>

#pragma hdrstop

#include "testHide.h"

#include "hideInfo.h"

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

#pragma package(smart_init);

#pragma resource "*.dfm"

TTestForm *TestForm;

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

__fastcall TTestForm::TTestForm(TComponent* Owner)

  : TForm(Owner)

{

}

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

void __fastcall TTestForm::FormCreate(TObject *Sender)

{

  SecretForm->FSecretCode = 13;                 //compiler stops here with error message

}

Although a program using the HideInfo unit can use classes of type TSecretForm, it cannot access the FSecretCode data member in any of those classes.


Defining the developer's interface

Topic groups

Declaring part of a class as protected makes that part visible only to the class itself and its descendants.

You can use protected declarations to define a component writer’s interface to the class. Application units do not have access to the protected parts, but derived classes do. This means that component writers can change the way a class works without making the details visible to application developers.


Defining the runtime interface

Topic groups	Example

Declaring part of a class as public makes that part visible to any code that has access to the class as a whole.

Public parts are available at runtime to all code, so the public parts of a class define its runtime interface. The runtime interface is useful for items that are not meaningful or appropriate at design time, such as properties that depend on runtime input or which are read-only. Methods that you intend for application developers to call must also be public.


Example: Defining the runtime interface

Here is an example that shows two read-only properties declared as part of a component’s runtime interface:

class PACKAGE TSampleComponent : public TComponent

{

private:

    int FTempCelsius;                                  // implementation details are private

    int GetTempFahrenheit();

public:

    ...

    __property int TempCelsius = {read=FTempCelsius};          // properties are public

    __property int TempFahrenheit = {read=GetTempFahrenheit};

};

This is the GetTempFahrenheit method in the .CPP file:

int TSampleComponent::GetTempFahrenheit()

{

  return FTempCelsius * (9 / 5) + 32;

}


Defining the design-time interface

Topic groups	Example

Declaring part of a class as published makes that part public and also generates runtime type information. Among other things, runtime type information allows the Object Inspector to access properties and events.

Because they show up in the Object Inspector, the published parts of a class define that class’s design-time interface. The design-time interface should include any aspects of the class that an application developer might want to customize at design time, but must exclude any properties that depend on specific information about the runtime environment.

Read-only properties cannot be part of the design-time interface because the application developer cannot assign values to them directly. Read-only properties should therefore be public, rather than published.


Example: Defining the design-time interface

Here is an example of a published property called Temperature. Because it is published, it appears in the Object Inspector at design time.

class PACKAGE TSampleComponent : public TComponent

{

private:

    int FTemperature;

  ...

__published:

    __property int Temperature = {read=FTemperature, write=FTemperature};

};


Dispatching methods

Topic groups

Dispatch is the term used to describe how your application determines which class method should be invoked when it encounters a class method call. When you write code that calls a class method, it looks like any other function call. Classes, however, have two different ways of dispatching methods.

The two types of method dispatch are

Regular (not virtual) methods

Virtual methods


Regular methods

Topic groups

Class methods are regular (or nonvirtual) unless you specifically declare them as virtual, or unless they override a virtual method in a base class. The compiler can determine the exact address of a regular class member at compile time. This is known as compile-time binding.

A base class regular method is inherited by derived classes. In the following example, an object of type Derived can call the method Regular() as it were its own method. Declaring a method in a derived class with the same name and parameters as a regular method in the class’s ancestor replaces the ancestor’s method. In the following example, when d->AnotherRegular() is called, it is being dispatched to the Derived class replacement for AnotherRegular().

class Base

{

public:

    void Regular();

    void AnotherRegular();

    virtual void Virtual();

};

class Derived : public Base

{

public:

    void AnotherRegular();          // replaces Base::AnotherRegular()

    void Virtual();                 // overrides Base::Virtual()

};

void FunctionOne()

{

  Derived *d;

  d = new Derived;

  d->Regular();                   // Calling Regular() as it were a member of Derived

                                  // The same as calling d->Base::Regular()

  d->AnotherRegular();            // Calling the redefined AnotherRegular(), ...

                                  // ... the replacement for Base::AnotherRegular()

  delete d;

}

void FunctionTwo(Base *b)

{

  b->Virtual();

  b->AnotherRegular();

}


Virtual methods

Topic groups

Unlike regular methods, which are bound at compile time, virtual methods are bound at runtime. The virtual mechanism of C++ allows a method to be called depending on the class type that is being used to invoke the method.

In the previous example, if you were to call FunctionTwo() with a pointer to a Derived object, the function Derived::Virtual() would be called. The virtual mechanism dynamically inspects the class type of the object you passed at runtime and dispatches the appropriate method. But the call to the regular functionb->AnotherRegular() will always call Base::AnotherRegular() because the address of AnotherRegular() was determined at compile time.

To declare a new virtual method, preface the method declaration with the virtual keyword.

When the compiler encounters the virtual keyword, it creates an entry in the class’s virtual method table (VMT). The VMT holds the addresses of all the virtual methods in a class. This lookup table is used at runtime to determine thatb->Virtual should call Derrived::Virtual(), and not Base::Virtual().

When you derive a new class from an existing class, the new class receives its own VMT, which includes the entries from its ancestor’s VMT, plus any additional virtual methods declared in the new class. In addition, the descendant class can override any of its inherited virtual methods.


Overriding methods

Topic groups	Example

Overriding methods means extending or refining an ancestor’s method, rather than replacing it. To override a method in a descendant class, redeclare the method in the derived class, ensuring that the number and type of arguments are the same.


Example: Overriding methods

The following code shows the declaration of two simple components. The first declares two methods, each with a different kind of dispatching. The other, derived from the first, replaces the nonvirtual method and overrides the virtual method.

class PACKAGE TFirstComponent : public TComponent

{

public:

  void Move();                               // regular method

  virtual void Flash();                      // virtual method

};

class PACKAGE TSecondComponent : public TFirstComponent

{

public:

  void Move();                // declares new method "hiding" TFirstComponent::Move()

  void Flash();               // overrides virtual TFirstComponent::Flash in TFirstComponent

};


Abstract class members

Topic groups

When a method is declared as abstract in an ancestor class, you must surface it (by redeclaring and implementing it) in any descendant component before you can use the new component in applications. C++Builder cannot create instances of a class that contains abstract members. For more information about surfacing inherited parts of classes, see Creating properties and Creating methods.


Classes and pointers

Topic groups

Every class (and therefore every component) is really a pointer. The status of classes as pointers becomes important when you pass a class as a parameter. In general, you should pass classes by value rather than by reference. The reason is that classes are already pointers, which are references; passing a class by reference amounts to passing a reference to a reference.


Creating properties: Overview
Creating properties

Topic groups

Properties are the most visible parts of components. The application developer can see and manipulate them at design time and get immediate feedback as the components react in the Form designer. Well-designed properties make your components easier for others to use and easier for you to maintain.

To make the best use of properties in your components, you should understand the following:

Why create properties?

Types of properties

Publishing inherited properties

Defining properties

Creating array properties

Storing and loading properties


Why create properties?

Topic groups

From the application developer’s standpoint, properties look like variables. Developers can set or read the values of properties as if they were data members. (About the only thing you can do with a variable that you cannot do with a property is pass it as an argument to a method by reference.)

Properties provide more power than simple data members because

Application developers can set properties at design time. Unlike methods, which are available only at runtime, properties let the developer customize components before running an application. Properties can appear in the Object Inspector, which simplifies the programmer’s job; instead of handling several parameters to construct an object, you let C++ Builder read the values from the Object Inspector. The Object Inspector also validates property assignments as soon as they are made.

Properties can hide implementation details. For example, data stored internally in an encrypted form can appear unencrypted as the value of a property; although the value is a simple number, the component may look up the value in a database or perform complex calculations to arrive at it. Properties let you attach complex effects to outwardly simple assignments; what looks like an assignment to a data member can be a call to a method which implements elaborate processing.

Properties can be virtual. Hence, what looks like a single property to an application developer may be implemented differently in different components.

A simple example is the Top property of all controls. Assigning a new value to Top does not just change a stored value; it repositions and repaints the control. And the effects of setting a property need not be limited to an individual component; for example, setting the Down property of a speed button to true sets Down property of all other speed buttons in its group to false.


Types of properties

Topic groups

A property can be of any type. Different types are displayed differently in the Object Inspector, which validates property assignments as they are made at design time.

Property type	Object Inspector treatment
Simple	Numeric, character, and string properties appear as numbers, characters, and strings. The application developer can edit the value of the property directly.
Enumerated	Properties of enumerated types (including Boolean) appear as editable strings. The developer can also cycle through the possible values by double-clicking the value column, and there is a drop-down list that shows all possible values.
Set	Properties of set types appear as sets. By double-clicking on the property, the developer can expand the set and treat each element as a Boolean value (true if it is included in the set).
Object	Properties that are themselves classes often have their own property editors, specified in the component’s registration procedure. If the class held by a property has its own published properties, the Object Inspector lets the developer to expand the list (by double-clicking) to include these properties and edit them individually. Object properties must descend from TPersistent.
Array	Array properties must have their own property editors; the Object Inspector has no built-in support for editing them. You can specify a property editor when you register your components.


Publishing inherited properties

Topic groups	Example

All components inherit properties from their ancestor classes. When you derive a new component from an existing one, your new component inherits all the properties of its immediate ancestor. If you derive from one of the abstract classes, many of the inherited properties are either protected or public, but not published.

To make a protected or public property available at design time in the Object Inspector, you must redeclare the property as published. Redeclaring means adding a declaration for the inherited property to the declaration of the descendant class.


Example: Publishing an inherited property

If you derive a component from TWinControl, for example, it inherits the protected Ctl3D property. By redeclaring Ctl3D in your new component, you can change the level of protection to either public or published.

The following code shows a redeclaration of Ctl3D as published, making it available at design time.

class PACKAGE TSampleComponent : public TWinControl

{

__published:

    __property Ctl3D;

};

When you redeclare a property, you specify only the property name, not the type and other information described in Defining properties. You can also declare new default values and specify whether to store the property.

Redeclarations can make a property less restricted, but not more restricted. Thus you can make a protected property public, but you cannot hide a public property by redeclaring it as protected.


Defining component properties

Topic groups

This section shows how to declare new properties and explains some of the conventions followed in the standard components. Topics include

The property declaration

Internal data storage

Direct access

Access methods

Default property values


The property declaration

Topic groups	Example

A property is declared in the declaration of its component class. To declare a property, you specify three things:

The name of the property.

The type of the property.

The methods used to read and write the value of the property. If no write method is declared, the property is read-only.

Properties declared in a __published section of the component’s class declaration are editable in the Object Inspector at design time. Properties declared in a public section are available at runtime and can be read or set in program code.


Example: Property declaration

Here is a typical declaration for a property called Count.

class PACKAGE TYourComponent : public TComponent

{

private:

    int FCount;                                               // data member for storage

    int __fastcall GetCount();                                // read method

    void __fastcall SetCount( int ACount );                   // write method

public:

    __property int Count = {read=GetCount, write=SetCount};   // property declaration

  ...

};


Internal data storage (properties)

Topic groups

There are no restrictions on how you store the data for a property. In general, however, C++Builder components follow these conventions:

Property data is stored in class data members.

The data members used to store property data are private and should be accessed only from within the component itself. Derived components should use the inherited property; they do not need direct access to the property’s internal data storage.

Identifiers for these data members consist of the letter F followed by the name of the property. For example, the raw data for the Width property defined in TControl is stored in a data member called FWidth.

The principle that underlies these conventions is that only the implementation methods for a property should access the data behind it. If a method or another property needs to change that data, it should do so through the property, not by direct access to the stored data. This ensures that the implementation of an inherited property can change without invalidating derived components.


Direct access

Topic groups	Example

The simplest way to make property data available is direct access. That is, the read and write parts of the property declaration specify that assigning or reading the property value goes directly to the internal-storage data member without calling an access method. Direct access is useful when you want to make a property available in the Object Inspector but changes to its value trigger no immediate processing.

It is common to have direct access for the read part of a property declaration but use an access method for the write part. This allows the status of the component to be updated when the property value changes.


Example: A property that uses direct access

The following component-type declaration shows a property that uses direct access for both the read and the write parts.

class PACKAGE TSampleComponent : public TComponent

{

private:                                        // internal storage is private

    bool FReadOnly;                             // declare data member to hold value

  ...

__published:                                    // make property available at design time

    __property bool ReadOnly = {read=FReadOnly, write=FReadOnly};

};


Access methods (properties)

Topic groups	Example

You can specify an access method instead of a data member in the read and write parts of a property declaration. Access methods should be protected, and are usually declared as virtual; this allows descendant components to override the property’s implementation.

Avoid making access methods public. Keeping them protected ensures that application developers do not inadvertently modify a property by calling one of these methods.


Example: Property access methods

Here is a class that declares three properties using the index specifier, which allows all three properties to have the same read and write access methods:

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

  int __fastcall GetDateElement(int Index);    // note Index parameter

  void __fastcall SetDateElement(int Index, int Value);

public:

  __property int Day = {read=GetDateElement, write=SetDateElement, index=3, nodefault};

  __property int Month = {read=GetDateElement, write=SetDateElement, index=2, nodefault};

  __property int Year = {read=GetDateElement, write=SetDateElement, index=1, nodefault};

};

Because each element of the date (day, month, and year) is an int, and because setting each requires encoding the date when set, the code avoids duplication by sharing the read and write methods for all three properties. You need only one method to read a date element, and another to write the date element.

Here is the read method that obtains the date element:

int __fastcall TSampleCalendar::GetDateElement(int Index)

{

  unsigned short AYear, AMonth, ADay;

  int result;

  FDate.DecodeDate(&AYear, &AMonth, &Aday); // break date into elements

  switch (Index)

  {

    case 1: result = AYear;  break;

    case 2: result = AMonth; break;

    case 3: result = ADay;   break;

    default: result = -1;

  }

  return result;

}

This is the write method that sets the appropriate date element:

void __fastcall TSampleCalendar::SetDateElement(int Index, int Value)

{

  unsigned short AYear, AMonth, ADay;

  if (Value > 0) // all elements must be positive

  {

    FDate.DecodeDate(&AYear, &AMonth, &ADay); // get date elements

    switch (Index)

    {

      case 1: AYear = Value;  break;

      case 2: AMonth = Value; break;

      case 3: ADay = Value;   break;

      default: return;

    }

 }

 FDate = TDateTime(AYear, AMonth, ADay); // encode the modified date

 Refresh();// update the visible calendar

}


The read method

Topic groups

The read method for a property is a function that takes no parameters (except as noted below) and returns a value of the same type as the property. By convention, the function’s name is Get followed by the name of the property. For example, the read method for a property called Count would be GetCount. The read method manipulates the internal storage data as needed to produce the value of the property in the appropriate type.

The only exceptions to the no-parameters rule are for array properties and properties that use index specifiers (see Creating array properties), both of which pass their index values as parameters. (Use index specifiers to create a single read method that is shared by several properties.)

If you do not declare a read method, the property is write-only. Write-only properties are seldom used.


The write method

Topic groups

The write method for a property is a member function that takes a single parameter (except as noted below) of the same type as the property. The parameter can be passed by reference or by value, and can have any name you choose. By convention, the write method’s name is Set followed by the name of the property. For example, the write method for a property called Count would be SetCount. The value passed in the parameter becomes the new value of the property; the write method must perform any manipulation needed to put the appropriate data in the property’s internal storage.

The only exceptions to the single-parameter rule are for array properties and properties that use index specifiers, both of which pass their index values as a second parameter. (Use index specifiers to create a single write method that is shared by several properties.)

If you do not declare a write method, the property is read-only. For a published property to be usable at design time, it must be defined as read/write.

Write methods commonly test whether a new value differs from the current value before changing the property. For example, here is a simple write method for an integer property called Count that stores its current value in a data member called FCount.

void __fastcall TMyComponent::SetCount( int Value )

{

  if ( Value != FCount )

  {

    FCount = Value;

    Update();

  }


Default property values

Topic groups

When you declare a property, you can specify a default value for it. C++Builder uses the default value to determine whether to store the property in a form file. If you do not specify a default value for a property, C++Builder always stores the property.

To declare a default value for a property, append an equal sign after the property name and a set of braces that holds the default keyword and the default value. For example,

__property bool IsTrue = {default=true};

Note:	Declaring a default value does not set the property to that value. The component’s constructor method should initialize property values when appropriate. However, since objects always initialize their data members to 0, it is not strictly necessary for the constructor to set integer properties to 0, string properties to null, or Boolean properties to false.


Specifying no default value

Topic groups	Example

When redeclaring a property, you can specify that the property has no default value, even if the inherited property specified one.

To designate a property as having no default value, append an equal sign after the property name and a set of braces that holds the nodefault keyword. For example,

__property int NewInteger = {nodefault};

When you declare a property for the first time, there is no need to include nodefault. The absence of a declared default value means that there is no default.


Example: A property with a default value

Here is the declaration of a component that includes a single Boolean property named IsTrue with a default value of true, including the constructor that sets the default value.

class PACKAGE TSampleComponent : public TComponent

{

private:

     bool FIsTrue;

public:

    virtual __fastcall TSampleComponent( TComponent* Owner );

__published:

    __property bool IsTrue = {read=FIsTrue, write=FIsTrue, default=true};

};

__fastcall TSampleComponent::TSampleComponent ( TComponent* Owner )

  : TComponent ( Owner )

{

  FIsTrue = true;

}


Creating array properties

Topic groups	Example

Some properties lend themselves to being indexed like arrays. For example, the Lines property of TMemo is an indexed list of the strings that make up the text of the memo; you can treat it as an array of strings. Lines provides natural access to a particular element (a string) in a larger set of data (the memo text).

Array properties are declared like other properties, except that

The declaration includes one or more indexes with specified types. The indexes can be of any type.

The read and write parts of the property declaration, if specified, must be methods. They cannot be data members.

The read and write methods for an array property take additional parameters that correspond to the indexes. The parameters must be in the same order and of the same type as the indexes specified in the declaration.

There are a few important differences between array properties and arrays. Unlike the index of an array, the index of an array property does not have to be an integer type. You can index a property on a string, for example. In addition, you can reference only individual elements of an array property, not the entire range of the property.


Example: Array property

The following example shows the declaration of a property that returns a string based on an integer index.

class PACKAGE TDemoComponent : public TComponent

{

private:

    System::AnsiString __fastcall GetNumberSize(int Index);

public:

    __property System::AnsiString NumberSize[int Index] = {read=GetNumberSize};

  ...

};

This is the GetNumberSize method in the .CPP file:

System::AnsiString __fastcall TDemoComponent::GetNumberSize(int Index)

{

  System::AnsiString Result;

  switch (Index)

  {

    case 0:

      Result = "Zero";

      break;

    case 100:

      Result = "Medium";

      break;

    case 1000:

      Result = "Large";

      break;

    default: Result = "Unknown size";

  }

  return Result;

}


Storing and loading properties

Topic groups

C++Builder stores forms and their components in form (.DFM) files. A form file is a binary representation of the properties of a form and its components. When C++Builder developers add the components you write to their forms, your components must have the ability to write their properties to the form file when saved. Similarly, when loaded into C++Builder or executed as part of an application, the components must restore themselves from the form file.

Most of the time you will not need to do anything to make your components work with form files because the ability to store a representation and load from it are part of the inherited behavior of components. Sometimes, however, you might want to alter the way a component stores itself or the way it initializes when loaded; so you should understand the underlying mechanism.

These are the aspects of property storage you need to understand:

Using the store-and-load mechanism

Specifying default values

Determining what to store

Initializing after loading


Using the store-and-load mechanism

Topic groups

The description of a form consists of a list of the form’s properties, along with similar descriptions of each component on the form. Each component, including the form itself, is responsible for storing and loading its own description.

By default, when storing itself, a component writes the values of all its public and published properties that differ from their default values, in the order of their declaration. When loading itself, a component first constructs itself, setting all properties to their default values, then reads the stored, non-default property values.

This default mechanism serves the needs of most components, and requires no action at all on the part of the component writer. There are several ways you can customize the storing and loading process to suit the needs of your particular components, however.


Specifying default values

Topic groups	Example

C++Builder components save their property values only if those values differ from the defaults. If you do not specify otherwise, C++Builder assumes a property has no default value, meaning the component always stores the property, whatever its value.

To specify a default value for a property,

1	Add an equal sign (=) after the property name.

2	After the equal sign, add braces({}).

3	Within the braces, type the keyword default, followed by another equal sign.

4	Specify the new default value.

For example,

__property Alignment = {default=taCenter};

You can also specify a default value when redeclaring a property. In fact, one reason to redeclare a property is to designate a different default value.

Note:	Specifying the default value does not automatically assign that value to the property on creation of the object. You must make sure that the component’s constructor assigns the necessary value. A property whose value is not set by a component’s constructor assumes a zero value—that is, whatever value the property assumes when its storage memory is set to 0. Thus numeric values default to 0, Boolean values to false, pointers to NULL, and so on. If there is any doubt, assign a value in the constructor method.


Example: Specifying a default value

The following code shows a component declaration that specifies a default value for the Align property and the implementation of the component’s constructor that sets the default value. In this case, the new component is a special case of the standard panel component that will be used for status bars in a window, so its default alignment should be to the bottom of its owner.

class PACKAGE TMyStatusBar : public TPanel

{

public:

  virtual __fastcall TMyStatusBar(TComponent* AOwner);

__published:

  __property Align = {default=alBottom};

};

The constructor of the TMyStatusBar component is in the .CPP file:

__fastcall TMyStatusBar::TMyStatusBar (TComponent* AOwner)

  : TPanel(AOwner)

{

   Align = alBottom;

}


Determining what to store

Topic groups	Example

You can control whether C++Builder stores each of your components’ properties. By default, all properties in the published part of the class declaration are stored. You can choose not to store a given property at all, or you can designate a function that determines at runtime whether to store the property.

To control whether C++Builder stores a property,

1	Add an equal sign (=) after the property name.

2	After the equal sign, add braces({}).

3	Within the braces, type the keyword stored, followed by true, false, or the name of a Boolean method.


Example: Stored properties

The following code shows a component that declares three new properties. One is always stored, one is never stored, and the third is stored depending on the value of a Boolean method:

class PACKAGE TSampleComponent : public TComponent

{

protected:

  bool __fastcall StoreIt();

public:

  __property int Important = {stored=true};         // always stored

   ...

__published:

  __property int Unimportant = {stored=false};      // never stored

  __property int Sometimes = {stored=StoreIt};      // storage depends on function value

};


Initializing after loading

Topic groups

After a component reads all its property values from its stored description, it calls a virtual method named Loaded, which performs any required initializations. The call to Loaded occurs before the form and its controls are shown, so you do not need to worry about initialization causing flicker on the screen.

To initialize a component after it loads its property values, override the Loaded method.

Note:	The first thing to do in any Loaded method is call the inherited Loaded method. This ensures that any inherited properties are correctly initialized before you initialize your own component.


Creating events: Overview
Creating events

Topic groups

An event is a link between an occurrence in the system (such as a user action or a change in focus) and a piece of code that responds to that occurrence. The responding code is an event handler, and is nearly always written by the application developer. Events let application developers customize the behavior of components without having to change the classes themselves. This is known as delegation.

Events for the most common user actions (such as mouse actions) are built into all the standard components, but you can also define new events. To create events in a component, you need to understand the following:

What are events?

Implementing the standard events

Defining your own events

Events are implemented as properties, so you should already be familiar with the material in Creating properties before you attempt to create or change a component’s events.


What are events?

Topic groups

An event is a mechanism that links an occurrence to some code. More specifically, an event is a closure that points to a method in a specific class instance.

From the application developer’s perspective, an event is just a name related to a system occurrence, such as OnClick, to which specific code can be attached. For example, a push button called Button1 has an OnClick method. By default, C++Builder generates an event handler called Button1Click in the form that contains the button and assigns it to OnClick. When a click event occurs in the button, the button calls the method assigned to OnClick, in this case, Button1Click.To write an event, you need to understand the following:

Events are closures.

Events are properties.

Event types are closure types.

Event handlers have a return type of void

Event handlers are optional.


Events are closures

Topic groups

C++Builder uses closures to implement events. A closure is a special pointer type that points to a specific method in a specific class instance. As a component writer, you can treat the closure as a place holder: your code detects that an event occurs, so you call the method (if any) specified by the user for that event.

Closures maintain a hidden pointer to a class instance. When the user assigns a handler to a component’s event, the assignment is not just to a method with a particular name, but rather to a specific method of a specific class instance. That instance is usually the form that contains the component, but it need not be.


Calling the click-event handler

Topic groups

All controls, for example, inherit a virtual method called Click for handling click events:

virtual void __fastcall Click(void);

The implementation of Click calls the user’s click-event handler, if one exists. If the user has assigned a handler to a control’s OnClick event, clicking the control results in that method being called. If no handler is assigned, nothing happens.


Events are properties

Topic groups

Components use properties to implement their events. Unlike most other properties, events do not use methods to implement their read and write parts. Instead, event properties use a private data member the same type as the property.

By convention, the data member’s name is the same as the name of the property, but preceded by the letter F. For example, the OnClick closure is stored in a data member called FOnClick of type TNotifyEvent, and the declaration of the OnClick event property looks like this:

class PACKAGE TControl : public TComponent

{

private:

    TNotifyEvent FOnClick;

  ...

protected:

    __property TNotifyEvent OnClick = {read=FOnClick, write=FOnClick};

  ...

};

To learn about TNotifyEvent and other event types, see the next section, Event types are closure types.

As with any other property, you can set or change the value of an event at runtime. The main advantage to having events be properties, however, is that component users can assign handlers to events at design time, using the Object Inspector.


Event types are closure types

Topic groups

Because an event is a pointer to an event handler, the type of the event property must be a closure type. Similarly, any code to be used as an event handler must be an appropriately typed method of a class.

To be compatible with an event of a given type, an event-handler method must have the same number and type of parameters, in the same order, passed in the same way.

C++Builder defines closures for all its standard events. When you create your own events, you can use an existing closure if that is appropriate, or define one of your own.


Event handlers have a return type of void

Topic groups

Event handlers must have a return type of void only. Even though the handler can return only void, you can still get information back from the user’s code by passing arguments by reference. When you do this, make sure you assign a valid value to the argument before calling the handler so you do not require the user’s code to change the value.

An example of passing arguments by reference to an event handler is the key-pressed event, of type TKeyPressEvent. TKeyPressEvent defines two arguments, one to indicate which object generated the event, and one to indicate which key was pressed:

typedef void __fastcall (__closure *TKeyPressEvent)(TObject *Sender, Char &Key);

Normally, the Key parameter contains the character pressed by the user. Under certain circumstances, however, the user of the component might want to change the character. One example might be to force all characters to uppercase in an edit control. In that case, the user could define the following handler for keystrokes:

void __fastcall TForm1::Edit1KeyPress(TObject *Sender, Char &Key)

{

  Key = UpCase(Key);

}

You can also use arguments passed by reference to let the user override the default handling.


Event handlers are optional

Topic groups

When creating events, remember that developers using your components may not attach handlers to them. This means that your component should not fail or generate errors simply because there is no handler attached to a particular event. (The mechanics of calling handlers and dealing with events that have no attached handler are explained in Calling the event.)

Events happen almost constantly in a Windows application. Just moving the mouse pointer across a visual component makes Windows send numerous mouse-move messages, which the component translates into OnMouseMove events. In most cases, developers do not want to handle the mouse-move events, and this should not cause a problem. So the components you create should not require handlers for their events.

Moreover, application developers can write any code they want in an event handler. The components in the VCL have events written in such a way as to minimize the chance of an event handler generating errors. Obviously, you cannot protect against logic errors in application code, but you can ensure that data structures are initialized before calling events so that application developers do not try to access invalid data.


Implementing the standard events

Topic groups

The controls that come with C++Builder inherit events for the most common Windows occurrences. These are called the standard events. Although all these events are built into the controls, they are often protected, meaning developers cannot attach handlers to them. When you create a control, you can choose to make events visible to users of your control.

There are three things you need to consider when incorporating the standard events into your controls:

Identifying standard events

Making events visible

Changing the standard event handling


Identifying standard events

Topic groups

There are two categories of standard events: those defined for all controls and those defined only for the standard windowed controls.

Standard events for all controls

The most basic events are defined in the class TControl. All controls, whether windowed, graphical, or custom, inherit these events. The following table lists events available in all controls:

OnClick	OnDragDrop	OnEndDrag	OnMouseMove
OnDblClick	OnDragOver	OnMouseDown	OnMouseUp
The standard events have corresponding protected virtual methods declared in TControl, with names that correspond to the event names. For example, OnClick events call a method named Click, and OnEndDrag events call a method named DoEndDrag.

Standard events for standard controls

In addition to the events common to all controls, standard windowed controls (those that descend from TWinControl) have the following events:

OnEnter	OnKeyDown	OnKeyPress
OnKeyUp	OnExit
Like the standard events in TControl, the windowed-control events have corresponding methods.


Making events visible

Topic groups	Example

The declarations of the standard events in TControl and TWinControl are protected, as are the methods that correspond to them. If you are inheriting from one of these abstract classes and want to make their events accessible at runtime or design time, you need to redeclare the events as either public or published.

Redeclaring a property without specifying its implementation keeps the same implementation methods, but changes the protection level. You can, therefore, take an event that is defined in TControl but not made visible, and surface it by declaring it as public or published.


Example: Making an event visible

For example, to create a component that surfaces the OnClick event at design time, you would add the following to the component’s class declaration.

class PACKAGE TMyControl : public TCustomControl

{

  ...

__published:

    __property OnClick;             // Makes OnClick available in the Object Inspector

};


Changing the standard event handling

Topic groups	Example

If you want to change the way your component responds to a certain kind of event, you might be tempted to write some code and assign it to the event. As an application developer, that is exactly what you would do. But when you are creating a component, you must keep the event available for developers who use the component.

This is the reason for the protected implementation methods associated with each of the standard events. By overriding the implementation method, you can modify the internal event handling; and by calling the inherited method you can maintain the standard handling, including the event for the application developer’s code.

The order in which you call the methods is significant. As a rule, call the inherited method first, allowing the application developer’s event-handler to execute before your customizations (and in some cases, to keep the customizations from executing). There may be times when you want to execute your code before calling the inherited method, however. For example, if the inherited code is somehow dependent on the status of the component and your code changes that status, you should make the changes and then allow the user’s code to respond to them.


Example: Changing the standard event handler

Supposeyou are writing a component and you want to modify the way it responds to mouse clicks. Instead of assigning a handler to the OnClick event as a application developer would, you override the protected method Click:

void __fastcall TMyControl::Click()

{

  TWinControl::Click();        // perform standard handling, including calling handler

  // your customizations go here

}


Defining your own events

Topic groups

Defining entirely new events is relatively unusual. There are times, however, when a component introduces behavior that is entirely different from that of any other component, so you will need to define an event for it.

There are the issues you will need to consider when defining an event:

Triggering the event

Defining the handler type

Declaring the event

Calling the event


Triggering the event

Topic groups

You need to know what triggers the event. For some events, the answer is obvious. For example, a mouse-down event occurs when the user presses the left button on the mouse and Windows sends a WM_LBUTTONDOWN message to the application. Upon receiving that message, a component calls its MouseDown method, which in turn calls any code the user has attached to the OnMouseDown event.

But some events are less clearly tied to specific external occurrences. For example, a scroll bar has an OnChange event, which is triggered by several kinds of occurrence, including keystrokes, mouse clicks, and changes in other controls. When defining your events, you must ensure that all the appropriate occurrences call the proper events.


Two kinds of events

Topic groups

There are two kinds of occurrence you might need to provide events for: user interactions and state changes. User-interaction events are nearly always triggered by a message from Windows, indicating that the user did something your component may need to respond to. State-change events may also be related to messages from Windows (focus changes or enabling, for example), but they can also occur through changes in properties or other code. You have total control over the triggering of the events you define. Define the events with care so that developers are able to understand and use them.


Defining the handler type

Topic groups

Once you determine when the event occurs, you must define how you want the event handled. This means determining the type of the event handler. In most cases, handlers for events you define yourself are either simple notifications or event-specific types. It is also possible to get information back from the handler.

Simple notifications

A notification event is one that only tells you that the particular event happened, with no specific information about when or where. Notifications use the type TNotifyEvent, which carries only one parameter, the sender of the event. All a handler for a notification “knows” about the event is what kind of event it was, and what component the event happened to. For example, click events are notifications. When you write a handler for a click event, all you know is that a click occurred and which component was clicked.

Notification is a one-way process. There is no mechanism to provide feedback or prevent further handling of a notification.

Event-specific handlers

In some cases, it is not enough to know which event happened and what component it happened to. For example, if the event is a key-press event, it is likely that the handler will want to know which key the user pressed. In these cases, you need handler types that include parameters for additional information.

If your event was generated in response to a message, it is likely that the parameters you pass to the event handler come directly from the message parameters.

Returning information from the handler

Because all event handlers return void only, the only way to pass information back from a handler is through a parameter passed by reference. Your components can use such information to determine how or whether to process an event after the user’s handler executes.

For example, all the key events (OnKeyDown, OnKeyUp, and OnKeyPress) pass by reference the value of the key pressed in a parameter named Key. The event handler can change Key so that the application sees a different key as being involved in the event. This is a way to force typed characters to uppercase, for example.


Declaring the event

Topic groups

Once you have determined the type of your event handler, you are ready to declare the closure and the property for the event. Be sure to give the event a meaningful and descriptive name so that users can understand what the event does. Try to be consistent with names of similar properties in other components.

Event names start with “On”

The names of most events in C++Builder begin with “On.” This is just a convention; the compiler does not enforce it. The Object Inspector determines that a property is an event by looking at the type of the property: all closure properties are assumed to be events and appear on the Events page.

Developers expect to find events in the alphabetical list of names starting with “On.” Using other kinds of names is likely to confuse them.


Calling the event

Topic groups

You should centralize calls to an event. That is, create a virtual method in your component that calls the application’s event handler (if it assigns one) and provides any default handling.

Putting all the event calls in one place ensures that someone deriving a new component from yours can customize event handling by overriding a single method, rather than searching through your code for places where you call the event.

There are two other considerations when calling the event:

Empty handlers must be valid.

Users can override default handling.


Empty handlers must be valid
HeadingTitle

Topic groups	Example

You should never create a situation in which an empty event handler causes an error, nor should the proper functioning of your component depend on a particular response from the application’s event-handling code.


Example: Calling an event handler

An empty handler should produce the same result as no handler at all. So the code for calling an application’s event handler should look like this:

if (OnClick)

  OnClick(this);

// perform default handling }

You should never have something like this:

if (OnClick)

  OnClick(this);

else

  // perform default handling


Users can override default handling

Topic groups	Example

For some kinds of events, developers may want to replace the default handling or even suppress all responses. To allow this, you need to pass an argument by reference to the handler and check for a certain value when the handler returns.

This is in keeping with the rule that an empty handler should have the same effect as no handler at all. Because an empty handler will not change the values of arguments passed by reference, the default handling always takes place after calling the empty handler.


Example: Overriding default event handling

When handling key-press events, for example, the user can suppress the component’s default handling of the keystroke by setting the Key parameter to a null character. The logic for supporting that looks like this:

if (OnKeyPress)

  OnKeyPress(this, &Key);

if (Key != NULL)

  //perform default handling

The actual code is a little different from this because it deals with Windows messages, but the logic is the same. By default, the component calls any user-assigned handler, then performs its standard handling. If the user’s handler sets Key to a null character, the component skips the default handling.


Creating methods: Overview
Creating methods

Topic groups

Component methods are no different from any other class’s methods. That is, they are member functions built into the structure of a component class. Although there are essentially no restrictions on what you can do with the methods of a component, C++Builder does use some standards you should follow. These guidelines include

Avoiding dependencies

Naming methods

Protecting methods

Making methods virtual

Declaring methods

In general, components should not contain many methods and you should minimize the number of methods that an application needs to call. The features you might be inclined to implement as methods are often better encapsulated into properties. Properties provide an interface that suits the C++Builder environment and are accessible at design time.


Avoiding interdependencies

Topic groups

At all times when writing components, minimize the preconditions imposed on the developer. To the greatest extent possible, developers should be able to do anything they want to a component, whenever they want to do it. There will be times when you cannot accommodate that, but your goal should be to come as close as possible.

This list gives you an idea of the kinds of dependencies to avoid:

Methods that the user must call to use the component

Methods that must execute in a particular order

Methods that put the component into a state or mode where certain events or methods could be invalid

The best way to handle these situations is to ensure that you provide ways out of them. For example, if calling a method puts your component into a state where calling another method might be invalid, then write that second method so that if an application calls it when the component is in a bad state, the method corrects the state before executing its main code. At a minimum, you should throw an exception in cases when a user calls a method that is invalid.

In other words, if you create a situation where parts of your code depend on each other, the burden should be on you to be sure that using the code in incorrect ways does not cause problems. A warning message, for example, is preferable to a system failure if the user does not accommodate your dependencies.


Naming methods

Topic groups

C++Builder imposes no restrictions on what you name methods or their parameters. There are a few conventions that make methods easier for application developers, however. Keep in mind that the nature of a component architecture dictates that many different kinds of people might use your components.

If you are accustomed to writing code that only you or a small group of programmers use, you might not think too much about how you name things. It is a good idea to make your method names clear because people unfamiliar with your code (and even unfamiliar with coding) might have to use your components.

Here are some suggestions for making clear method names:

Make names descriptive. Use meaningful verbs.

A name like PasteFromClipboard is much more informative than simply Paste or PFC.

Function names should reflect the nature of what they return.

Although it might be obvious to you as a programmer that a function named X returns the horizontal position of something, a name like GetHorizontalPosition is more universally understandable.

If a function return type is void, the function name should be active.

Use active verbs in your function names. For example, ReadFileNames is much more helpful than DoFiles.

As a final consideration, make sure the method really needs to be a method. A good guideline is that method names have verbs in them. If you find that you create a lot of methods that do not have verbs in their names, consider whether those methods ought to be properties.


Protecting methods

Topic groups

All parts of classes, including data members, methods, and properties, have a level of protection or “visibility,” as explained in Controlling access. Choosing the appropriate visibility for a method is simple.

Most methods you write in your components are public or protected. You rarely need to make a method private, unless it is truly specific to that type of component, to the point that even derived components should not have access to it.

Note:	There is generally no reason for declaring a method (other than an event handler) as __published. Doing so looks to the end user exactly as if the method were public.


Methods that should be public

Topic groups

Any method that application developers need to call must be declared as public. Keep in mind that most method calls occur in event handlers, so methods should avoid tying up system resources or putting Windows in a state where it cannot respond to the user.

Note:	Constructors and destructors should always be public.


Methods that should be protected

Topic groups

Any implementation methods for the component should be protected so that applications cannot call them at the wrong time. If you have methods that application code should not call, but that are called in derived classes, declare them as protected.

For example, suppose you have a method that relies on having certain data set up for it beforehand. If you make that method public, there is a chance that applications will call it before setting up the data. On the other hand, by making it protected, you ensure that applications cannot call it directly. You can then set up other, public methods that ensure that data setup occurs before calling the protected method.

Property-implementation methods should be declared as virtual protected methods. Methods that are so declared allow the application developers to override the property implementation, either augmenting its functionality or replacing it completely. Such properties are fully polymorphic. Keeping access methods protected ensures that developers do not accidentally call them, inadvertently modifying a property.


Making methods virtual

Topic groups

You make methods virtual when you want different types to be able to execute different code in response to the same method call.

If you create components intended to be used directly by application developers, you can probably make all your methods nonvirtual. On the other hand, if you create abstract components from which other components will be derived, consider making the added methods virtual. This way, derived components can override the inherited virtual methods.


Declaring methods

Topic groups	Example

Declaring a method in a component is the same as declaring any class method.

To declare a new method in a component, you do these things:

Add the declaration to the component’s class declaration in the component’s header file.

Write the code that implements the method in the .CPP file of the unit.


Example: Declaring methods

The following code shows a component that defines two new methods, one protected method and one public virtual method. This is the interface definition in the .H file:

class PACKAGE TSampleComponent : public TControl

{

protected:

    void __fastcall MakeBigger();

public:

    virtual int __fastcall CalculateArea();

  ...

};

This is the code in the .CPP file of the unit that implements the methods:

void __fastcall TSampleComponent::MakeBigger()

{

  Height = Height + 5;

  Width = Width + 5;

}

int __fastcall TSampleComponent::CalculateArea()

{

  return Width * Height;

}


Using graphics in components: Overview
Using graphics in components

Topic groups

Windows provides a powerful Graphics Device Interface (GDI) for drawing device-independent graphics. The GDI, however, imposes extra requirements on the programmer, such as managing graphic resources. C++Builder takes care of all the GDI drudgery, allowing you to focus on productive work instead of searching for lost handles or unreleased resources.

As with any part of the Windows API, you can call GDI functions directly from your C++Builder application. But you will probably find that using C++Builder’s encapsulation of the graphic functions is faster and easier.

The topics in this section include

Overview of graphics

Using the canvas

Working with pictures

Off-screen bitmaps

Responding to changes


Overview of graphics

Topic groups	Example

C++Builder encapsulates the Windows GDI at several levels. The most important to you as a component writer is the way components display their images on the screen. When calling GDI functions directly, you need to have a handle to a device context, into which you have selected various drawing tools such as pens, brushes, and fonts. After rendering your graphic images, you must restore the device context to its original state before disposing of it.

Instead of forcing you to deal with graphics at a detailed level, C++Builder provides a simple yet complete interface: your component’s Canvas property. The canvas ensures that it has a valid device context, and releases the context when you are not using it. Similarly, the canvas has its own properties representing the current pen, brush, and font.

The canvas manages all these resources for you, so you need not concern yourself with creating, selecting, and releasing things like pen handles. You just tell the canvas what kind of pen it should use, and it takes care of the rest.

One of the benefits of letting C++Builder manage graphic resources is that it can cache resources for later use, which can speed up repetitive operations. For example, if you have a program that repeatedly creates, uses, and disposes of a particular kind of pen tool, you need to repeat those steps each time you use it. Because C++Builder caches graphic resources, chances are good that a tool you use repeatedly is still in the cache, so instead of having to recreate a tool, C++Builder uses an existing one.

An example of this is an application that has dozens of forms open, with hundreds of controls. Each of these controls might have one or more TFont properties. Though this could result in hundreds or thousands of instances of TFont objects, most applications wind up using only two or three font handles thanks to the VCL font cache.


Example: Simplified graphics

Here are two examples of how simple C++Builder’s graphics code can be. The first uses standard GDI functions to draw a yellow ellipse outlined in blue on a window in an application written with ObjectWindows. The second uses a canvas to draw the same ellipse in an application written with C++Builder.

This is the ObjectWindows code:

void TMyWindow::Paint(TDC& PaintDC, bool erase, TRect& rect)

{

  HPEN PenHandle, OldPenHandle;

  HBRUSH BrushHandle, OldBrushHandle;

  PenHandle = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));

  OldPenHandle = SelectObject(PaintDC, PenHandle);

  BrushHandle = CreateSolidBrush(RGB(255, 255, 0));

  OldBrushHandle = SelectObject(PaintDC, BrushHandle);

  Ellipse(10, 20, 50, 50);

  SelectObject(OldBrushHandle);

  DeleteObject(BrushHandle);

  SelectObject(OldPenHandle);

  DeleteObject(PenHandle);

)

This C++Builder code accomplishes the same thing:

void __fastcall TForm1::FormPaint(TObject *Sender)

{

  Canvas->Pen->Color = clBlue;

  Canvas->Brush->Color = clYellow;

  Canvas->Ellipse(10, 20, 50, 50);

}


Using the canvas

Topic groups

The canvas class encapsulates Windows graphics at several levels, including high-level functions for drawing individual lines, shapes, and text; intermediate properties for manipulating the drawing capabilities of the canvas; and low-level access to the Windows GDI.

The following table summarizes the capabilities of the canvas.

Level	Operation	Tools
High	Drawing lines and shapes	Methods such as MoveTo, LineTo, Rectangle, and Ellipse
	Displaying and measuring text	TextOut, TextHeight, TextWidth, and TextRect methods
	Filling areas	FillRect and FloodFill methods
Intermediate	Customizing text and graphics	Pen, Brush, and Font properties
	Manipulating pixels	Pixels property.
	Copying and merging images	Draw, StretchDraw, BrushCopy, and CopyRect methods; CopyMode property
Low	Calling Windows GDI functions	Handle property


Working with pictures

Topic groups

Most of the graphics work you do in C++Builder is limited to drawing directly on the canvases of components and forms. C++Builder also provides for handling stand-alone graphic images, such as bitmaps, metafiles, and icons, including automatic management of palettes.

There are three important aspects to working with pictures in C++Builder:

Using a picture, graphic, or canvas

Loading and storing graphics

Handling palettes


Using a picture, graphic, or canvas

Topic groups

There are three kinds of classes in C++Builder that deal with graphics:

A canvas represents a bitmapped drawing surface on a form, graphic control, printer, or bitmap. A canvas is always a property of something else, never a stand-alone class.

A graphic represents a graphic image of the sort usually found in a file or resource, such as a bitmap, icon, or metafile. C++Builder defines classes TBitmap, TIcon, and TMetafile, all descended from a generic TGraphic. You can also define your own graphic classes. By defining a minimal standard interface for all graphics, TGraphic provides a simple mechanism for applications to use different kinds of graphics easily.

A picture is a container for a graphic, meaning it could contain any of the graphic classes. That is, an item of type TPicture can contain a bitmap, an icon, a metafile, or a user-defined graphic type, and an application can access them all in the same way through the picture class. For example, the image control has a property called Picture, of type TPicture, enabling the control to display images from many kinds of graphics.

Keep in mind that a picture class always has a graphic, and a graphic might have a canvas. (The only standard graphic that has a canvas is TBitmap.) Normally, when dealing with a picture, you work only with the parts of the graphic class exposed through TPicture. If you need access to the specifics of the graphic class itself, you can refer to the picture’s Graphic property.


Loading and storing graphics

Topic groups	Example

All pictures and graphics in C++Builder can load their images from files and store them back again (or into different files). You can load or store the image of a picture at any time.

To load an image into a picture from a file, call the picture’s LoadFromFile method.

To save an image from a picture into a file, call the picture’s SaveToFile method.

LoadFromFile and SaveToFile each take the name of a file as the only parameter. LoadFromFile uses the extension of the file name to determine what kind of graphic object it will create and load. SaveToFile saves whatever type of file is appropriate for the type of graphic object being saved.


Example: Loading a bitmap

To load a bitmap into an image control’s picture, pass the name of a bitmap file to the picture’s LoadFromFile method:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

  Image1->Picture->LoadFromFile("c:\\windows\\athena.bmp");

}

The picture recognizes .BMP as the standard extension for bitmap files, so it creates its graphic as a TBitmap, then calls that graphic’s LoadFromFile method. Because the graphic is a bitmap, it loads the image from the file as a bitmap.


Handling palettes

Topic groups

When running on a palette-based device (typically, a 256-color video mode), C++Builder controls automatically support palette realization. That is, if you have a control that has a palette, you can use two methods inherited from TControl to control how Windows accommodates that palette.

Palette support for controls has these two aspects:

Specifying a palette for a control

Responding to palette changes

Most controls have no need for a palette, but controls that contain “rich color” graphic images (such as the image control) might need to interact with Windows and the screen device driver to ensure the proper appearance of the control. Windows refers to this process as realizing palettes.

Realizing palettes is the process of ensuring that the foremost window uses its full palette, and that windows in the background use as much of their palettes as possible, then map any other colors to the closest available colors in the “real” palette. As windows move in front of one another, Windows continually realizes the palettes.

Note:	C++Builder itself provides no specific support for creating or maintaining palettes, other than in bitmaps. If you have a palette handle, however, C++Builder controls can manage it for you.


Specifying a palette for a control

Topic groups

To specify a palette for a control, override the control’s GetPalette method to return the handle of the palette.

Specifying the palette for a control does these things for your application:

It tells the application that your control’s palette needs to be realized.

It designates the palette to use for realization.


Responding to palette changes

Topic groups

If your control specifies a palette by overriding GetPalette, C++Builder automatically takes care of responding to palette messages from Windows. The method that handles the palette messages is PaletteChanged.

The primary role of PaletteChanged is to determine whether to realize the control’s palette in the foreground or the background. Windows handles this realization of palettes by making the topmost window have a foreground palette, with other windows resolved in background palettes. C++Builder goes one step further, in that it also realizes palettes for controls within a window in tab order. The only time you might need to override this default behavior is if you want a control that is not first in tab order to have the foreground palette.


Offscreen bitmaps

Topic groups

When drawing complex graphic images, a common technique in Windows programming is to create an off-screen bitmap, draw the image on the bitmap, and then copy the complete image from the bitmap to the final destination onscreen. Using an off-screen image reduces flicker caused by repeated drawing directly to the screen.

The bitmap class in C++Builder, which represents bitmapped images in resources and files, can also work as an off-screen image.

There are two main aspects to working with off-screen bitmaps:

Creating and managing off-screen bitmaps.

Copying bitmapped images.


Creating and managing off-screen bitmaps

Topic groups

When creating complex graphic images, avoid drawing them directly on a canvas that appears onscreen. Instead of drawing on the canvas for a form or control, you can construct a bitmap object, draw on its canvas, and then copy its completed image to the onscreen canvas. The most common use of an offscreen bitmap is in the Paint method of a graphic control.

For an example of painting a complex image on an offscreen bitmap, see the source code for the Gauge control from the Samples page of the Component palette. The gauge draws its different shapes and text on an offscreen bitmap before copying them to the screen. Source code for the gauge is in the file GAUGES.CPP in the EXAMPLES\CONTROLS\SOURCE subdirectory.


Copying bitmapped images

Topic groups

C++Builder provides four different ways to copy images from one canvas to another. Depending on the effect you want to create, you call different methods.

The following table summarizes the image-copying methods in canvas objects.

To create this effect	Call this method
Copy an entire graphic.	Draw
Copy and resize a graphic.	StretchDraw
Copy part of a canvas.	CopyRect
Copy a bitmap with raster operations.	BrushCopy


Responding to changes

Topic groups	Example

All graphic objects, including canvases and their owned objects (pens, brushes, and fonts) have events built into them for responding to changes in the object. By using these events, you can make your components (or the applications that use them) respond to changes by redrawing their images.

Responding to changes in graphic objects is particularly important if you publish them as part of the design-time interface of your components. The only way to ensure that the design-time appearance of the component matches the properties set in the Object Inspector is to respond to changes in the objects.

To respond to changes in a graphic object, assign a method to the class’s OnChange event.


Example: Responding to changes

The shape component publishes properties representing the pen and brush it uses to draw its shape. The component’s constructor assigns a method to the OnChange event of each, causing the component to refresh its image if either the pen or brush changes. Although the shape component is written in Object Pascal, the following is a C++ translation of the shape component with a new name, TMyShape.

This is the class declaration in the header file:

class PACKAGE TMyShape : public TGraphicControl

{

private:

protected:

public:

    virtual __fastcall TMyShape(TComponent* Owner);

__published:

    TPen *FPen;

    TBrush *FBrush;

    void __fastcall StyleChanged(TObject *Sender);

};

This is the code in the .CPP file:

__fastcall TMyShape::TMyShape(TComponent* Owner)

  : TGraphicControl(Owner)

{

  Width = 65;

  Height = 65;

  FPen = new TPen;

  FPen->OnChange = StyleChanged;

  FBrush = new TBrush;

  FBrush->OnChange = StyleChanged;

}

void __fastcall TMyShape::StyleChanged(TObject *Sender)

{

  Invalidate();

}


Handling messages: Overview
Handling messages

Topic groups

One of the keys to traditional Windows programming is handling the messages sent by Windows to applications. C++Builder handles most of the common ones for you. It is possible, however, that you will need to handle messages that C++Builder does not already handle or that you will create your own messages.

There are three aspects to working with messages:

Understanding the message-handling system

Changing message handling

Creating new message handlers


Understanding the message-handling system

Topic groups

All C++Builder classes have a built-in mechanism for handling messages, called message-handling methods or message handlers. The basic idea of message handlers is that the class receives messages of some sort and dispatches them, calling one of a set of specified methods depending on the message received. If no specific method exists for a particular message, there is a default handler.

The following diagram shows the message-dispatch system:

The Visual Component Library defines a message-dispatching system that translates all Windows messages (including user-defined messages) directed to a particular class into method calls. You should never need to alter this message-dispatch mechanism. All you will need to do is create message-handling methods. See the section Declaring a new message-handling method for more on this subject.


What's in a Windows message?

Topic groups

A Windows message can be thought of as a data structure that contains several useful data members. The most important of these is an integer-size value that identifies the message. Windows defines a lot of messages, and the MESSAGES.HPP file declares identifiers for all of them.

Windows programmers are used to working with the Windows definitions that identify a message, such as WM_COMMAND or WM_PAINT. A traditional Windows program contains a window procedure that serves as a callback for system generated messages. In this window procedure there is usually a large switch statement with case labels for each message this window intends to handle.

Additional useful information is passed to this window procedure in two parameters, wParam and lParam, for “word parameter” and “long parameter”. Often, each parameter contains more than one piece of information and it is necessary to pull out the relevant portions with Windows macros such as LOWORD and HIWORD. For example, calling HIWORD( lParam ) yields the high word of this parameter.

Originally, Windows programmers had to remember or look up in the Windows API what information each parameter contained. Windows has recently implemented “message crackers” to simplify the syntax associated with handling a Windows message and its associated parameters. With “message crackers,” instead of using a large switch statement that unpacks all of the information into the parameters, you can simply associate a handler function with the message. If you include WINDOWSX.H into a standard Windows program, the HANDLE_MSG macro is available to your program so you can write code like this:

void MyKeyDownHandler(  HWND hwnd. UINT nVirtKey, BOOL fDown, int CRepeat, UINT flags )

{

  ...

}

LRESULT MyWndProc( HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam )

{

  switch(  Message )

  {

    HANDLE_MSG( hwnd, WM_KEYDOWN, MyKeyDownHandler );

    ...

}

Using this style of message cracking makes it clearer that messages are being dispatched to a particular handler. Also, you can give significant names to the parameter list for your handler function. It is easier to understand a function that takes a parameter called nVirtKey, which is the value for wParam in a WM_KEYDOWN message.


Dispatching messages

Topic groups

When an application creates a window, it registers a window procedure with the Windows kernel. The window procedure is the routine that handles messages for the window. Traditionally, the window procedure contains a huge switch statement with entries for each message the window has to handle. Keep in mind that “window” in this sense means just about anything on the screen: each window, each control, and so on. Every time you create a new type of window, you have to create a complete window procedure.

C++Builder simplifies message dispatching in several ways:

Each component inherits a complete message-dispatching system.

The dispatch system has default handling. You define handlers only for messages you need to respond to specially.

You can modify small parts of the message handling and rely on inherited methods for most processing.

The greatest benefit of this message dispatch system is that you can safely send any message to any component at any time. If the component does not have a handler defined for the message, the default handling takes care of it, usually by ignoring the message.

Tracing the flow of messages

C++Builder registers a method called MainWndProc as the window procedure for each type of component in an application. MainWndProc contains an exception-handling block, passing the message structure from Windows to a virtual method called WndProc and handling any exceptions by calling the application class’s HandleException method.

MainWndProc is a nonvirtual method that contains no special handling for any particular messages. Customizations take place in WndProc, since each component type can override the method to suit its particular needs.

WndProc methods check for any special conditions that affect their processing so they can “trap” unwanted messages. For example, while being dragged, components ignore keyboard events, so the WndProc method of TWinControl passes along keyboard events only if the component is not being dragged. Ultimately, WndProc calls Dispatch, a nonvirtual method inherited from TObject, which determines which method to call to handle the message.

Dispatch uses the Msg data member of the message structure to determine how to dispatch a particular message. If the component defines a handler for that particular message, Dispatch calls the method. If the component does not define a handler for that message, Dispatch calls DefaultHandler.


Changing message handling

Topic groups

Before changing the message handling of your components, make sure that is what you really want to do. C++Builder translates most Windows messages into events that both the component writer and the component user can handle. Rather than changing the message-handling behavior, you should probably change the event-handling behavior.

To change message handling, you override the message-handling method. You can also prevent a component from handling a message under certain circumstances by trapping the message.


Overriding the handler method

Topic groups	Example

To change the way a component handles a particular message, you override the message-handling method for that message. If the component does not already handle the particular message, you need to declare a new message-handling method.

To override a message-handling method,

1	Declare a new method in your component with the same name as the method it overrides in the protected part of the component declaration.

2	Map the method to the message it overrides by using three macros.

The macros take this form:

BEGIN_MESSAGE_MAP

  MESSAGE_HANDLER(parameter1, parameter2, parameter3)

END_MESSAGE_MAP

Parameter1 is the message index as Windows defines it, parameter2 is the message structure type, and parameter3 is the name of the message method.

You can include as many MESSAGE_HANDLER macros as you want between the BEGIN_MESSAGE_MAP and END_MESSAGE_MAP macros.


Example: Overriding a message handler

For example, to override a component’s handling of the WM_PAINT message, you redeclare the WMPaint method, and with three macros, map the method to the WM_PAINT message:

class PACKAGE TMyComponent : public TComponent

{

protected:

    void __fastcall WMPaint(TWMPaint* Message);

BEGIN_MESSAGE_MAP

  MESSAGE_HANDLER(WM_PAINT, TWMPaint, WMPaint)

END_MESSAGE_MAP(TComponent)

};


Using message parameters

Topic groups

Once inside a message-handling method, your component has access to all the parameters of the message structure. Because the parameter passed to the message handler is a pointer, the handler can change the values of the parameters if necessary. The only parameter that changes frequently is the return value for the message: the value returned by the SendMessage call that sends the message.

Because the type of the Message parameter in the message-handling method varies with the message being handled, you should refer to the documentation on Windows messages for the names and meanings of individual parameters. If for some reason you need to refer to the message parameters by their old-style names (WParam, LParam, and so on), you can typecast Message to the generic type TMessage, which uses those parameter names.


Trapping messages

Topic groups

Under some circumstances, you might want your components to ignore messages. That is, you want to keep the component from dispatching the message to its handler. To trap a message, you override the virtual method WndProc.

The WndProc method screens messages before passing them to the Dispatch method, which in turn determines which method gets to handle the message. By overriding WndProc, your component gets a chance to filter out messages before dispatching them. An override of WndProc for a control derived from TWinControl looks like this:

void __fastcall TMyControl::WndProc(TMessage* Message)

{

  // tests to determine whether to continue processing

  TWinControl->WndProc(Message);

}

TheTControl component defines entire ranges of mouse messages that it filters when a user is dragging and dropping controls. Overriding WndProc helps this in two ways:

It can filter ranges of messages instead of having to specify handlers for each one.

It can preclude dispatching the message at all, so the handlers are never called.


The WndProc method

Topic groups

Here is part of the WndProc method for TControl as it is implemented in VCL in Object Pascal:

procedure TControl.WndProc(var Message: TMessage);

begin

  ...

  if (Message.Msg >= WM_MOUSEFIRST) and (Message.Msg <= WM_MOUSELAST) then

    if Dragging then                                         { handle dragging specially }

      DragMouseMsg(TWMMouse(Message))

    else

      ...                                                         { handle others normally }

    end;

...                                                           { otherwise process normally }

end;


Creating new message handlers

Topic groups

Because C++Builder provides handlers for most common Windows messages, the time you will most likely need to create new message handlers is when you define your own messages. Working with user-defined messages has two aspects:

Defining your own messages

Declaring a new message-handling method


Defining your own messages

A number of the standard components define messages for internal use. The most common reasons for defining messages are broadcasting information not covered by standard Windows messages and notification of state changes.

Defining a message is a two-step process. The steps are

1	Declaring a message identifier.

2	Declaring a message-structure type.


Declaring a message identifier

Topic groups	Example

A message identifier is an integer-sized constant. Windows reserves the messages below 1,024 for its own use, so when you declare your own messages you should start above that level.

The constant WM_APP represents the starting number for user-defined messages. When defining message identifiers, you should base them on WM_APP.

Be aware that some standard Windows controls use messages in the user-defined range. These include list boxes, combo boxes, edit boxes, and command buttons. If you derive a component from one of these and want to define a new message for it, be sure to check the MESSAGES.HPP file to see which messages Windows already defines for that control.


Example: User-defined messages

The following code shows two user-defined messages.

#define WM_MYFIRSTMESSAGE  (WM_APP + 400)

#define WM_MYSECONDMESSAGE (WM_APP + 401)


Declaring a message-structure type

Topic groups	Example

If you want to give useful names to the parameters of your message, you need to declare a message-structure type for that message. The message-structure is the type of the parameter passed to the message-handling method. If you do not use the message’s parameters, or if you want to use the old-style parameter notation (wParam, lParam, and so on), you can use the default message-structure, TMessage.

To declare a message-structure type, follow these conventions:

1	Name the structure type after the message, preceded by a T.

2	Call the first data member in the structure Msg, of type TMsgParam.

3	Define the next two bytes to correspond to the Word parameter, and the next two bytes as unused.

Or

Define the next four bytes to correspond to the Longint parameter.

4	Add a final data member called Result, of type Longint.


Example: Message structure

For example, here is the message structure for all mouse messages, TWMKey:

struct TWMKey

{

  Cardinal Msg;                      // first parameter is the message ID

  Word CharCode;                     // this is the first wParam

  Word Unused;

  Longint KeyData;                   // this is the lParam

  Longint Result;                    // this is the result data member

};


Declaring a new message-handling method

Topic groups	Example

There are two sets of circumstances that require you to declare new message-handling methods:

Your component needs to handle a Windows message that is not already handled by the standard components.

You have defined your own message for use by your components.

To declare a message-handling method, do the following:

1	Declare the method in a protected part of the component’s class declaration.

2	Be sure that the method returns void.

3	Name the method after the message it handles, but without any underline characters.

4	Pass a pointer called Message of the type of the message structure.

5	Map the method to the message using macros.

6	Within the message method implementation, write code for any handling specific to the component.

7	Call the inherited message handler.


Example: Message handler

Here is the declaration of a message handler for a user-defined message called CM_CHANGECOLOR.

#define CM_CHANGECOLOR (WM_APP + 400)

class TMyControl : public TControl

{

protected:

    void __fastcall CMChangeColor(TMessage &Message);

BEGIN_MESSAGE_MAP

  MESSAGE_HANDLER(CM_CHANGECOLOR, TMessage, CMChangeColor)

END_MESSAGE_MAP(TControl)

};

void __fastcall TMyControl::CMChangeColor(TMessage &Message)

{

  Color = Message.LParam;                   // set color from long parameter

  TControl::CMChangeColor(Message);         // call the inherited message handler

}


Making components available at design time: Overview
Making components available at design time

Topic groups

This chapter describes the steps for making the components you create available in the IDE. Making your components available at design time requires several steps:

Registering components

Adding palette bitmaps

Providing Help for your component

Adding property editors

Adding component editors

Compiling components into packages

Not all these steps apply to every component. For example, if you don’t define any new properties or events, you don’t need to provide Help for them. The only steps that are always necessary are registration and compilation.

Once your components have been registered and compiled into packages, they can be distributed to other developers and installed in the IDE. For information on installing packages in the IDE, see Installing component packages.


Registering components

Topic groups

Registration works on a compilation unit basis, so if you create several components in a single compilation unit, you can register them all at once.

To register a component, add a Register function to the .CPP file of the unit. Within the Register function, you register the components and determine where to install them on the Component palette.

Note:	If you create your component by choosing Component|New Component in the IDE, the code required to register your component is added automatically.

The steps for manually registering a component are:

Declaring the Register function

Writing the Register function


Declaring the register function

Topic groups

Registration involves writing a single function in the .CPP file of the unit, which must have the name Register. The Register function must exist within a namespace. The namespace is the name of the file the component is in with all lowercase letters except the first letter.

The following code shows how the Register functionis implemented within a namespace. The namespace is named Newcomp, where the file is named Newcomp.CPP:

namespace Newcomp

{

  void __fastcall PACKAGE Register()

  {

  }

}

Within the Register function, call RegisterComponents for each component you want to add to the Component palette. If the header and .CPP file combination contain several components, you can register them all in one step. The PACKAGE macro expands to a statement that allows classes to be imported and exported.


Writing the Register function

Topic groups

Inside the Register function of a unit containing components, you must register each component you want to add to the Component palette. If the unit contains several components, you can register them at the same time.

To register a component, call the RegisterComponents function once for each page of the Component palette to which you want to add components. RegisterComponents involves three important things:

1	Specifying the components

2	Specifying the palette page

3	Using the RegisterComponents function


Specifying the components

Topic groups

Within the Register function, declare an open array of type TComponentClass that holds the array of components you are registering. The syntax should look like this:

TMetaClass classes[1] = {__classid(TNewComponent));

In this case, the array of classes contains just one component, but you can add all the components you want to register to the array. For example, this code places two components in the array:

TMetaClass classes[2] =

                      {__classid(TNewComponent), __classid(TAnotherComponent)};

Another way to add a component to the array is to assign the component to the array in a separate statement. These statements add the same two components to the array as the previous example:

TMetaClass classes[2];

classes[0] = __classid(TNewComponent);

classes[1] = __classid(TAnotherComponent);


Specifying the palette page

Topic groups

The palette-page name is an AnsiString. If the name you give for the palette page does not already exist, C++Builder creates a new page with that name. C++Builder stores the names of the standard pages in string-list resources so that international versions of the product can name the pages in their native languages. If you want to install a component on one of the standard pages, you should obtain the string for the page name by calling the LoadStr function, passing the constant representing the string resource for that page, such as srSystem for the System page.


Using the RegisterComponents function

Topic groups

Within the Register function, call RegisterComponents to register the components in the classes array. RegisterComponents is a function that takes three parameters: the name of a Component palette page, the array of component classes, and the index of the last entry in the array.

The following Register function found in the NEWCOMP.CPP file, registers a component named TMyComponent and places it on a Component palette page called Miscellaneous:

namespace Newcomp

{

  void __fastcallPACKAGE Register()

  {

      TMetaClass classes[1] = {__classid(TMyComponent)};

      RegisterComponents("Miscellaneous", classes, 0);

  }

}

Note that the third argument in the RegisterComponents call is 0, which is the index of the last entry in the classes array (the size of the array minus 1).

You can also register several components on the same page at once, or register components on different pages, as shown in the following code:

namespace Mycomps

{

 void __fastcallPACKAGE Register()

  {

      // declares an array that holds two components

      TMetaClass classes1[2] = {__classid(TFirst), __classid(TSecond)};

      // adds a new palette page with the two components in the classes1 array

      RegisterComponents("Miscellaneous", classes1, 1);

      // declares a second array

      TMetaClass classes2[1];

      // assigns a component to be the first element in the array

      classes2[0]  = __classid(TThird);

      // adds the component in the classes2 array to the Samples page

      RegisterComponents("Samples", classes2, 0);

  }

}

In the example two arrays, classes1 and classes2 are declared. In the first RegisterComponents call the classes1 array has 2 entries, so the third argument is the index of the second entry, which is 1. In the second RegisterComponents call, the classes2 array has one element, so the third argument is 0.


Adding palette bitmaps

Topic groups

Every component needs a bitmap to represent the component on the Component palette. If you don’t specify your own bitmap, C++Builder uses a default bitmap.

Because the palette bitmaps are needed only at design time, you don’t compile them into the component’s compilation unit. Instead, you supply them in a Windows resource file with the same name as the .CPP file, but with the extension .DCR (dynamic component resource). You can create this resource file using the Image editor in C++Builder. Each bitmap should be 24 pixels square.

For each component you want to install, supply a palette bitmap file, and within each palette bitmap file, supply a bitmap for each component you register. The bitmap image has the same name as the component class. Keep the palette bitmap file in the same directory with the compiled files, so C++Builder can find the bitmaps when it installs the components on the Component palette.

For example, if you create a component named TMyControl, you need to create a .DCR or .RES resource file that contains a bitmap called TMYCONTROL. The resource names are not case-sensitive, but by convention, they are usually in uppercase letters.


Providing Help for your component

Topic groups

When you select a standard component on a form, or a property or event in the Object Inspector, you can press F1 to get Help on that item. You can provide developers with the same kind of documentation for your components if you create the appropriate Help files.

You can provide a small Help file to describe your components, and your help file becomes part of the user’s overall C++Builder Help system.

See the section Creating the Help file for information on how to compose the help file for use with a component.


Creating the help file

Topic groups

You can use any tool you want to create the source file for a Windows Help file (in .rtf format). C++Builder includes the Microsoft Help Workshop, which compiles your Help files and provides an online help authoring guide. You can find complete information about creating Help files in the online guide for Help Workshop.

Composing help files for components consists of the steps:

Creating the entries

Making component help context-sensitive

Adding component help files


Creating the entries

Topic groups

To make your component’s Help integrate seamlessly with the Help for the rest of the components in the library, observe the following conventions:

1	Each component should have a help topic.

The component topic should show which unit the component is declared in and briefly describe the component. The component topic should link to secondary windows that describe the component’s position in the object hierarchy and list all of its properties, events, and methods. Application developers access this topic by selecting the component on a form and pressing F1. For an example of a component topic, place any component on a form and press F1.

The component topic must have a # footnote with a value unique to the topic. The # footnote uniquely identifies each topic by the Help system.

The component topic should have a K footnote for keyword searching in the help system Index that includes the name of the component class. For example, the keyword footnote for the TMemo component is “TMemo.”

The component topic should also have a $ footnote that provides the title of the topic. The title appears in the Topics Found dialog box, the Bookmark dialog box, and the History window.

2	Each component should include the following secondary navigational topics:

A hierarchy topic with links to every ancestor of the component in the component hierarchy.

A list of all properties available in the component, with links to entries describing those properties.

A list of all events available in the component, with links to entries describing those events.

A list of methods available in the component, with links to entries describing those methods.

Links to object classes, properties, methods, or events in the C++Builder help system can be made using Alinks. When linking to an object class, the Alink uses the class name of the object, followed by an underscore and the string “object”. For example, to link to the TCustomPanel object, use the following:

!AL(TCustomPanel_object,1)

When linking to a property, method, or event, precede the name of the property, method, or event by the name of the object that implements it and an underscore. For example, to link to the Text property which is implemented by TControl, use the following:

!AL(TControl_Text,1)

To see an example of the secondary navigation topics, display the help for any component and click on the links labeled hierarchy, properties, methods, or events.

3	Each property, method, and event that is declared within the component should have a topic.

A property, event, or method topic should show the declaration of the item and describe its use. Application developers see these topics either by highlighting the item in the Object Inspector and pressing F1 or by placing the cursor in the Code editor on the name of the item and pressing F1. To see an example of a property topic, select any item in the Object Inspector and press F1.

The property, event, and method topics should include a K footnote that lists the name of the property, method, or event, and its name in combination with the name of the component. Thus, the Text property of TControl has the following K footnote:

Text,TControl;TControl,Text;Text,

The property, method, and event topics should also include a $ footnote that indicates the title of the topic, such as TControl::Text.

All of these topics should have a topic ID that is unique to the topic, entered as a # footnote.


Making component help context-sensitive

Topic groups

Each component, property, method, and event topic must have an A footnote. The A footnote is used to display the topic when the user selects a component and presses F1, or when a property or event is selected in the Object Inspector and the user presses F1. The A footnotes must follow certain naming conventions:

If the Help topic is for a component, the A footnote consists of two entries separated by a semicolon using this syntax:

ComponentClass_Object;ComponentClass

where ComponentClass is the name of the component class.

If the Help topic is for a property or event, the A footnote consists of three entries separated by semicolons using this syntax:

ComponentClass_Element;Element_Type;Element

where ComponentClass is the name of the component class, Element is the name of the property, method, or event, and Type is the either Property, Method, or Event

For example, for a property named BackgroundColor of a component named TMyGrid, the A footnote is

TMyGrid_BackgroundColor;BackgroundColor_Property;BackgroundColor


Adding component help files

Topic groups

To add your Help file to C++Builder, use the OpenHelp utility (called oh.exe) located in the bin directory or accessed using Help|Customize in the IDE.

You will find information about using OpenHelp in the OpenHelp.hlp file, including adding your Help file to the Help system.


Adding property editors

Topic groups

The Object Inspector provides default editing for all types of properties. You can, however, provide an alternate editor for specific properties by writing and registering property editors. You can register property editors that apply only to the properties in the components you write, but you can also create editors that apply to all properties of a certain type.

At the simplest level, a property editor can operate in either or both of two ways: displaying and allowing the user to edit the current value as a text string, and displaying a dialog box that permits some other kind of editing. Depending on the property being edited, you might find it useful to provide either or both kinds.

Writing a property editor requires these five steps:

1	Deriving a property-editor class

2	Editing the property as text

3	Editing the property as a whole

4	Specifying editor attributes

5	Registering the property editor


Deriving a property-editor class

Topic groups	Example

The DSGNINTF.HPP file defines several kinds of property editors, all of which descend from TPropertyEditor. When you create a property editor, your property-editor class can either descend directly from TPropertyEditor or indirectly through one of the property-editor classes described in the table below.

The DSGNINTF.HPP file also defines some very specialized property editors used by unique properties such as the component name. The listed property editors are the ones that are the most useful for user-defined properties.

Type	Properties edited
TOrdinalProperty	All ordinal-property editors (those for integer, character, and enumerated properties) descend from TOrdinalProperty.
TIntegerProperty	All integer types, including predefined and user-defined subranges.
TCharProperty	Char-type and subranges of Char, such as ‘A’..’Z’.
TEnumProperty	Any enumerated type.
TFloatProperty	All floating-point numbers.
TStringProperty	AnsiStrings.
TSetElementProperty	Individual elements in sets, shown as Boolean values
TSetProperty	All sets. Sets are not directly editable, but can expand into a list of set-element properties.
TClassProperty	Classes. Displays the name of the class and allows expansion of the class’s properties.
TMethodProperty	Method pointers, most notably events.
TComponentProperty	Components in the same form. The user cannot edit the component’s properties, but can point to a specific component of a compatible type.
TColorProperty	Component colors. Shows color constants if applicable, otherwise displays hexadecimal value. Drop-down list contains the color constants. Double-click opens the color-selection dialog box.
TFontNameProperty	Font names. The drop-down list displays all currently installed fonts.
TFontProperty	Fonts. Allows expansion of individual font properties as well as access to the font dialog box.


Example: Property editor class

The following example shows the declaration of a simple property editor namedTMyPropertyEditor:

class PACKAGE TMyPropertyEditor : public TPropertyEditor

{

  public:

    virtual bool __fastcallAllEqual(void);

    virtual System::AnsiString __fastcall GetValue(void);

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

   __fastcall virtual ~TMyPropertyEditor(void) { }

   __fastcallTMyPropertyEditor(void) : Dsgnintf::TPropertyEditor() { }

};


Editing the property as text

All properties need to provide a string representation of their values for the Object Inspector to display. Most properties also allow the user to type in a new value for the property. Property-editor classes provide virtual methods you can override to convert between the text representation and the actual value.

The methods you override are called GetValue and SetValue. Your property editor also inherits a set of methods used for assigning and reading different sorts of values, as shown in the following table.

Property type	Get method	Set method
Floating point	GetFloatValue	SetFloatValue
Closure (event)	GetMethodValue	SetMethodValue
Ordinal type	GetOrdValue	SetOrdValue
String	GetStrValue	SetStrValue
When you override a GetValue method, you will call one of the Get methods, and when you override SetValue, you will call one of the Set methods.


Setting the property value

Topic groups

The property editor’s SetValue method takes a string typed by the user in the Object Inspector, converts it into the appropriate type, and sets the value of the property. If the string does not represent a proper value for the property, SetValue should throw an exception and not use the improper value.

To read string values into properties, override the property editor’s SetValue method.

SetValue should convert the string and validate the value before calling one of the Set methods.


Editing the property as a whole

Topic groups

You can optionally provide a dialog box in which the user can visually edit a property. The most common use of property editors is for properties that are themselves classes. An example is the Font property, for which the user can open a font dialog box to choose all the attributes of the font at once.

To provide a whole-property editor dialog box, override the property-editor class’s Edit method.

Edit methods use the same Get and Set methods used in writing GetValue and SetValue methods. In fact, an Edit method calls both a Get method and a Set method. Because the editor is type-specific, there is usually no need to convert the property values to strings. The editor generally deals with the value “as retrieved.”

When the user clicks the ‘...’ button next to the property or double-clicks the value column, the Object Inspector calls the property editor’s Edit method.

Within your implementation of the Edit method, follow these steps:

1	Construct the editor you are using for the property.

2	Read the current value and assign it to the property using a Get method.

3	When the user selects a new value, assign that value to the property using a Set method.

4	Destroy the editor.


Specifying editor attributes

Topic groups	Example

The property editor must provide information that the Object Inspector can use to determine what tools to display. For example, the Object Inspector needs to know whether the property has subproperties or can display a list of possible values.

To specify editor attributes, override the property editor’s GetAttributes method.

GetAttributes is a method that returns a set of values of type TPropertyAttributes that can include any or all of the following values:

Flag	Related method	Meaning if included
paValueList	GetValues	The editor can give a list of enumerated values.
paSubProperties	GetProperties	The property has subproperties that can display.
paDialog	Edit	The editor can display a dialog box for editing the entire property.
paMultiSelect	N/A	The property should display when the user selects more than one component.
paAutoUpdate	SetValue	Updates the component after every change instead of waiting for approval of the value.
paSortList	N/A	The Object Inspector should sort the value list.
paReadOnly	N/A	Users cannot modify the property value.
paRevertable	N/A	Enables the Revert to Inherited menu item on the Object Inspector’s context menu. The menu item tells the property editor to discard the current property value and return to some previously established default or standard value.


Example: Specifying editor attributes

Color properties are more versatile than most, in that they allow several ways for users to choose them in the Object Inspector: typing, selection from a list, and customized editor. TColorProperty’s GetAttributes method, therefore, includes several attributes in its return value:

virtual __fastcall TPropertyAttributes TColorProperty::GetAttributes()

{

  return TPropertyAttributes() << paMultiSelect << paDialog << paValueList;

}


Registering the property editor

Topic groups	Example

Once you create a property editor, you need to register it with C++Builder. Registering a property editor associates a type of property with a specific property editor. You can register the editor with all properties of a given type or just with a particular property of a particular type of component.

To register a property editor, call the RegisterPropertyEditor function.

RegisterPropertyEditor takes four parameters:

A type-information pointer for the type of property to edit. Specify the type information like this:

__typeinfo(TMyComponent)

The type of the component to which this editor applies. If this parameter is null, the editor applies to all properties of the given type.

The name of the property. This parameter only has meaning if the previous parameter specifies a particular type of component. In that case, you can specify the name of a particular property in that component type to which this editor applies.

The type of property editor to use for editing the specified property.


Example: Registering a property editor

Here is an excerpt from the function that registers the editors for the standard components on the Component palette:

namespace Newcomp

{

  void __fastcall PACKAGE Register()

  {

    RegisterPropertyEditor(__typeinfo(TComponent), 0L, "", __classid(TComponentProperty));

    RegisterPropertyEditor(__typeinfo(TComponentName), __classid(TComponent), "Name",

        __classid(TComponentNameProperty));

    RegisterPropertyEditor(__typeinfo(TMenuItem), __classid(TMenu), "",

        __classid(TMenuItemProperty));

    ...

  }

}

The three statements in this function cover the different uses of RegisterPropertyEditor:

The first statement is the most typical. It registers the property editor TComponentProperty for all properties of type TComponent (or descendants of TComponent that do not have their own editors registered). In general, when you register a property editor, you have created an editor for a particular type, and you want to use it for all properties of that type, so the second and third parameters are NULL and an empty string, respectively.

The second statement is the most specific kind of registration. It registers an editor for a specific property in a specific type of component. In this case, the editor is for the Name property (of type TComponentName) of all components.

The third statement is more specific than the first, but not as limited as the second. It registers an editor for all properties of type TMenuItem in components of type TMenu.


Adding component editors

Topic groups

Component editors determine what happens when the component is double-clicked in the designer and add commands to the context menu that appears when the component is right-clicked. They can also copy your component to the Windows clipboard in custom formats.

If you do not give your components a component editor, C++Builder uses the default component editor. The default component editor is implemented by the class TDefaultEditor. TDefaultEditor does not add any new items to a component’s context menu. When the component is double-clicked, TDefaultEditor searches the properties of the component and generates (or navigates to) the first event handler it finds.

To add items to the context menu, change the behavior when the component is double-clicked, or add new clipboard formats, derive a new class from TComponentEditor and register its use with your component. In your overridden methods, you can use the Component property of TComponentEditor to access the component that is being edited.

Adding a custom component editor consists of the steps:

Adding items to the context menu

Changing the double-click behavior

Adding clipboard formats

Registering the component editor


Adding items to the context menu

Topic groups

When the user right-clicks the component, the GetVerbCount and GetVerb methods of the component editor are called to build context menu. You can override these methods to add commands (verbs) to the context menu.

Adding items to the context menu requires the steps:

Specifying menu items

Implementing commands


Specifying menu items

Topic groups

Override the GetVerbCount method to return the number of commands you are adding to the context menu. Override the GetVerb method to return the strings that should be added for each of these commands. When overriding GetVerb, add an ampersand (&) to a string to cause the following character to appear underlined in the context menu and act as a shortcut key for selecting the menu item. Be sure to add an ellipsis (...) to the end of a command if it brings up a dialog. GetVerb has a single parameter that indicates the index of the command.

The following code overrides the GetVerbCount and GetVerb methods to add two commands to the context menu.

int __fastcallTMyEditor::GetVerbCount(void)

{

  return 2;

}

System::AnsiString __fastcall TMyEditor::GetVerb(int Index)

{

  switch (Index)

  {

    case 0: return “&DoThis ...”; break;

    case 1: return “Do&That”; break;

  }

}

Note:	Be sure that your GetVerb method returns a value for every possible index indicated by GetVerbCount.


Implementing commands

Topic groups

When the command provided by GetVerb is selected in the designer, the ExecuteVerb method is called. For every command you provide in the GetVerb method, implement an action in the ExecuteVerb method. You can access the component that is being edited using the Component property of the editor.

For example, the following ExecuteVerb method implements the commands for the GetVerb method in the previous example.

void __fastcall TMyEditor::ExecuteVerb(int Index)

{

  switch (Index)

  {

    case 0:

      TMyDialog *MySpecialDialog = new TMyDialog();

      MySpecialDialog->Execute();

      ((TMyComponent *)Component)->ThisProperty = MySpecialDialog->ReturnValue;

      delete MySpecialDialog;

      break;

    case 1:

      That();  // call the “That” method

      break;

  }

}


Changing the double-click behavior

Topic groups

When the component is double-clicked, the Edit method of the component editor is called. By default, the Edit method executes the first command added to the context menu. Thus, in the previous example, double-clicking the component executes the DoThis command.

While executing the first command is usually a good idea, you may want to change this default behavior. For example, you can provide an alternate behavior if

you are not adding any commands to the context menu.

you want to display a dialog that combines several commands when the component is double-clicked.

Override the Edit method to specify a new behavior when the component is double-clicked. For example, the following Edit method brings up a font dialog when the user double-clicks the component:

void __fastcall TMyEditor::Edit(void)

{

  TFontDialog *pFontDlg = new TFontDialog(NULL);

  pFontDlg->Execute();

  ((TMyComponent *)Component)->Font = pFontDlg->Font;

  delete pFontDlg;

}

Note:	If you want a double-click on the component to display the Code editor for an event handler, use TDefaultEditor as a base class for your component editor instead of TComponentEditor. Then, instead of overriding the Edit method, override the protected TDefaultEditor::EditProperty method instead. EditProperty scans through the event handlers of the component, and brings up the first one it finds. You can change this to look a particular event instead. For example:

void __fastcall TMyEditor::EditProperty(TPropertyEditor* PropertyEditor,

                                     bool &Continue, bool &FreeEditor)

{

  if (PropertyEditor->ClassNameis(“TMethodProperty”) &&

      CompareText(PropertyEditor->GetName, “OnSpecialEvent”) == 0)

  {

    TDefaultEditor::EditProperty(PropertyEditor, Continue, FreeEditor);

  }

}


Adding clipboard formats

Topic groups

By default, when a user chooses Copy while a component is selected in the IDE, the component is copied in C++Builder’s internal format. It can then be pasted into another form or data module. Your component can copy additional formats to the Clipboard by overriding the Copy method.

For example, the following Copy method allows a TImage component to copy its picture to the Clipboard. This picture is ignored by the C++Builder IDE, but can be pasted into other applications.

void __fastcall TMyComponentEditor::Copy(void)

{

  WORD AFormat;

  int AData;

  HPALETTE APalette;

  ((TImage *)Component)->Picture->SaveToClipboardFormat(AFormat, AData, APalette);

  TClipboard *pClip = Clipboard(); // don’t clear the clipboard!

  pClip->SetAsHandle(AFormat, AData);

}


Registering the component editor

Topic groups

Once the component editor is defined, it can be registered to work with a particular component class. A registered component editor is created for each component of that class when it is selected in the form designer.

To create the association between a component editor and a component class, call RegisterComponentEditor. RegisterComponentEditor takes the name of the component class that uses the editor, and the name of the component editor class that you have defined. For example, the following statement registers a component editor class named TMyEditor to work with all components of type TMyComponent:

RegisterComponentEditor(__classid( TMyComponent), __classid(TMyEditor));

Place the call to RegisterComponentEditor in the namespace where you register your component. For example, if a new component named TMyComponent and its component editor TMyEditor are both implemented in NewComp.cpp, the following code (in NewComp.cpp) registers the component and its association with the component editor.

namespace Newcomp

{

  void __fastcallPACKAGE Register()

  {

    	TMetaClass classes[1] = {__classid(TMyComponent)};

    RegisterComponents("Miscellaneous", classes, 0);

    RegisterComponentEditor(classes[0], __classid(TMyEditor));

  }

}


Compiling components into packages

Topic groups

Once your components are registered, you must compile them as packages before they can be installed in the IDE. A package can contain one or several components as well as custom property editors. For more information about packages, see Working with packages and components.

To create and compile a package, see Creating and editing packages. Put the source-code units for your custom components in the package’s Contains list. If your components depend on other packages, include those packages in the Requires list.

To install your components in the IDE, see Installing component packages.


Troubleshooting custom components

Topic groups

A common problem when registering and installing custom components is that the component does not show up in the list of components after the package is successfully installed.

The most common causes for component not showing up in the list or on the palette:

Missing PACKAGE modifier on the Register function

Missing PACKAGE modifier on the class

Missing #pragma package(smart_init) in the C++ source file

Register function is not found in a namespace with the same name as the source code module name.

Register is not being successfully exported. Use tdump on the .BPL to look for the exported function:

tdump -ebpl mypack.bpl mypack.dmp

In the exports section of the dump, you should see the Register function (within the namespace) being exported.


Modifying an existing component: Overview
Modifying an existing component

Topic groups

The easiest way to create a component is to derive it from a component that does nearly everything you want, then make whatever changes you need. The example in this chapter modifies the standard memo component to create a memo that has a yellow background. The other chapters in this part describe creating more complex components. The basic process is always the same, but more complex components require more steps in customizing the new class.

Modifying an existing component takes only two steps:

Creating and registering the component

Modifying the component class


Creating and registering the component

Topic groups

Creation of every component begins the same way: you create a unit, derive a component class, register it, and install it on the Component palette. Creating a new component.

For this example, follow the general procedure for creating a component, with these specifics:

1	Name the component’s unit YelMemo and save it, so that the header file is YELMEMO.H and the .CPP file is YELMEMO.CPP.

2	Derive a new component class called TYellowMemo, descended from TMemo.

3	Register TYellowMemo on the Samples page of the Component palette.

The resulting header file should look like this:

#ifndef YelMemoH

#define YelmemoH

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

#include <vcl\sysutils.hpp>

#include <vcl\controls.hpp>

#include <vcl\classes.hpp>

#include <vcl\forms.hpp>

#include <vcl\StdCtrls.hpp>

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

class PACKAGE TYellowMemo : public TMemo

{

private:

protected:

public:

__published:

};

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

#endif

The accompanying .CPP file should look like this:

#include <vcl\vcl.h>

#pragma hdrstop

#include "Yelmemo.h"

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

#pragma package(smart_init);

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

static inline TYellowMemo *ValidCtrCheck()

{

  return new TYellowMemo(NULL);

}

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

namespace Yelmemo

{

  void __fastcall Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

Note:	This example assumes you are not using the Component wizard to create this component, but that you are creating it manually. If you use the Component wizard, a constructor will automatically be added to TYellowMemo.


Modifying the component object

Topic groups

Once you have created a new component class, you can modify it in almost any way. In this case, you will change only the initial value of one property in the memo component. This involves two small changes to the component class:

Overriding the constructor.

Specifying the new default property value.

The constructor actually sets the value of the property. The default tells C++Builder what values to store in the form (.DFM) file. C++Builder stores only values that differ from the default, so it is important to perform both steps.


Overriding the constructor

Topic groups

When a component is placed on a form at design time, or when an application constructs a component at runtime, the component’s constructor sets the property values. When a component is loaded from a form file, the application sets any properties changed at design time.

Note:	When you override a constructor, the new constructor must call the inherited constructor before doing anything else. For more information, see Overriding methods.

For this example, your new component needs to override the constructor inherited from TMemo to set the Color property to clYellow. To achieve this, add the declaration of the constructor override to the class declaration, then write the new constructor in the .CPP part file:

class PACKAGE TYellowMemo : public TMemo

{

public:

    virtual __fastcall TYellowMemo(TComponent* Owner); // the constructor declaration

__published:

    __property Color;

};

__fastcall TYellowMemo::TYellowMemo(TComponent* Owner)

  : TMemo(Owner)                                // the constructor implementation first...                                                  // ...calls the constructor for TMemo

{

  Color = clYellow;                            // colors the component yellow

}

Note:	If you used the Component wizard to create the component, all you need to do is add Color = clYellow; to the existing constructor.

Now you can install the new component on the Component palette and add it to a form. Note that the Color property now defaults to clYellow.


Specifying the new default property value

Topic groups

When C++Builder stores a description of a form in a form file, it stores the values only of properties that differ from their defaults. Storing only the differing values keeps the form files small and makes loading the form faster. If you create a property or change the default value, it is a good idea to update the property declaration to include the new default. Form files, loading, and default values are explained in more detail in Chapter 37, “Making components available at design time.”Making components available at design time

To change the default value of a property,

1	Redeclare the property name.

2	Place an equal sign (=) after the property name.

3	Type the default keyword, another equal sign, and the default value all within braces.

Note that you do not need to redeclare the entire property, just the name and the default value.

For the yellow memo component, you redeclare the Color property in a __published part of the class declaration, with a default value of clYellow:

class PACKAGE TYellowMemo : public TMemo

{

public:

    virutal __fastcall TYellowMemo(TComponent* Owner);

__published:

    __property Color = {default=clYellow};

};

Here is TYellowMemo again, but this time it has another published property, WordWrap, with a default value of false:

class PACKAGE TYellowMemo : public TMemo

{

public:

    virtual __fastcall TYellowMemo(TComponent* Owner);

__published:

    __property Color = {default=clYellow};

    __property WordWrap = {default=false};

};

Specifying the default property value does not affect the workings of the component at all. You must still explicitly set the default value in the component’s constructor. The difference is in the inner workings of the application. For TYellowMemo, C++Builder no longer writes WordWrap to the form file if it is false, because you have told it that the constructor will set that value automatically. Here is the constructor:

__fastcall TYellowMemo::TYellowMemo(TComponent* AOwner) : TMemo(AOwner)

{

  Color = clYellow;

  WordWrap = false;

}


Creating a graphic component

Topic groups

A graphic control is a simple kind of component. Because a purely graphic control never receives focus, it does not have or need a window handle. Users can still manipulate the control with the mouse, but there is no keyboard interface.

The graphic component presented in this chapter is TShape, the shape component is on the Additional page of the Component palette. Although the component created is identical to the standard shape component, you need to call it something different to avoid duplicate identifiers. This chapter calls its shape component TSampleShape and shows you all the steps involved in creating the shape component:

Creating and registering the component

Publishing inherited properties

Adding graphic capabilities


Creating and registering the component

Topic groups

Creation of every component begins the same way: derive a component class, save the component’s .CPP and .H files, derive a component class, register it, compile it, and install it on the Component palette. Creating a new component.

Note:	This example assumes you are not using the Component wizard, but are creating the component manually.

For this example, follow the general procedure for creating a component, with these specifics:

Derive a new component type called TSampleShape, descended from TGraphicControl.

Call the component’s header file SHAPES.H and the .CPP file SHAPES.CPP.

Register TSampleShape on the Samples page of the Component palette.

The resulting header file should look like this:

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

#ifndef ShapesH

#define ShapesH

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

#include <vcl\sysutils.hpp>

#include <vcl\controls.hpp>

#include <vcl\classes.hpp>

#include <vcl\forms.hpp>

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

class PACKAGE TSampleShape : public TGraphicControl

{

private:

protected:

public:

__published:

};

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

#endif

The .CPP file should look like this:

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

#include <vcl\vcl.h>

#pragma hdrstop

#include "Shapes.h"

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

#pragma package(smart_init);

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

static inline TSampleShape *ValidCtrCheck()

{

  return new TSampleShape(NULL);

}

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

namespace Shapes

{

  void __fastcall Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

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


Publishing inherited properties

Topic groups

Once you derive a component type, you can decide which of the properties and events declared in the protected parts of the ancestor class you want to surface in the new component. TGraphicControl already publishes all the properties that enable the component to function as a control, so all you need to publish is the ability to respond to mouse events and handle drag-and-drop.

Publishing inherited properties and events is explained in Publishing inherited propertiesand Making events visible. Both processes involve redeclaring just the name of the properties in the published part of the class declaration.

For the shape control, you can publish the three mouse events, the three drag-and-drop events, and the two drag-and-drop properties:

class PACKAGE TSampleShape : public TGraphicControl

{

private:

__published:

    __property DragCursor ;

    __property DragMode ;

    __property OnDragDrop ;

    __property OnDragOver ;

    __property OnEndDrag ;

    __property OnMouseDown ;

    __property OnMouseMove ;

    __property OnMouseUp ;

};

The sample shape control now makes mouse and drag-and-drop interactions available to its users.


Adding graphic capabilities

Topic groups

Once you have declared your graphic component and published any inherited properties you want to make available, you can add the graphic capabilities that distinguish your component. You have two tasks to perform when creating a graphic control:

1	Determining what to draw.

2	Drawing the component image.

In addition, for the shape control example, you will add some properties that enable application developers to customize the appearance of the shape at design time.


Determining what to draw

Topic groups

A graphic control can change its appearance to reflect a dynamic condition, including user input. A graphic control that always looks the same should probably not be a component at all. If you want a static image, you can import the image instead of using a control.

In general, the appearance of a graphic control depends on some combination of its properties. The gauge control, for example, has properties that determine its shape and orientation and whether it shows its progress numerically as well as graphically. Similarly, the shape control has a property that determines what kind of shape it should draw.

To give your control a property that determines the shape it draws, add a property called Shape. This requires

1	Declaring the property type.

2	Declaring the property.

3	Writing the implementation method.

Creating properties is explained in more detail in Creating proeprties.


Declaring the property type

Topic groups

When you declare a property of a user-defined type, you must declare the type first, before the class that includes the property. The most common sort of user-defined type for properties is enumerated.

For the shape control, you need an enumerated type with an element for each kind of shape the control can draw.

Add the following type definition above the shape control class’s declaration.

enum TSampleShapeType { sstRectangle, sstSquare, sstRoundRect, sstRoundSquare,   sstEllipse, sstCircle };

class PACKAGE TSampleShape : public TGraphicControl         // this is already there

You can now use this type to declare a new property in the class.


Declaring the property

Topic groups

When you declare a property, you usually need to declare a private data member to store the data for the property, then specify methods for reading and writing the property value. Often, you don’t need to use a method to read the value, but can just point to the stored data instead.

For the shape control, you will declare a data member that holds the current shape, then declare a property that reads that data member and writes to it through a method call.

Add the following declarations to TSampleShape:

class PACKAGE TSampleShape : public TGraphicControl

{

private:

    TSampleShapeType FShape;

    void __fastcall SetShape(TSampleShapeType Value);

__published:

    __property TSampleShapeType Shape = {read=FShape, write=SetShape, nodefault};

};

Now all that remains is to add the implementation of SetShape.


Writing the implementation method

Topic groups

When the read or write part of a property definition uses a method instead of directly accessing the stored property data, you need to implement the method.

Add the implementation of the SetShape method to the SHAPES.CPP file:

void __fastcall TSampleShape::SetShape(TSampleShapeType Value)

{

  if (FShape != Value)               // ignore if this isn’t a change

  {

     FShape = Value;               // store the new value

     Invalidate();                 // force a repaint with the new shape

  }

}


Overriding the constructor and destructor

Topic groups

To change default property values and initialize owned classes for your component, you must override the inherited constructor and destructor. In both cases, remember always to call the inherited method in your new constructor or destructor.

Changing default property values

The default size of a graphic control is fairly small, so you can change the width and height in the constructor. Changing default property values is explained in more detail in Modifying an existing component.

In this example, the shape control sets its size to a square 65 pixels on each side.

Add the overridden constructor to the declaration of the component class:

class PACKAGE TSampleShape : public TGraphicControl

{

public:

    virtual __fastcall TSampleShape(TComponent *Owner);

};

If you started creating the component with the Component wizard, this step will already be done for you.

1	Redeclare the Height and Width properties with their new default values:

class PACKAGE TSampleShape : public TGraphicControl

{

  ...

__published:

    __property Height;

    __property Width;

}

2	Write the new constructor in the .CPP file:

__fastcall TSampleShape::TSampleShape(TComponent* Owner) : TGraphicControl(Owner)

{

  Width = 65;

  Height = 65;

}

If you used the Component wizard, all you need to do is add the new default values to the already existing constructor.


Publishing the pen and brush

Topic groups

By default, a canvas has a thin black pen and a solid white brush. To let developers change the pen and brush, you must provide classes for them to manipulate at design time, then copy the classes into the canvas during painting. Classes such as an auxiliary pen or brush are called owned classes because the component owns them and is responsible for creating and destroying them.

Managing owned classes requires

1	Declaring the data members.

2	Declaring the access properties.

3	Initializing owned classes.

4	Setting owned classes’ properties.


Declaring the data members

Topic groups

Each class a component owns must have a data member declared for it in the component. The data member ensures that the component always has a pointer to the owned object so that it can destroy the class before destroying itself. In general, a component initializes owned objects in its constructor and destroys them in its destructor.

Data members for owned objects are nearly always declared as private. If applications (or other components) need access to the owned objects, you can declare published or public properties for this purpose.

Add data members for a pen and brush to the shape control:

class PACKAGE TSampleShape : public TGraphicControl

{

private:               // data members are always private

    TPen *FPen;        // a data member for the pen object

    TBrush *FBrush;    // a data member for the brush object

    ...

};


Declaring the access properties

Topic groups

You can provide access to the owned objects of a component by declaring properties of the type of the objects. That gives developers a way to access the objects at design time or runtime. Usually, the read part of the property just references the data member, but the write part calls a method that enables the component to react to changes in the owned object.

To the shape control, add properties that provide access to the pen and brush data members. You will also declare methods for reacting to changes to the pen or brush.

class PACKAGE TSampleShape : public TGraphicControl

{

  ...

private:

    TPen *FPen;

    TBrush *FBrush;

    void __fastcall SetBrush(TBrush *Value);

    void __fastcall SetPen(TPen *Value);

    ...

__published:

    __property TBrush* Brush = {read=FBrush, write=SetBrush, nodefault};

    __property TPen* Pen = {read=FPen, write=SetPen, nodefault};

};

Then, write the SetBrush and SetPen methods in the .CPP file:

void __fastcall TSampleShape::SetBrush( TBrush* Value)

{

  FBrush->Assign(Value);

}

void __fastcall TSampleShape::SetPen( TPen* Value)

{

  FPen->Assign(Value);

}

To directly assign the contents of Value to FBrush...

  FBrush = Value;

...would overwrite the internal pointer for FBrush, lose memory, and create a number of ownership problems.


Initializing owned classes

Topic groups

If you add classes to your component, the component’s constructor must initialize them so that the user can interact with the objects at runtime. Similarly, the component’s destructor must also destroy the owned objects before destroying the component itself.

Because you have added a pen and a brush to the shape control, you need to initialize them in the shape control’s constructor and destroy them in the control’s destructor:

1	Construct the pen and brush in the shape control constructor:

__fastcall TSampleShape::TSampleShape(TComponent* Owner) : TGraphicControl(Owner)

{

  Width = 65;

  Height = 65;

  FBrush = new TBrush();                               // construct the pen

  FPen = new TPen();                                   // construct the brush

}

2	Add the overridden destructor to the declaration of the component class:

class PACKAGE TSampleShape : public TGraphicControl

{

  ...

public:                                                // destructors are always public

    virtual __fastcall TSampleShape(TComponent* Owner);

    __fastcall ~TSampleShape();                        // the destructor

    ...

};

3	Write the new destructor in the .CPP file:

__fastcall TSampleShape::~TSampleShape()

{

  delete FPen;                                         // delete the pen object

  delete FBrush;                                       // delete the brush object

}


Setting owned classes' properties
Setting owned classes’ properties

Topic groups

As the final step in handling the pen and brush classes, you need to make sure that changes in the pen and brush cause the shape control to repaint itself. Both pen and brush classes have OnChange events, so you can create a method in the shape control and point both OnChange events to it.

Add the following method to the shape control, and update the component’s constructor to set the pen and brush events to the new method:

class PACKAGE TSampleShape : public TGraphicControl

{

  ...

public:

    void __fastcall StyleChanged(TObject* Owner);

    ...

};

In the SHAPES.CPP file, assign the StyleChanged method to the OnChange events for the pen and brush classes in the TSampleShape constructor:

__fastcall TSampleShape::TSampleShape(TComponent* Owner) : TGraphicControl(Owner)

{

  Width = 65;

  Height = 65;

  FBrush = new TBrush();

  FBrush->OnChange = StyleChanged;

  FPen = new TPen();

  FPen->OnChange = StyleChanged;

}

Include the implementation of the StyleChanged method:

void __fastcall TSampleShape::StyleChanged( TObject* Sender)

{

  Invalidate();         // repaints the component

}

With these changes, the component redraws to reflect changes to either the pen or the brush.


Drawing the component image

Topic groups

The essential element of a graphic control is the way it paints its image on the screen. The abstract type TGraphicControl defines a method called Paint that you override to paint the image you want on your control.

The Paint method for the shape control needs to do several things:

Use the pen and brush selected by the user.

Use the selected shape.

Adjust coordinates so that squares and circles use the same width and height.

Overriding the Paint method requires two steps:

1	Add Paint to the component’s declaration.

2	Write the Paint method in the .CPP file.

For the shape control, add the following declaration to the class declaration:

class PACKAGE TSampleShape : public TGraphicControl

{

  ...

protected:

    virtual void __fastcall Paint();

    ...

};

Then write the method in the .CPP file:

void __fastcall TSampleShape::Paint()

{

  int X,Y,W,H,S;

  Canvas->Pen = FPen;                                // copy the component’s pen

  Canvas->Brush = FBrush;                            // copy the component’s brush

  W=Width;                                           // use the component width

  H=Height;                                          // use the component height

  X=Y=0;                                             // save smallest for circles/squares

  if( W<H ) S=W;

  else

  S=H;

  switch(FShape)

  {

    case sstRectangle:                                // draw rectangles and squares

    case sstSquare:

      Canvas->Rectangle(X,Y,X+W,Y+H);

      break;

    case sstRoundRect:                                // draw rounded rectangles and squares

    case sstRoundSquare:

      Canvas->RoundRect(X,Y,X+W,Y+H,S/4,S/4);

      break;

    case sstCircle:                                   // draw circles and ellipses

    case sstEllipse:

      Canvas->Ellipse(X,Y,X+W,Y+H);

      break;

    default:

      break;

  }

}

Paint is called whenever the control needs to update its image. Windows tells controls to paint when they first appear or when a window in front of them goes away. In addition, you can force repainting by calling Invalidate, as the StyleChanged method does.


Refining the shape drawing

Topic groups

The standard shape control does one more thing that your sample shape control does not yet do: it handles squares and circles as well as rectangles and ellipses. To do that, you need to write code that finds the shortest side and centers the image.

Here is a refined Paint method that adjusts for squares and ellipses:

void __fastcall TSampleShape::Paint(void)

{

  int X,Y,W,H,S;

  Canvas->Pen = FPen;                        // copy the component’s pen

  Canvas->Brush = FBrush;                    // copy the component’s brush

  W=Width;                                   // use the component width

  H=Height;                                  // use the component height

  X=Y=0;                                     // save smallest for circles/squares

  if( W<H ) S=W;

  else

  S=H;

  switch(FShape)                              // adjust height, width and position

  {

    case sstRectangle:

    case sstRoundRect:

    case sstEllipse:

      Y=X=0;                                // origin is top-left for these shapes

      break;

    case sstSquare:

    case sstRoundSquare:

    case sstCircle:

      X= (W-S)/2;                            // center these horizontally

      Y= (H-S)/2;                            // and vertically

      break;

    default:

    break;

  }

  switch(FShape)

  {

    case sstSquare:                          // draw rectangles and squares

      W=H=S;                                 // use shortest dimension for width and height

    case sstRectangle:

      Canvas->Rectangle(X,Y,X+W,Y+H);

      break;

    case sstRoundSquare:                     // draw rounded rectangles and squares

      W=H=S;

    case sstRoundRect:

      Canvas->RoundRect(X,Y,X+W,Y+H,S/4,S/4);

      break;

    case sstCircle:                          // draw circles and ellipses

      W=H=S;

    case sstEllipse:

      Canvas->Ellipse(X,Y,X+W,Y+H);

      break;

    default:

      break;

  }

}


Customizing a grid: Overview
Customizing a grid

Topic groups

C++Builder provides abstract components you can use as the basis for customized components. The most important of these are grids and list boxes. In this chapter, you will see how to create a small one-month calendar from the basic grid component, TCustomGrid.

Creating the calendar involves these tasks:

Creating and registering the component

Publishing inherited properties

Changing initial values

Resizing the cells

Filling in the cells

Navigating months and years

Navigating days

The resulting component is similar to the TCalendar component on the Samples page of the Component palette.


Creating and registering the component

Topic groups

Creation of every component begins the same way: derive a component class, save the component’s .CPP and .H files, derive a component class, register it, compile it, and install it on the Component palette. Creating a new component.

For this example, follow the general procedure for creating a component, with these specifics:

Derive a new component type called TSampleCalendar, descended from TCustomGrid.

Call the component’s header file CALSAMP.H and its .CPP file CALSAMP.CPP.

Register TSampleCalendar on the Samples page of the Component palette.

The resulting header file should look like this:

#ifndef CalSampH

#define CalSampH

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

#include <vcl\sysutils.hpp>

#include <vcl\controls.hpp>

#include <vcl\classes.hpp>

#include <vcl\forms.hpp>

#include <vcl\grids.hpp>

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

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

protected:

public:

__published:

};

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

#endif

The CALSAMP.CPP file should resemble this:

#include <vcl\vcl.h>

#pragma hdrstop

#include "CalSamp.h"

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

#pragma package(smart_init);

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

static inline TSampleCalendar *ValidCtrCheck()

{

  return new TSampleCalendar(NULL);

}

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

namespace Calsamp

{

  void __fastcall PACKAGE Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

NoteIf you used the Component wizard to create the component, the header file will also declare a new constructor, and the CALSAMP.CPP file will have the beginnings of a constructor. If the constructor is not there, you can add it later.


Publishing inherited properties

Topic groups

The abstract grid component, TCustomGrid, provides a large number of protected properties. You can choose which of those properties you want to make available to users of the calendar control.

To make inherited protected properties available to users of your components, redeclare the properties in the _published part of your component’s declaration.

For the calendar control, publish the following properties and events, as shown here:

class PACKAGE TSampleCalendar : public TCustomGrid

{

...

__published:

    __property Align ;                    // publish properties

    __property BorderStyle ;

    __property Color ;

    __property Ctl3D ;

    __property Font ;

    __property GridLineWidth ;

    __property ParentColor ;

    __property ParentCtl3D ;

    __property ParentFont ;

    __property OnClick ;                   // publish events

    __property OnDblClick ;

    __property OnDragDrop ;

    __property OnDragOver ;

    __property OnEndDrag ;

    __property OnKeyDown ;

    __property OnKeyPress ;

    __property OnKeyUp ;

};

There are a number of other properties you could also publish, but which do not apply to a calendar, such as the Options property that would enable the user to choose which grid lines to draw.

If you install the modified calendar component to the Component palette and use it in an application, you will find many more properties and events available in the calendar, all fully functional. You can now start adding new capabilities of your own design.


Changing initial values

Topic groups

A calendar is essentially a grid with a fixed number of rows and columns, although not all the rows always contain dates. For this reason, you have not published the grid properties ColCount and RowCount, because it is highly unlikely that users of the calendar will want to display anything other than seven days per week. You still must set the initial values of those properties so that the week always has seven days, however.

To change the initial values of the component’s properties, override the constructor to set the desired values.

Remember that you need to add the constructor to the public part of the component’s class declaration, then write the new constructor in the header file:

class PACKAGE TSampleCalendar : public TCustomGrid

{

protected:

    virtual void __fastcall DrawCell(long ACol, long ARow, const Windows::TRect &Rect,

      TGridDrawState AState);

    ...

public:

    virtual __fastcall TSampleCalendar(TComponent *Owner);        // the added constructor

    ...

};

In the CALSAMP.CPP file, write the constructor code:

__fastcall TSampleCalendar::TSampleCalendar(TComponent *Owner) : TCustomGrid(Owner)

{

  ColCount = 7;

  RowCount = 7;

  FixedCols = 0;

  FixedRows = 1;

  ScrollBars = ssNone;

  Options = (Options >> goRangeSelect) << goDrawFocusSelected;

}

void __fastcall TSampleCalendar::DrawCell(int ACol, int ARow, const Windows::TRect

  &ARect, TGridDrawState AState)

{

}

Note:	You will notice that a DrawCell method was also added to the class declaration, and in the .CPP file, the DrawCell method was started. This is not absolutely necessary now, but if you should attempt to test TSampleCalendar before overriding DrawCell, you would encounter a pure virtual function error. This is because TCustomGrid is an abstract class. Overriding DrawCell is discussed later in the “Filling in the cells” section.

The calendar now has seven columns and seven rows, with the top row fixed, or nonscrolling.


Resizing the cells

Topic groups

When a user or application changes the size of a window or control, Windows sends a message called WM_SIZE to the affected window or control so it can adjust any settings needed to later paint its image in the new size. Your component can respond to that message by altering the size of the cells so they all fit inside the boundaries of the control. To respond to the WM_SIZE message, you will add a message-handling method to the component.

Creating a message-handling method is described in detail in the section Creating new message handlers.

In this case, the calendar control needs a response to WM_SIZE, so add a protected method called WMSize to the control, then write the method so that it calculates the proper cell size to allow all cells to be visible in the new size:

class PACKAGE TSampleCalendar : public TCustomGrid

{

...

protected:

    void __fastcall WMSize(TWMSize &Message);

BEGIN_MESSAGE_MAP

  MESSAGE_HANDLER(WM_SIZE, TWMSize, WMSize)

END_MESSAGE_MAP(TCustomGrid)

};

Here is the code for the method in the CALSAMP.CPP file:

void __fastcall TSampleCalendar::WMSize(TWMSize Message)

{

  int GridLines;                                  // temporary local variable

  GridLines = 6 * GridLineWidth;                  // calculated combined size of all lines

  DefaultColWidth = (Message.Width - GridLines) / 7;    // set new default cell width

  DefaultRowHeight = (Message.Height - GridLines) / 7;  // and cell height

}

Now when the calendar is resized, it displays all the cells in the largest size that will fit in the control.


Filling in the cells

Topic groups

A grid control fills in its contents cell-by-cell. In the case of the calendar, that means calculating which date, if any, belongs in each cell. The default drawing for grid cells takes place in a virtual method called DrawCell.

To fill in the contents of grid cells, override the DrawCell method.

The easiest part to fill in is the heading cells in the fixed row. The runtime library contains an array with short day names, so for the calendar, use the appropriate one for each column:

Here is the code for the DrawCell method:

void __fastcall TSampleCalendar::DrawCell(long ACol, long ARow, const Windows::TRect &ARect,

  TGridDrawState AState)

{

  String TheText;

  int TempDay;

  if (ARow == 0) TheText = ShortDayNames[ACol + 1];

  else

  {

    TheText = "";

    TempDay = DayNum(ACol, ARow);                   // DayNum is defined later

    if (TempDay != -1) TheText = IntToStr(TempDay);

  }

  Canvas->TextRect(ARect, ARect.Left + (ARect.Right - ARect.Left

   - Canvas->TextWidth(TheText)) / 2,

  ARect.Top + (ARect.Bottom - ARect.Top - Canvas->TextHeight(TheText)) / 2, TheText);

}


Tracking the date

Topic groups

For the calendar control to be useful, users and applications must have a mechanism for setting the day, month, and year. C++Builder stores dates and times in variables of type TDateTime. TDateTime is an encoded numeric representation of the date and time, which is useful for programmatic manipulation, but not convenient for human use.

You can therefore store the date in encoded form, providing runtime access to that value, but also provide Day, Month, and Year properties that users of the calendar component can set at design time.

Tracking the date in the calendar consists of the processes:

Storing the internal date

Accessing the day, month, and year

Generating the day numbers

Selecting the current day


Storing the internal date

Topic groups

To store the date for the calendar, you need a private data member to hold the date and a runtime-only property that provides access to that date.

1	Declare a private data member to hold the date:

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

    TDateTime FDate;

    ...

};

2	Initialize the date data member in the constructor:

__fastcall TSampleCalendar::TSampleCalendar(TComponent *Owner) : TCustomGrid(Owner)

{

  ...

  FDate = FDate.CurrentDate();

}

3	Declare a runtime property to allow access to the encoded date:

class PACKAGE TSampleCalendar : public TCustomGrid

{

public:

    __property TDateTime CalendarDate = {read=FDate, write=SetCalendarDate, nodefault};

    ...

};

You will need a method for setting the date, because setting the date requires updating the onscreen image of the control. Declare SetCalendarDate in TSampleCalendar:

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

    void __fastcall SetCalendarDate(TDateTime Value);

    ...

};

This is the SetCalendarDate method:

void __fastcall TSampleCalendar::SetCalendarDate(TDateTime Value)

{

  FDate = Value;                      // Set the new date value

  Refresh();                          // Update the onscreen image

}


Accessing the day, month, and year

Topic groups

An encoded numeric date is fine for applications, but humans prefer to work with days, months, and years. You can provide alternate access to those elements of the stored, encoded date by creating properties.

Because each element of the date (day, month, and year) is an integer, and because setting each requires encoding the date when set, you can avoid duplicating the code each time by sharing the implementation methods for all three properties. That is, you can write two methods, one to read an element and one to write one, and use those methods to get and set all three properties.

To provide design-time access to the day, month, and year, you do the following:

1	Declare the three properties, assigning each a unique index number:

class PACKAGE TSampleCalendar : public TCustomGrid

{

  ...

public:

    __property int Day = {read=GetDateElement, write=SetDateElement, index=3,

      nodefault};

    __property int Month = {read=GetDateElement, write=SetDateElement, index=2,       nodefault};

    __property int Year = {read=GetDateElement, write=SetDateElement, index=1,       nodefault};

};

2	Declare and write the implementation methods, setting different elements for each index value:

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

    int __fastcall GetDateElement(int Index);           // note the Index parameter

    void __fastcall SetDateElement(int Index, int Value);

    ...

};

Here are the GetDateElement and SetDateElement methods:

int __fastcall TSampleCalendar::GetDateElement(int Index)

{

  unsigned short AYear, AMonth, ADay;

  int result;

  FDate.DecodeDate(&AYear, &AMonth, &ADay);            // break encoded date into elements

  switch (Index)

  {

    case 1: result = AYear;  break;

    case 2: result = AMonth; break;

    case 3: result = ADay;   break;

    default: result = -1;

  }

  return result;

}

void __fastcall TSampleCalendar::SetDateElement(int Index, int Value)

{

  unsigned short AYear, AMonth, ADay;

  if (Value > 0)                                   // all elements must be positive

  {

    FDate.DecodeDate(&AYear, &AMonth, &ADay);     // get current date elements

    switch (Index)

    {

      case 1: AYear = Value;     break;

      case 2: AMonth = Value;    break;

      case 3: ADay = Value;      break;

      default: return;

    }

  }

  FDate = TDateTime(AYear, AMonth, ADay);         // encode the modified date

  Refresh();                                      // update the visible calendar

}

Now you can set the calendar’s day, month, and year at design time using the Object Inspector or at runtime using code. Of course, you have not yet added the code to paint the dates into the cells, but now you have the needed data.


Generating the day numbers

Topic groups

Putting numbers into the calendar involves several considerations. The number of days in the month depends on which month it is, and whether the given year is a leap year. In addition, months start on different days of the week, dependent on the month and year. Use the IsLeapYear function to determine whether the year is a leap year. Use the MonthDays array in the SysUtils header file to get the number of days in the month.

Once you have the information on leap years and days per month, you can calculate where in the grid the individual dates go. The calculation is based on the day of the week the month starts on.

Because you will need the month-offset number for each cell you fill in, the best practice is to calculate it once when you change the month or year, then refer to it each time. You can store the value in a class data member, then update that data member each time the date changes.

To fill in the days in the proper cells, you do the following:

1	Add a month-offset data member to the class and a method that updates the data member value:

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

    int FMonthOffset;                         // storage for the offset

    ...

protected:

    virtual void __fastcall UpdateCalendar(void);

    ...

};

void __fastcall TSampleCalendar::UpdateCalendar(void)

{

  unsigned short AYear, AMonth, ADay;

  TDateTime FirstDate;                          // date of first day of the month

  if ((int)FDate != 0)                          // only calculate offset if date is valid

  {

    FDate.DecodeDate(&AYear, &AMonth, &ADay);  // get elements of date

    FirstDate = TDateTime(AYear, AMonth, 1);   // date of the first

    FMonthOffset = 2 - FirstDate.DayOfWeek();  // generate the offset into the grid

  }

  Refresh();                                    // always repaint the control

}

2	Add statements to the constructor and the SetCalendarDate and SetDateElement methods that call the new update method whenever the date changes:

__fastcall TSampleCalendar::TSampleCalendar(TComponent *Owner)

  : TCustomGrid(Owner)

{

  ...

  UpdateCalendar();

}

void __fastcall TSampleCalendar::SetCalendarDate(TDateTime Value)

{

  FDate = Value;                                  // this was already here

  UpdateCalendar();                               // this previously called Refresh

}

void __fastcall TSampleCalendar::SetDateElement(int Index, int Value)

{

  ...

  FDate = TDateTime(AYear, AMonth, ADay);        // this was already here

  UpdateCalendar();                              // this previously called Refresh

}

3	Add a method to the calendar that returns the day number when passed the row and column coordinates of a cell:

int __fastcall TSampleCalendar::DayNum(int ACol, int ARow)

{

  int result = FMonthOffset + ACol + (ARow - 1) * 7;       // calculate day for this cell

  if ((result < 1)||(result > MonthDays[IsLeapYear(Year)][Month]))

    result = -1;   // return -1 if invalid

  return result;

}

Remember to add the declaration of DayNum to the component’s type declaration.

4	Now that you can calculate where the dates go, you can update DrawCell to fill in the dates:

void __fastcall TSampleCalendar::DrawCell(long ACol, long ARow, const TRect &ARect,

  TGridDrawState AState)

{

  String TheText;

  int TempDay;

  if (ARow == 0)                                    // this is the header row

    TheText = ShortDayNames[ACol + 1];              // just use the day name

  else

  {

    TheText = "";                                   // blank cell is the default

    TempDay = DayNum(ACol, ARow);                   // get number for this cell

    if (TempDay != -1) TheText = IntToStr(TempDay); // use the number if valid

  }

  Canvas->TextRect(ARect, ARect.Left + (ARect.Right - ARect.Left -

    Canvas->TextWidth(TheText)) / 2,

  ARect.Top + (ARect.Bottom - ARect.Top - Canvas->TextHeight(TheText)) / 2, TheText);

}

Now if you reinstall the calendar component and place one on a form, you will see the proper information for the current month.


Selecting the current day

Topic groups

Now that you have numbers in the calendar cells, it makes sense to move the selection highlighting to the cell containing the current day. By default, the selection starts on the top left cell, so you need to set the Row and Column properties both when constructing the calendar initially and when the date changes.

To set the selection on the current day, change the UpdateCalendar method to set Row and Column before calling Refresh:

void __fastcall TSampleCalendar::UpdateCalendar(void)

{

  unsigned short AYear, AMonth, ADay;

  TDateTime FirstDate;

  if ((int) FDate != 0)

  {

  ...                                           // existing statements to set FMonthOffset

    Row = (ADay - FMonthOffset) / 7 + 1;

    Col = (ADay - FMonthOffset) % 7;

  }

  Refresh();                                  // this is already here

}

Note that you are now reusing the ADay variable previously set by decoding the date.


Navigating months and years

Topic groups

Properties are useful for manipulating components, especially at design time. But sometimes there are types of manipulations that are so common or natural, often involving more than one property, that it makes sense to provide methods to handle them. One example of such a natural manipulation is a “next month” feature for a calendar. Handling the wrapping around of months and incrementing of years is simple, but very convenient for the developer using the component.

The only drawback to encapsulating common manipulations into methods is that methods are only available at runtime. However, such manipulations are generally only cumbersome when performed repeatedly, and that is fairly rare at design time.

For the calendar, add the following four methods for next and previous month and year. Each of these methods uses the IncMonth function in a slightly different manner to increment or decrement CalendarDate, by increments of a month or a year. After incrementing or decrementing CalendarDate, decode the date value to fill the Year, Month, and Day properties with corresponding new values.

void __fastcall TSampleCalendar::NextMonth()

{

  DecodeDate(IncMonth(CalendarDate, 1), Year, Month, Day);

}

void __fastcall TSampleCalendar::PrevMonth()

{

  DecodeDate(IncMonth(CalendarDate, -1), Year, Month, Day);

}

void __fastcall TSampleCalendar::NextYear()

(

  DecodeDate(IncMonth(CalendarDate, 12), Year, Month, Day);

)

void __fastcall TSampleCalendar::PrevYear()

{

  DecodeDate(IncMonth(CalendarDate, -12), Year, Month, Day);

}

Be sure to add the declarations of the new methods to the class declaration.

Now when you create an application that uses the calendar component, you can easily implement browsing through months or years.


Navigating days

Topic groups

Within a given month, there are two obvious ways to navigate among the days. The first is to use the arrow keys, and the other is to respond to clicks of the mouse. The standard grid component handles both as if they were clicks. That is, an arrow movement is treated like a click on an adjacent cell.

The process of navigating days consists of

Moving the selection

Providing an OnChange event

Excluding blank cells


Moving the selection

Topic groups

The inherited behavior of a grid handles moving the selection in response to either arrow keys or clicks, but if you want to change the selected day, you need to modify that default behavior.

To handle movements within the calendar, override the Click method of the grid.

When you override a method such as Click that is tied in with user interactions, you will nearly always include a call to the inherited method, so as not to lose the standard behavior.

The following is an overridden Click method for the calendar grid. Be sure to add the declaration of Click to TSampleCalendar.

void __fastcall TSampleCalendar::Click()

{

  int TempDay = DayNum(Col, Row);            // get the day number for the clicked cell

  if (TempDay != -1) Day = TempDay;          // change day if valid

}


Providing an OnChange event

Topic groups

Now that users of the calendar can change the date within the calendar, it makes sense to allow applications to respond to those changes.

Add an OnChange event to TSampleCalendar.

1	Declare the event, a data member to store the event, and a virtual method to call the event:

class PACKAGE TSampleCalendar : public TCustomGrid

{

private:

    TNotifyEvent FOnChange;

    ...

protected:

    virtual void __fastcall Change();

__published:

    __property TNotifyEvent OnChange = {read=FOnChange, write=FOnChange};

    ...

}

2	Write the Change method:

void __fastcall TSampleCalendar::Change()

{

  if(FOnChange != NULL) FOnChange(this);

}

3	Add statements calling Change to the end of the SetCalendarDate and SetDateElement methods:

void __fastcall TSampleCalendar::SetCalendarDate(TDateTime Value)

{

  FDate = Value;

  UpdateCalendar();

  Change();              // this is the only new statement

}

void __fastcall TSampleCalendar::SetDateElement(int Index, int Value)

{

  ...   // many statements setting element values

  FDate = TDateTime(AYear, AMonth, ADay);

  UpdateCalendar();

  Change();              // this is new

}

Applications using the calendar component can now respond to changes in the date of the component by attaching handlers to the OnChange event.


Excluding blank cells

Topic groups

As the calendar is written, the user can select a blank cell, but the date does not change. It makes sense, then, to disallow selection of the blank cells.

To control whether a given cell is selectable, override the SelectCell method of the grid.

SelectCell is a function that takes a column and row as parameters, and returns a Boolean value indicating whether the specified cell is selectable.

You can override SelectCell to return false if the cell does not contain a valid date:

bool __fastcall TSampleCalendar::SelectCell(long ACol, long ARow)

{

  if (DayNum(ACol,ARow) == -1) return false;          // -1 indicates invalid date

  else return TCustomGrid::SelectCell(ACol, ARow);    // otherwise, use inherited value

}

Now if the user clicks a blank cell or tries to move to one with an arrow key, the calendar leaves the current cell selected.


Making a control data aware
Making a control data-aware

Topic groups

When working with database connections, it is often convenient to have controls that are data aware. That is, the application can establish a link between the control and some part of a database. C++Builder includes data-aware labels, edit boxes, list boxes, combo boxes, lookup controls, and grids. You can also make your own controls data aware. For more information about using data-aware controls, see Using data controls.

There are several degrees of data awareness. The simplest is read-only data awareness, or data browsing, the ability to reflect the current state of a database. More complicated is editable data awareness, or data editing, where the user can edit the values in the database by manipulating the control. Note also that the degree of involvement with the database can vary, from the simplest case, a link with a single field, to more complex cases, such as multiple-record controls.

This section first illustrates the simplest case, making a read-only control that links to a single field in a dataset. The specific control used will be the calendar created in Customizing a grid, TSampleCalendar. You can also use the standard calendar control on the Samples page of the Component palette, TCalendar.

The section then continues with an explanation of how to make the new data-browsing control a data-editing control.


Creating a data-browsing control

Topic groups

Creating a data-aware calendar control, whether it is a read-only control or one in which the user can change the underlying data in the dataset, involves the following steps:

Creating and registering the component.

Adding the data link.

Responding to data changes.


Creating and registering the component

Topic groups	Example

Creation of every component begins the same way: derive a component class, save the component’s .CPP and .H files, derive a component class, register it, compile it, and install it on the Component palette. This process is outlined in Creating a new component.

For this example, follow the general procedure for creating a component, with these specifics:

Derive a new component class called TDBCalendar, descended from TSampleCalendar.The sectionCustomizing a gridName the header file DBCAL.H and the .CPP file DBCAL.CPP.

Register TDBCalendar on the Samples page of the Component palette.


Example: Creating and registering a component

The resulting header file should look like this:

#ifndef DBCalH

#define DBCalH

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

#include <vcl\sysutils.hpp>

#include <vcl\controls.hpp>

#include <vcl\classes.hpp>

#include <vcl\forms.hpp>

#include <vcl\grids.hpp>              // include the Grids header

#include “calsamp.h”                  // include the header that declares TSampleCalendar

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

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

protected:

public:

__published:

};

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

#endif

The .CPP file should look like this:

#pragma link “Calsamp”                        // link in TSampleCalendar

#include <vcl\vcl.h>

#pragma hdrstop

#include "DBCal.h"

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

#pragma package(smart_init);

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

static inline TDBCalendar *ValidCtrCheck()

{

return new TDBCalendar(NULL);

}

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

namespace Dbcal

{

  void __fastcall PACKAGE Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

Note:	If you used the Component wizard to begin the TDBCalendar component, your header file will have the constructor already declared, and the .CPP file has the constructor’s definition.

You can now proceed with making the new calendar a data browser.


Making the control read-only

Topic groups

Because this data calendar will be read-only with respect to the data, it makes sense to make the control itself read-only, so users will not make changes within the control and expect them to be reflected in the database.

Making the calendar read-only involves,

Adding the ReadOnly property.

Allowing needed updates.

Note that if you started with the TCalendar component from C++Builder’s Samples page instead of TSampleCalendar, it already has a ReadOnly property, so you can skip these steps.


Adding the read-only property

Topic groups

By adding a ReadOnly property, you will provide a way to make the control read-only at design time. When that property is set to true, you can make all cells in the control unselectable.

1	Add the property declaration and a private data member to hold the value in the DBCAL.H file:

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

    bool FReadOnly;                                  // field for internal storage

protected:

public:

    virtual __fastcall TDBCalendar(TComponent* Owner);

__published:

    __property ReadOnly = {read=FReadOnly, write=FReadOnly, default=true};

};

2	Write the constructor in DBCAL.CPP:

virtual __fastcall TDBCalendar::TDBCalendar(TComponent* Owner) :

  TSampleCalendar(Owner)

{

  FReadOnly = true;                                   // sets the default value

}

3	Override the SelectCell method to disallow selection if the control is read-only. Use of SelectCell is explained in “Excluding blank cells” on page 40-13.

bool __fastcall TDBCalendar::SelectCell(long ACol, long ARow)

{

  if (FReadOnly) return false;                        // can’t select if read only

  return TSampleCalendar::SelectCell(ACol, ARow);     // otherwise, use inherited method

}

Remember to add the declaration of SelectCell to the class declaration of TDBCalendar.

If you now add the calendar to a form, you will find that the component ignores clicks and keystrokes. It also fails to update the selection position when you change the date.


Allowing needed updates

Topic groups

The read-only calendar uses the SelectCell method for all kinds of changes, including setting the Row and Col properties. The UpdateCalendar method sets Row and Col every time the date changes, but because SelectCell disallows changes, the selection remains in place, even though the date changes.

To get around this absolute prohibition on changes, you can add an internal Boolean flag to the calendar, and permit changes when that flag is set to true:

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

    ...

    bool FUpdating;                                  // private flag for internal use

protected:

    virtual bool __fastcall SelectCell(long ACol, long ARow);

public:

    ...

    virtual void __fastcall UpdateCalendar();

    ...

};

bool __fastcall TDBCalendar::SelectCell(long ACol, long ARow)

{

    if (!FUpdating && FReadOnly) return false;          // can’t select if read only

    return TSampleCalendar::SelectCell(ACol, ARow);     // otherwise, use inherited method

}

void __fastcall TDBCalendar::UpdateCalendar()

{

  FUpdating=true;                                     // set flag to allow updates

  try

  {

     TSampleCalendar::UpdateCalendar();              // update as usual

  }

  catch(...)

  {

    FUpdating = false;

    throw;

  }

  FUpdating = false;                                  // always clear the flag

}

The calendar still disallows user changes, but now correctly reflects changes made in the date by changing the date properties. Now that you have a true read-only calendar control, you are ready to add the data-browsing ability.


Adding the data link

Topic groups

The connection between a control and a database is handled by a class called a data link. The data-link class that connects a control with a single data member in a database is TFieldDataLink. There are also data links for entire tables.

A data-aware control owns its data-link class. That is, the control has the responsibility for constructing and destroying the data link. For details on management of owned classes, see Creating a graphic component.

Establishing a data link as an owned class requires these three steps:

1	Declaring the data member

2	Declaring the access properties

3	Initializing the data link


Declaring the datamember

Topic groups

A component needs a data member for each of its owned classes, as explained in Declaring the class fieldsdata members. In this case, the calendar needs a data member of type TFieldDataLink for its data link.

Declare a field for the data link in the calendar:

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

    TFieldDataLink *FDataLink;

    ...

};

Before you can compile the application, you need to include the DB.HPP and DBTABLES.HPP files in the DBCAL.H file:

#include <DB.hpp>

#include <DBTables.hpp>


Declaring the access properties for a data-aware control
Declaring the access properties

Topic groups	See also	Example

Every data-aware control has a DataSource property that specifies which data-source class in the application provides the data to the control. In addition, a control that accesses a single field needs a DataField property to specify that field in the data source.

Unlike the access properties for the owned classes in the example in Creating a graphic component, these access properties do not provide access to the owned classes themselves, but rather to corresponding properties in the owned class. That is, you will create properties that enable the control and its data link to share the same data source and field.

Declare the DataSource and DataField properties and their implementation methods, then write the methods as “pass-through” methods to the corresponding properties of the data-link class


Example: Declaring access properties

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

   ...

    AnsiString __fastcall GetDataField();             // methods are private

    TDataSource *__fastcall GetDataSource();          // returns name of data field

    void __fastcall SetDataField(AnsiString Value); // returns reference to data                                                           // source

    void __fastcall SetDataSource(TDataSource *Value);    // assigns name of data field

    ...

__published:                                // make properties available at design time

    __property AnsiString DataField = {read=GetDataField, write=SetDataField, nodefault};

    __property TDataSource * DataSource = {read=GetDataSource, write=SetDataSource,       nodefault};

   ...

};

AnsiString __fastcall TDBCalendar::GetDataField()

{

  return FDataLink->FieldName;

}

TDataSource *__fastcall TDBCalendar::GetDataSource()

{

  return FDataLink->DataSource;

}

void __fastcall TDBCalendar::SetDataField(AnsiString Value)

{

  FDataLink->FieldName = Value;

}

void __fastcall TDBCalendar::SetDataSource(TDataSource *Value)

{

  if(Value != NULL)

    Value->FreeNotification(this);

  FDataLink->DataSource = Value;

}

Now that you have established the links between the calendar and its data link, there is one more important step. You must construct the data link class when the calendar control is constructed, and destroy the data link before destroying the calendar.


Initializing the data link

Topic groups	See also

A data-aware control needs access to its data link throughout its existence, so it must construct the data-link object as part of its own constructor, and destroy the data-link object before it is itself destroyed.

Override the constructor and destructor of the calendar:

class PACKAGE TDBCalendar : public TSampleCalendar

{

public:

    virtual __fastcall TDBCalendar(TComponent *Owner);

    __fastcall ~TDBCalendar();

};

__fastcall TDBCalendar::TDBCalendar(TComponent* Owner) : TSampleCalendar(Owner)

{

    FReadOnly = true;

    FDataLink = new TFieldDataLink();

    FDataLink->Control = this;

}

__fastcall TDBCalendar::~TDBCalendar()

{

    FDataLink->Control = NULL;

    FDataLink-OnUpdateData = NULL;

    delete FDataLink;

}

Now you have a complete data link, but you have not yet told the control what data it should read from the linked field. The next section explains how to do that.


Responding to data changes

Topic groups	Example

Once a control has a data link and properties to specify the data source and data field, it needs to respond to changes in the data in that field, either because of a move to a different record or because of a change made to that field.

Data-link classes all have events named OnDataChange. When the data source indicates a change in its data, the data-link object calls any event handler attached to its OnDataChange event.

To update a control in response to data changes, attach a handler to the data link’s OnDataChange event.

In this case, you will add a method to the calendar, then designate it as the handler for the data link’s OnDataChange.

Declare and implement the DataChange method, then assign it to the data link’s OnDataChange event in the constructor. In the destructor, detach the OnDataChange handler before destroying the object.


Example: Responding to data changes

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

    void __fastcall DataChange(TObject *Sender);

    ...

};

void __fastcall TDBCalendar::DataChange( TObject* Sender)

{

  if (FDataLink->Field == NULL)                          // if no field is assigned ...

  CalendarDate = 0;                                      // ...set to invalid date

  else CalendarDate = FDataLink->Field->AsDateTime;  // otherwise, set to new data

}

__fastcall TDBCalendar::TDBCalendar(TComponent* Owner) :  TSampleCalendar(AOwner)

{

  FReadOnly = true;

  FDataLink = new TFieldDataLink();             // construct the data-link object

  FDataLink->Control = this;

  FDataLink->OnDataChange = DataChange;         // attach the handler

}

__fastcall TDBCalendar::~TDBCalendar()

{

  FDataLink->Control = NULL;

  FDataLink-OnUpdateData = NULL;

  FDataLink->OnDataChange = NULL;                 // detach the handler before...

  delete FDataLink;                               // ...destroying the data-link object

}

You now have a data-browsing control.


Creating a data-editing control

Topic groups

When you create a data-editing control, you create and register the component and add the data link just as you do for a data-browsing control. You also respond to data changes in the underlying field in a similar manner, but you must handle a few more issues.

For example, you probably want your control to respond to both key and mouse events. Your control must respond when the user changes the contents of the control. When the user exits the control, you want the changes made in the control to be reflected in the dataset.

The data-editing control described here is the same calendar control described in the first part of the chapter. The control is modified so that it can edit as well as view the data in its linked field.

Modifying the existing control to make it a data-editing control involves:

Changing the default value of FReadOnly.

Handling mouse-down and key-down messages.

Updating the field data-link class.

Modifying the Change method.

Updating the dataset.


Changing the default value of FReadOnly

Topic groups

Because this is a data-editing control, the ReadOnly property should be set to false by default. To make the ReadOnly property false, change the value of FReadOnly in the constructor:

__fastcall TDBCalendar::TDBCalendar (TComponent* Owner) : TSampleCalendar(Owner)

{

  FReadOnly = false;             // set the default value

  ...

}


Handling mouse-down and key-down messages

Topic groups

When the user of the control begins interacting with it, the control receives either mouse-down messages (WM_LBUTTONDOWN, WM_MBUTTONDOWN, or WM_RBUTTONDOWN) or a key-down message (WM_KEYDOWN) from Windows. To enable a control to respond to these messages, you must write handlers that respond to these messages.

Responding to mouse-down messages

Responding to key-down messages


Responding to mouse-down messages

Topic groups

A MouseDown method is a protected method for a control’s OnMouseDown event. The control itself calls MouseDown in response to a Windows mouse-down message. When you override the inherited MouseDown method, you can include code that provides other responses in addition to calling the OnMouseDown event.

To override MouseDown, add the MouseDown method to the TDBCalendar class:

class PACKAGE TDBCalendar : public TSampleCalendar

{

...

protected:

    virtual void __fastcall MouseDown(TMouseButton Button,  TShiftState Shift, int X,       int Y);

    ...

};

Write the MouseDown method in the .CPP file:

void __fastcall TDBCalendar::MouseDown(TMouseButton Button,  TShiftState Shift, int  X,   int Y)

{

  TMouseEvent MyMouseDown;                               // declare event type

  if (!FReadOnly && FDataLink->Edit())                   // if the field can be edited

    TSampleCalendar::MouseDown(Button, Shift, X, Y);   // call the inherited MouseDown

  else

  {

    MyMouseDown = OnMouseDown;                          // assign OnMouseDown event

    if (MyMouseDown != NULL) MyMouseDown(this, Button,  // execute code in the...

       Shift, X, Y);                                    // ...OnMouseDown event handler

  }

}

When MouseDown responds to a mouse-down message, the inherited MouseDown method is called only if the control’s ReadOnly property is false and the data-link object is in edit mode, which means the field can be edited. If the field cannot be edited, the code the programmer put in the OnMouseDown event handler, if one exists, is executed.


Responding to key-down messages

Topic groups

A KeyDown method is a protected method for a control’s OnKeyDown event. The control itself calls KeyDown in response to a Windows key-down message. When overriding the inherited KeyDown method, you can include code that provides other responses in addition to calling the OnKeyDown event.

To override KeyDown, follow these steps:

1	Add a KeyDown method to the TDBCalendar class:

class PACKAGE TDBCalendar : public TSampleCalendar

{

   ...

protected:

    virtual void __fastcall KeyDown(unsigned short &Key,  TShiftState Shift);

   ...

};

2	Write the KeyDown method in the .CPP file:

void __fastcall TDBCalendar::KeyDown(unsigned short &Key,  TShiftState Shift)

{

  TKeyEvent MyKeyDown;                                // declare event type

  Set<unsigned short,0,8> keySet;

  keySet = keySet << VK_UP << VK_DOWN << VK_LEFT      // assign virtual keys to set

    << VK_RIGHT << VK_END << VK_HOME << VK_PRIOR << VK_NEXT;

  if (!FReadOnly &&                              // if control is not read only...

      (keySet.Contains(Key)) &&                   // ...and key is in the set...

      FDataLink->Edit() )                         // ...and field is in edit mode

  {

    TCustomGrid::KeyDown(Key, Shift);          // call the inherited KeyDown method

  }

  else

  {

    MyKeyDown = OnKeyDown;                            // assign OnKeyDown event

    if (MyKeyDown != NULL) MyKeyDown(this,Key,Shift); // execute code in...

  }                                                      // ...OnKeyDown event handler

}

When KeyDown responds to a mouse-down message, the inherited KeyDown method is called only if the control’s ReadOnly property is false, the key pressed is one of the cursor control keys, and the data-link object is in edit mode, which means the field can be edited. If the field cannot be edited or some other key is pressed, the code the programmer put in the OnKeyDown event handler, if one exists, is executed.


Updating the field data-link object

Topic groups

There are two types of data changes:

A change in a field value that must be reflected in the data-aware control.

A change in the data-aware control that must be reflected in the field value.

The TDBCalendar component already has a DataChange method that handles a change in the field’s value in the dataset by assigning that value to the CalendarDate property. The DataChange method is the handler for the OnDataChange event. So the calendar component can handle the first type of data change.

Similarly, the field data-link class also has an OnUpdateData event that occurs as the user of the control modifies the contents of the data-aware control. The calendar control has a UpdateData method that becomes the event handler for the OnUpdateData event. UpdateData assigns the changed value in the data-aware control to the field data link.

1	To reflect a change made to the value in the calendar in the field value, add an UpdateData method to the private section of the calendar component:

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

    void __fastcall UpdateData(TObject *Sender);

};

2	Write the UpdateData method in the .CPP file:

void __fastcall TDBCalendar::UpdateData( TObject* Sender)

{

    FDataLink->Field->AsDateTime = CalendarDate;     // set field link to calendar date

}

3	Within the constructor for TDBCalendar, assign the UpdateData method to the OnUpdateData event:

__fastcall TDBCalendar::TDBCalendar(TComponent* Owner)

  : TSampleCalendar(Owner)

{

  FDataLink = new TFieldDataLink();       // this was already here

  FDataLink->OnDataChange = DataChange;   // this was here too

  FDataLink->OnUpdateData = UpdateData;   // assign UpdateData to the OnUpdateData event

}


Modifying the Change method

Topic groups

The Change method of the TDBCalendar is called whenever a new date value is set. Change calls the OnChange event handler, if one exists. The component user can write code in the OnChange event handler to respond to changes in the date.

When the calendar date changes, the underlying dataset should be notified that a change has occurred. You can do that by overriding the Change method and adding one more line of code. These are the steps to follow:

1	Add a new Change method to the TDBCalendar component:

class PACKAGE TDBCalendar : public TSampleCalendar

{

protected:

    virtual void __fastcall Change();

    ...

};

2	Write the Change method, calling the Modified method that informs the dataset the data has changed, then call the inherited Change method:

void __fastcall TDBCalendar::Change()

{

  if (FDataLink != NULL)

    FDataLink->Modified();                // call the Modified method

  TSampleCalendar::Change();              // call the inherited Change method

}


Updating the dataset

Topic groups

So far, a change within the data-aware control has changed values in the field data-link class. The final step in creating a data-editing control is to update the dataset with the new value. This should happen after the person changing the value in the data-aware control exits the control by clicking outside the control or pressing the Tab key.

VCL has defined message control IDs for operations on controls. For example, the CM_EXIT message is sent to the control when the user exits the control. You can write message handlers that respond to the message. In this case, when the user exits the control, the CMExit method, the message handler for CM_EXIT, responds by updating the record in the dataset with the changed values in the field data-link class. For more information about message handlers, see Handling messages.

To update the dataset within a message handler, follow these steps:

1	Add the message handler to the TDBCalendar component:

class PACKAGE TDBCalendar : public TSampleCalendar

{

private:

    void __fastcall CMExit(TWMNoParams Message);

BEGIN_MESSAGE_MAP

  MESSAGE_HANDLER(CM_EXIT, TWMNoParams, CMExit)

END_MESSAGE_MAP

};

2	Write the code in the .CPP file so that it looks like this:

void __fastcall TDBCalendar::CMExit(TWMNoParams &Message)

{

  try

  {

    FDataLink.UpdateRecord();          // tell data link to update database

  }

  catch(...)

  {

    SetFocus();                        // if it failed, don’t let focus leave

    throw;

  }

}


Making a dialog box a component: Overview
Making a dialog box a component

Topic groups

You will find it convenient to make a frequently used dialog box a component that you can add to the Component palette. Your dialog-box components will work just like the components that represent the standard Windows common dialog boxes. The goal is to create a simple component that a user can add to a project and set properties for at design time.

Making a dialog box a component requires these steps:

1	Defining the component interface

2	Creating and registering the component

3	Creating the component interface

4	Testing the component

The C++Builder “wrapper” component associated with the dialog box creates and executes the dialog box at runtime, passing along the data the user specified. The dialog-box component is therefore both reusable and customizable.

In this section, you will see how to create a wrapper component around the generic About Box form provided in the C++Builder Object Repository.

Note:	Copy the files ABOUT.H, ABOUT.CPP and ABOUT.DFM into your working directory. Add ABOUT.CPP to your project so that an ABOUT.OBJ file is created when your dialog wrapper component builds.

There are not many special considerations for designing a dialog box that will be wrapped into a component. Nearly any form can operate as a dialog box in this context.


Defining the component interface

Topic groups

Before you can create the component for your dialog box, you need to decide how you want developers to use it. You create an interface between your dialog box and applications that use it.

For example, look at the properties for the common dialog box components. They enable the developer to set the initial state of the dialog box, such as the caption and initial control settings, then read back any needed information after the dialog box closes. There is no direct interaction with the individual controls in the dialog box, just with the properties in the wrapper component.

The interface must therefore contain enough information that the dialog box form can appear in the way the developer specifies and return any information the application needs. You can think of the properties in the wrapper component as being persistent data for a transient dialog box.

In the case of the About box, you do not need to return any information, so the wrapper’s properties only have to contain the information needed to display the About box properly. Because there are four separate fields in the About box that the application might affect, you will provide four string-type properties to provide for them.


Creating and registering the component

Topic groups

Creation of every component begins the same way: derive a component class, save the component’s .CPP and .H files, derive a component class, register it, compile it, and install it on the Component palette. This process is outlined in Creating a new component.

For this example, follow the general procedure for creating a component, with these specifics:

Derive a new component type called TAboutBoxDlg, descended from TComponent.

Call the component’s unit files ABOUTDLG.H and ABOUTDLG.CPP.

Register TAboutBoxDlg on the Samples page of the Component palette.

The resulting .HPP file should look like this:

#ifndef AboutDlgH

#define AboutDlgH

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

#include <vcl\sysutils.hpp>

#include <vcl\controls.hpp>

#include <vcl\classes.hpp>

#include <vcl\forms.hpp>

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

class PACKAGE TAboutBoxDlg : public TComponent

{

private:

protected:

public:

__published:

};

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

#endif

The .CPP file of the unit should look like this:

#include <vcl\vcl.h>

#pragma hdrstop

#include "AboutDlg.h"

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

#pragma package(smart_init);

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

static inline TAboutBoxDlg *ValidCtrCheck()

{

  return new TAboutBoxDlg(NULL);

}

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

namespace Aboutdlg {

{

  void __fastcall PACKAGE Register()

  {

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

    RegisterComponents("Samples", classes, 0);

  }

}

Note:	If you used the Component wizard to begin this component, TAboutDlg will also have a constructor added.

The new component now has only the capabilities built into TComponent. It is the simplest nonvisual component. In the next section, you will create the interface between the component and the dialog box.


Creating the component interface

Topic groups

These are the steps to create the component interface:

1	Including the form unit files

2	Adding interface properties

3	Adding the Execute method


Including the form unit

Topic groups

For your wrapper component to initialize and display the wrapped dialog box, you must add the form’s files to the project.

Include ABOUT.HPP and link in ABOUT.OBJ in the component’s header file:

#include "About.h"

#pragma link "About.obj"

The form header file always declares an instance of the form class. In the case of the About box, the form class is TAboutBox, and the ABOUT.H file includes the following:

extern TAboutBox *AboutBox;


Adding interface properties

Topic groups	Example

Before proceeding, decide on the properties your wrapper needs to enable developers to use your dialog box as a component in their applications. Then, you can add declarations for those properties to the component’s class declaration.

Properties in wrapper components are somewhat simpler than the properties you would create if you were writing a regular component. Remember that in this case, you are just creating some persistent data that the wrapper can pass back and forth to the dialog box. By putting that data in the form of properties, you enable developers to set data at design time so that the wrapper can pass it to the dialog box at runtime.

Declaring an interface property requires two additions to the component’s class declaration:

A private data member, which is a variable the wrapper uses to store the value of the property

The published property declaration itself, which specifies the name of the property and tells it which data member to use for storage

Interface properties of this sort do not need access methods. They use direct access to their stored data. By convention, the data member that stores the property’s value has the same name as the property, but with the letter F in front. The data member and the property must be of the same type.


Example: Adding interface properties

For example, to declare an integer-type interface property called Year, you would declare it as follows:

class PACKAGE TWrapper : public TComponent

{

private:

    int FYear;                               // data member to hold the Year-property data

protected:

public:

__published:

    __property int Year = {read=FYear, write=FYear};        // property matched with storage

};

For this About box, you need four string-type properties—one each for the product name, the version information, the copyright information, and any comments. This is how the ABOUTDLG.H file should look:

class PACKAGE TAboutBoxDlg : public TComponent

{

private:

    int FYear;

    String FProductName, FVersion, FCopyright, FComments;

protected:

public:

__published:

    __property int Year = {read=FYear, write=FYear};

    __property String ProductName = {read=FProductName, write=FProductName};

    __property String Version = {read=FVersion, write=FVersion};

    __property String Copyright = {read=FCopyright, write=FCopyright};

    __property String Comments = {read=FComments, write=FComments};

};

When you install the component onto the Component palette and place the component on a form, you will be able to set the properties, and those values will automatically stay with the form. The wrapper can then use those values when executing the wrapped dialog box.


Adding the Execute method

Topic groups	Example

The final part of the component interface is a way to open the dialog box and return a result when it closes. As with the common-dialog-box components, you will use a boolean function called Execute that returns true if the user clicks OK, or false if the user cancels the dialog box.

The declaration for the Execute method always looks like this:

class PACKAGE TMyWrapper : public TComponent

{

  ...

public:

    bool __fastcall Execute();

  ...

};

The minimum implementation for Execute needs to construct the dialog box form, show it as a modal dialog box, and return either true or false, depending on the return value from ShowModal.


Example: Adding an Execute method

Here is the minimal Execute method for a dialog-box form of type TMyDialogBox:

bool __fastcall TMyWrapper::Execute()

{

  DialogBox = new TMyDialogBox(Application);    // construct the form

    bool Result;

  try

  {

    Result = (DialogBox->ShowModal()  IDOK);  // execute; set result based on how closed

  }

  catch(...)

  {

    Result = false;                             // if it fails, set Result to false

  }

  DialogBox->Free();                            // dispose of form

}

In practice, there will be more code inside the exception handler. Specifically, before calling ShowModal, the wrapper will set some of the dialog box’s properties based on the wrapper component’s interface properties. After ShowModal returns, the wrapper will probably set some of its interface properties based on the outcome of the dialog box execution.

In the case of the About box, you need to use the wrapper component’s four interface properties to set the contents of the labels in the About box form. Because the About box does not return any information to the application, there is no need to do anything after calling ShowModal. Write the About-box wrapper’s Execute method so that it looks like this in the ABOUTDLG.CPP file:

bool __fastcall TAboutBoxDlg::Execute()

{

  AboutBox = new TAboutBox(Application);                // construct the About box

  bool Result;

  try

  {

    if (ProductName == ““)                              // if product name’s left blank ...

      ProductName = Application->Title;                 // ... use application title instead

    AboutBox->ProductName->Caption = ProductName;       // copy product name

    AboutBox->Version->Caption = Version;               // copy version information

    AboutBox->Copyright->Caption = Copyright;           // copy copyright information

    AboutBox->Comments->Caption = Comments;             // copy comments

    AboutBox->Caption = “About “ + ProductName;         // set About-box caption

     Result = (AboutBox->ShowModal() == IDOK);           // execute and set result

  }

  catch(...)

  {

    Result = false;                       // if it fails, set Result to false

    ...

  }

  AboutBox->Free();                       // dispose of About box

  return Result == IDOK;                  // compare Result to IDOK and return Boolean value

}

To the ABOUTDLG.H header, add the declaration for the Execute method to the public part of the TAboutDlg class:

class PACKAGE TAboutDlg : public TComponent

{

public:

     virtual bool __fastcall Execute();

};


Testing the component

Topic groups

Once you have installed the dialog-box component, you can use it as you would any of the common dialog boxes, by placing one on a form and executing it. A quick way to test the About box is to add a command button to a form and execute the dialog box when the user clicks the button.

For example, if you created an About dialog box, made it a component, and added it to the Component palette, you can test it with the following steps:

1	Create a new project.

2	Place an About-box component on the main form.

3	Place a command button on the form.

4	Double-click the command button to create an empty click-event handler.

5	In the click-event handler, type the following line of code:

    AboutBoxDlg1->Execute();

6	Run the application.

When the main form appears, click the command button. The About box appears with the default project icon and the name Project1. Choose OK to close the dialog box.

You can further test the component by setting the various properties of the About-box component and again running the application.


Related topic groups

Component Writer's Guide

•	Overview of component creation

•	Object-oriented programming for component writers

•	Creating properties

•	Creating events

•	Creating methods

•	Using graphics in components

•	Handling messages

•	Making components available at design time

•	Modifying an existing component

•	Creating a graphic component

•	Customizing a grid

•	Making a control data aware

•	Making a dialog box a component


Overview of component creation

Related topic groups

•	Overview of component creation

•	The Visual Component Library

•	Components and classes

•	How do you create components?

•	Modifying existing controls

•	Creating original controls

•	Creating graphic controls

•	Subclassing Windows controls

•	Creating nonvisual components

•	What goes into a component?

•	Removing dependencies

•	Properties, methods, and events

•	Graphics encapsulation

•	Registration Overview

•	Creating a new component

•	Using the Component wizard

•	Creating a component manually

•	Creating a unit

•	Deriving the component

•	Declaring a new constructor

•	Registering the component

•	Testing uninstalled components

•	Installing a component on the Component palette

•	Component file locations

•	Adding the component


Object-oriented programming for component writers

Related topic groups

•	Object-oriented programming for component writers: Overview

•	Defining new classes

•	Deriving new classes

•	Changing class defaults to avoid repetition

•	Adding new capabilities to a class

•	Declaring a new component class

•	Ancestors and descendants

•	Controlling access

•	Hiding implementation details

•	Defining the developer's interface

•	Defining the runtime interface

•	Defining the design-time interface

•	Dispatching methods

•	Regular methods

•	Virtual methods

•	Overriding methods

•	Abstract class members

•	Classes and pointers


Creating properties

Related topic groups

•	Creating properties: Overview

•	Why create properties?

•	Types of properties

•	Publishing inherited properties

•	Defining component properties

•	The property declaration

•	Internal data storage (properties)

•	Direct access

•	Access methods (properties)

•	The read method

•	The write method

•	Default property values

•	Specifying no default value

•	Creating array properties

•	Storing and loading properties

•	Using the store-and-load mechanism

•	Specifying default values

•	Determining what to store

•	Initializing after loading


Creating events

Related topic groups

•	Creating events: Overview

•	What are events?

•	Events are closures

•	Calling the click-event handler

•	Events are properties

•	Event types are closure types

•	Event handlers have a return type of void

•	Event handlers are optional

•	Implementing the standard events

•	Identifying standard events

•	Making events visible

•	Changing the standard event handling

•	Defining your own events

•	Triggering the event

•	Two kinds of events

•	Defining the handler type

•	Declaring the event

•	Calling the event

•	Empty handlers must be valid

•	Users can override default handling


Creating methods

Related topic groups

•	Creating methods: Overview

•	Avoiding interdependencies

•	Naming methods

•	Protecting methods

•	Methods that should be public

•	Methods that should be protected

•	Making methods virtual

•	Declaring methods


Using graphics in components

Related topic groups

•	Using graphics in components: Overview

•	Overview of graphics

•	Using the canvas

•	Working with pictures

•	Using a picture, graphic, or canvas

•	Loading and storing graphics

•	Handling palettes

•	Specifying a palette for a control

•	Responding to palette changes

•	Offscreen bitmaps

•	Creating and managing off-screen bitmaps

•	Copying bitmapped images

•	Responding to changes


Handling messages

Related topic groups

•	Handling messages: Overview

•	Understanding the message-handling system

•	What's in a Windows message?

•	Dispatching messages

•	Changing message handling

•	Overriding the handler method

•	Using message parameters

•	Trapping messages

•	The WndProc method

•	Creating new message handlers

•	Declaring a message identifier

•	Declaring a message-structure type

•	Declaring a new message-handling method


Making components available at design time

Related topic groups

•	Making components available at design time: Overview

•	Registering components

•	Declaring the register function

•	Writing the Register function

•	Specifying the components

•	Specifying the palette page

•	Using the RegisterComponents function

•	Adding palette bitmaps

•	Providing Help for your component

•	Creating the help file

•	Creating the entries

•	Making component help context-sensitive

•	Adding component help files

•	Adding property editors

•	Deriving a property-editor class

•	Setting the property value

•	Editing the property as a whole

•	Specifying editor attributes

•	Registering the property editor

•	Adding component editors

•	Adding items to the context menu

•	Specifying menu items

•	Implementing commands

•	Changing the double-click behavior

•	Adding clipboard formats

•	Registering the component editor

•	Compiling components into packages

•	Troubleshooting custom components


Modifying an existing component

Related topic groups

•	Modifying an existing component: Overview

•	Creating and registering the component

•	Modifying the component object

•	Overriding the constructor

•	Specifying the new default property value


Creating a graphic component

Related topic groups

•	Creating a graphic component

•	Creating and registering the component

•	Publishing inherited properties

•	Adding graphic capabilities

•	Determining what to draw

•	Declaring the property type

•	Declaring the property

•	Writing the implementation method

•	Overriding the constructor and destructor

•	Publishing the pen and brush

•	Declaring the data members

•	Declaring the access properties

•	Initializing owned classes

•	Setting owned classes' properties

•	Drawing the component image

•	Refining the shape drawing


Customizing a grid

Related topic groups

•	Customizing a grid: Overview

•	Creating and registering the component

•	Publishing inherited properties

•	Changing initial values

•	Resizing the cells

•	Filling in the cells

•	Tracking the date

•	Storing the internal date

•	Accessing the day, month, and year

•	Generating the day numbers

•	Selecting the current day

•	Navigating months and years

•	Navigating days

•	Moving the selection

•	Providing an OnChange event

•	Excluding blank cells


Making a control data aware

Related topic groups

•	Making a control data aware

•	Creating a data-browsing control

•	Creating and registering the component

•	Making the control read-only

•	Adding the read-only property

•	Allowing needed updates

•	Adding the data link

•	Declaring the datamember

•	Declaring the access properties for a data-aware control

•	Initializing the data link

•	Responding to data changes

•	Creating a data-editing control

•	Changing the default value of FReadOnly

•	Handling mouse-down and key-down messages

•	Responding to mouse-down messages

•	Responding to key-down messages

•	Updating the field data-link object

•	Modifying the Change method

•	Updating the dataset


Making a dialog box a component

Related topic groups

•	Making a dialog box a component: Overview

•	Defining the component interface

•	Creating and registering the component

•	Creating the component interface

•	Including the form unit

•	Adding interface properties

•	Adding the Execute method

•	Testing the component