Writing Delphi Components

I strongly suggest using Teach Yourself Borland Delphi 4 in 21 Days, Chapter 20 - Creating Components. It is a very good reference. I am not going to repeat it here. (Actually, the Delphi help Contents / Creating Custom Components is also very good.) However, there are a few extra hints that I would like to add

Basic Functionality | Sub-Directories | Testing | Resources | Components Which Need a Windows Handle
Design Time Icons | Graphics | Freeing Components | Referencing Another Component
Hints References

I have given Writing Help Files its own page.


Basic Functionality


Sub-Directories

When writing a new component, you should assume from the start that there there will be many files - some you will want to distribute, and those you want to keep private. For instance, you probably don't want to distribute all the testcases that you use to validate your work. At any rate, it makes sense to try and keep the different types of files in separate directories. One suggestion is to create the following sub-directories.


Testing

When testing a new component, before you place it in the Component palette, be sure to define it under the form's private section, not the section where the other components are defined. The first section of the form class is reserved for only registered components that you've selected from the palette ... you will get an error if you place your code there.

After you have registered the component, you can place it on your form by selecting it from the Component Palette. At this point, the published properties and procedures (events) should appear in the property editor (Object Inspector).

You can still edit the component's code and place breakpoints. However, the design time attributes won't change until you explicitly save the component source and re-compile the package.

In order to re-compile a package, first "Open" it - from the menu, select

  File / Open... / [the appropriate package - *.dpk]

or

  File / Reopen... / 
Then click the Compile button.
(Remember to save the code first.)


Resources

Delphi components (and applications) should store Cursors, Icons, Bitmaps, and Strings as resources (*.res and *.dcr files). Basically, resources are linked into *.exe and *.dll files. This not only allows you to distribute fewer files, but it allows you to customize strings (text) for other languages by simply linking in another resource (ie, you don't have to re-compile).

See Resources.html for a discussion of various resource viewers and editors.

In general, you should plan for your components to contain one *.res file (for run time mouse cursors and the like) and one *.dcr file (for the design time toolbar bitmaps).

For *.res files, Be sure to place the following in the interface section of the unit (*.pas file) that defines the control - I place mine just before the implementation section. (It should also work in the implementation section.)

If used, be sure to place the *.dcr file in the same directory as the *.pas file with the same base name. When properly installed, the *.dcr file will have its own line in the package dialog box. If it is missing (normally because it did not exist when the unit was originally added), simply add the associated *.pas file again (which automatically includes the *.dcr file) and press compile.

Be careful, the term "icon" has 2 completely different meanings

With Delphi components, the toolbar icons are stored as bitmap resources, the icons that represent programs are stored as icon resources. In general, components should not contain any icon resources.


Components Which Need a Windows Handle

Normally, components which are not displayed at run time are descended from TComponent. However, if a component processes Windows Messages and/or needs a Windows Handle, then it should descend from TCustomControl.

As discussed below, this causes a number of problems. Basically,


Design Time Icons (Toolbar Buttons)

All projects have a default resource file - ProjectName.res - which is included via the *.dpr (Delphi project) file via The project's icon (the icon shown in Windows Explorer and in the project's title bar) is saved as MAINICON, a 32x32 16-color icon.

Component icons (displayed in the tool palette) are 25x25 bitmaps with the same name as the class. These MUST NOT be placed in the same file as the project's icon. One convention is to use the same name as the unit that defines the component. If you use an *.res (resource) file, then the bitmaps will be compiled into your *.exe files. However, if you use an *.dcr (dynamic component resource) file, then the bitmaps will be used for the palette but not compiled into *.exe files.

To make this work, place the *.dcr file in the same directory as the *.pas file with the same base name. The *.pas file must not use a Resource statement to include the *.dcr file

Instead, when the *.pas file is added to the package (*.dpk file), the *.dcr file will be automatically included and will have its own line in the package dialog box. If it is missing (normally because it did not exist when the unit was originally added), simply add the associated *.pas file again and press compile.

In general, you should plan for your components to contain one *.res file (for run time mouse cursors and the like) and one *.dcr file (for the design time toolbar bitmaps). Resource files can be created and edited via

(The Delphi help says that the bitmaps should be 24x24, "Delphi 4 in 21 Days" says 25x25. Larger and smaller sizes seem to also work. Use your judgement. For more information, search the Delphi 5 help for palette bitmaps.)

Delphi allows a form fragment (called a frame) to be defined so that a common component configuration and code is easier to reuse. These are added to the toolbar by displaying the frame (select File Open...), right clicking it, and selecting Add To Palette. The Palette Icon can be set to any 24x24 bitmap (*.bmp file). It will not allow a 25x25 bitmap to be added.

The lower-left pixel of the bitmap defines the transparent color - every occurrence of that color is automatically replaced with the color of the toolbar background.

See this for additional information on Resource files.

Warning: You must NOT, for any reason, delete any icons in your component's *.res files. If you do, there is no way to control which icon is associated with ANY application that uses your component. If you accidentally add an icon to the *.res file and then "delete" it, the Image Editor actually only hides it (it deletes the pointer but leaves the icon) and you're screwed. To fix this, use the Resource Hacker (or a similar tool) to actually delete the icon.

I suspect that you will have the same problem using cursors since the implementation is similar to icons. Bitmaps are handled differently and should not cause a problem.


Components Based on TComponent

If a component is created from TComponent, then the icon displayed in the palette is what is displayed on the form.


Components Based on TCustomControl

If a component is created from TCustomControl, then the Paint method is called at both design time and at run time, and you must draw the control yourself.

In addition, when you add the control to the form, the Height and Width are set to zero. You need to set these to non-zero values in the constructor. If these are not changed in the constructor, then the Paint method will not be called. You will also not be able to use the mouse to select the control on the form. Even blocking an area won't get it. You will have to use the Object Inspector to select your invisible component.

In order to display the control only at design time, simply set the Visible (public, not published) property to false. This property is ignored at design time ... and the Paint method won't be called at run time.


Code Examples


More

Well, this stuff is simple enough, except that this took me 2 days to figure it out. I originally started out with a TCustomControl component (joysticks can produce Windows messages, and those require a Windows handle, and TCustomControl provides that). I repeatedly changed between TCustomControl and TComponent, but I couldn't get an icon to display at design time.

The changes took effect immediately for run time, but had no effect at design time. Yes, I had Package / Options / Rebuild as Needed selected. So I spun my wheels for 2 days until I discovered that an explicit package re-build was required to change the design time functions.

Talk about no documentation.

Another feature is

I was lucky to stumble onto that one.


Graphics

Classes derived from TGraphicControl


Freeing Components

Sometimes, one component will contain another. According to the help files, the rule is that if you create it, then you must free it. However, the correct rule is "if you OWN it, then you must free it". Notice in the code example below This is based on experience - I was not able to find any documentation to support this behavior.


Referencing Another Component

It is possible for one control to store a pointer (reference) to another control. However, when the referenced component is deleted, it is very important that the reference is set to nil. (Otherwise, in design mode, this can cause a pretty severe crash.)

For code details, see Actions that Reference Components.


Hints


Windowed controls (based on TWinControl) can contain other windowed controls. To display a child control, simply set


The standard sequence for a component placed on a form is


TWinControl.SetBounds

At design time, when you manually re-size a control, SetBounds is called

At run-time, SetBounds is NOT called


LockWindowUpdate

When writing lots of data to a TWinControl component, sometimes the display will flicker. Use LockWindowUpdate to stop the flickering and to speed up the program.


References


Author: Robert Clemenzi - clemenzi@cpcug.org
URL: http:// cpcug.org / user / clemenzi / technical / Languages / Delphi / Components.htm