Anda di halaman 1dari 16

Custom Component Development in Delphi

Everything about creating custom components in Delphi. The ultimate source.

That's why you love Delphi!


I think we can all agree: components are essential elements of the Delphi environment. One
of the most important features of Delphi is that we can use Delphi to create our own
components.
We 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 and creating nonvisual
components. Visual or not, with or without property editor, from scratch...you name it.
Developing Delphi components isn't a simple task, it involves quite a bit of knowledge of the
VCL. However, developing custom components is not an impossible task; writing
components is just pure programming.
Articles, papers, tutorials
What follows is a list of articles that deal with custom component development in Delphi.

Fullscreen
Accessing protected members of a component
Many Delphi components have useful properties and methods that are marked invisible
("protected") to a Delphi developer. In this article you will find the workaround to this
problem - thus enabling you to access a DBGrid's RowHeights property, for example.
Creating Custom Delphi Components - Inside and Out
This tutorial will explain component writing to you, which should result in more code reuse.
It will go over properties, events and methods, and will also explain how to install
components. The final part of this tutorial is about Object-Oriented design.
Creating Custom Delphi Components, Part I
This first part demonstrates some of the best approaches to building components, and at the

same time provides tips on deciding on the best base class to inherit from, using virtual
declarations, the complexities of overriding, and so on.
Creating Custom Delphi Components, Part II
Quite often it is necessary to write components that perform more advanced functions.
These components often need to either reference other components, have custom property
data formats, or have a property that owns a list of values rather than a single value. We will
explore various examples covering these very subjects, starting with the most simple.
Ads

Custom Web Development


Delphi Controls
Delphi Developer
Website Development Class
Delphi Component

Creating Custom Delphi Components, Part III


This article is the final part of a three part article on components. Part one covered the basic
creating of components, part two covered how to write advanced properties, how to write
custom streaming for those properties, and sub-properties. This final part will cover property
/ component editors, how to write dedicated editors for your component / property, and
how to write "hidden" components.

More resources
First, if you want more, consider buying a book on Developing custom components.
Second, why not try locating an existing (with source perhaps) component you are looking
for.
Third, when you are 100% sure there is no such question on custom component
development you can't answer...there will be something that you don't know. Everything
you have to do is to ask a question on the Delphi Programming Forum and wait for answers.
If you still need more samples .. try the second page ...
Articles, papers, tutorials
Here is a list of articles that deal with custom component development in Delphi.

VCL Component Messages [RTF]


Component Messages (CM_) are generated only by the VCL and are not reflected
Windows Messages (WM_), as one may assume. In spite of that Component
Notifications (CN_) are reflected Windows Messages. The idea behind it is, that
Windows often sends messages to a parent window of a control instead of the
control itself. The VCL simply converts (reflects) these messages to Component
Notifications and then sends it to the control, for which the message originally was
meant.

Delphi Component Building.


In this article, read about every aspect of Delphi Component building. Design a
TTicTacToe component and learn about: how to build our own components for

Delphi, how to add properties, methods and custom events to them, how to wrap
them around DLLs, how to install them, how to design a palette bitmap and write online help to support the component user.
continue reading below our video

Building SuperComponents in Delphi [download]


SuperComponents, also known as aggregate or compound components, are
collections of existing sub-components and their relationships combined into a single
component. The collections are typically arranged inside a container parent
component that manages the visual layout of the sub-components.

Accessing protected members of a component


Many Delphi components have useful properties and methods that are marked invisible
("protected") to a Delphi developer. In this article you will find the workaround to this
problem - thus enabling you to access a DBGrid's RowHeights property, for example.
Have you ever caught yourself in talking to yourself? A discussion like the one
below perhaps?
"Hmm, that DBNavigator is really cool, but if I could only lay my hands on the buttons and those pictures it
displays?", or "I could really use the OnClick event for the DBGrid, but why is it not accessible in the Object
Inspector?!", or "If I could just change the height of that title row in the DBGrid".
A little OOP story...
As you probably know (and those who do not will learn now), in Delphi OOP the protected data of a class is
accessible to any method that appears in the same unit as the class.
In Delphi class declaration, every member of a class has an attribute called visibility, which is indicated by one of
the reserved words private, protected, public, published, or automated. If you are not familiar with the meaning of
those keywords, please read at least some of the pages of the "Creating Custom Delphi Components - Inside and
Out" article.
Recall the self-dialog above, what if you really need to get your hands on the Height property of the first row
(where titles are displayed) of a DBGrid component.
In this article, you'll find out about a technique sometimes called the "Protected Hack" - deriving (subclassing) a
component from an existing Delphi component with the only purpose or exposing the protected properties and
methods.
Note: Another workaround to this "problem" is to create a new component derived from the component you want
to hack and make the properties either public or published. This would end up in installing the new component on
the Palette so you can access it from everywhere. However, many times you'll only need to expose only one
property or method (presumably limited to a single unit in your code) - why bother with creating a whole new
component?
"Protected Hack"
The general technique explained here can be used to surface hidden (that is, "protected" in Delphi OOP jargon)
properties and methods for Delphi classes (components). If you know that an ancestor could do something (handle
an event, access a property, execute a method) this is how to make the descendant do it:

Let's say we need to hack a DBGrid. What we want to access is the RowHeights property.
Make sure you are editing the unit where the DBGrid is declared (for example: DBGrid1 dropped on
Form1, so we are editing the Unit1.pas)
Create a noticeably worthless derived class:

THackDBGrid = class(TDBGrid);
Apparently, the class THackDBGrid derived like this does not bring any new functionality to DBGrid.
Though, it does provide the *free* access to all the protected members of the DBGrid component.
To use once protected members, you must typecast the component you refer too to the new (descendant)
class.

Now, let's see some Delphi code. Assume you have a Delphi form (Form1) with a DBGrid (DBGrid1) displaying some
data. To access the protected RowHeights property of that DBGrid you first hact the DBGrid comonent. Here's the
portion of the Unit1.pas unit - where Form1 is declared:
uses
Windows, Messages, ..., Grids, DBGrids, ... ;
type THackDBGrid = class(TDBGrid);
type
TForm1 = class(TForm)
DBGrid1: TDBGrid;
....
Note the THackDBGrid declaration! Now, to change the height of the title row of the DBGrid1 you simply typecast
DBGrid1 to THackDBGrid like:
//makes the Title's row 20 px in height (if displayed)
THackDBGrid(DBGrid1).RowHeights[0] := 20;
Ok, that's it. RowHeights?! Yes, the RowHeights property of a TCustomGrid (the TDBGrid derives from) gives the
height (in pixels) of all rows in the grid. In TDBGrid this property is protected - in THackDBGrid it is editable!
Note that the next assignemet gives the compile time error: "Undeclared identifier: RowHeights".
DBGrid1.RowHeights[0]:=20;
It's working! ... Howcome?
Yep, it is. This is why: the THackDBGrid automatically inherits all the protected members of the TDBGrid (and all
public, private, etc. of course), due to the fact that THackDBGrid is in the same unit as the code that accesses the
protected property, the protected property is available.
Note: do not think that you can define the THackDBGrid in another unit - the code will no longer compile.
Another approach - no type-casting.
If you need to access the protected members of a component but do not want to use type casting, you can declare
the new (hacked) class with the same name as the old class:
type TDBGrid = class(DBGrids.TDBGrid);
Now, when accessing any protected member, you do not need to use the THackDBGrid(DBGrid1).Something
syntax; Delphi will use the modified component as if it were the old one. Therefore a line like
DBGrid1.RowHeights[0]:=20; will compile.
Warning!
This is a hack - a very bad OOP technique. There are reasons that components do not expose certain members of
their class declaration. Use the technique explained in this article at your own risk.

Note: "a hack" - means that it breaks standard OOP practices. This "hack" works (and will work in future Delphi
version) because it relies on a specific Delphi Pascal OOP feature.

Creating Custom Delphi Components - Inside and Out


Page 1: About components, properties, methods and events
Article submitted by: Alistair Keys
What's This Tutorial About?
This tutorial will explain component writing to you, which should result in more code reuse. It will go over
properties, events and methods, and will also explain how to install components. The final part of this tutorial is
about Object-Oriented design.
What Are Components?
Components are simply little bits of an overall system. If you have had any experience with Object-Oriented
design, please don't shoot me for such a simplistic explanation - I'll do better, honest ;-). Components can help
code reuse - if you find yourself repeating the same code, you can write a component for your task and place it on
future projects with two mouse clicks.
Have a look at the Delphi component palette. You will see all sorts of objects - buttons, checkboxes, shapes and so
on. If you find you have a recurring task that is getting pretty monotonous, you can create a component to do the
job for you. Once you've finished it, you can install it and use it like any other.
Properties
Properties are data held by the component. An example here would be a "car" component, which would
properties for "PaintColour", "Manufacturer", and so on. You can consider properties to be just variables, although
they are more complex than that.
When you are defining properties, you usually specify three things: its data type, how to read the property and how
to write the property (if applicable). The first is obvious; if you want the property, you need to know what it is
actually representing! The next two aren't quite as obvious.
Accessing properties seems straightforward... we've all done it (e.g. "Button1.Caption := 'xyz';", where we're
accessing Button1's Caption property). This just seems like a simple variable assignment, and that's probably what
you assumed it was. Well, it's not! Delphi performs some trickery and actually does some work behind the
scenes. So:
When accessing properties, you could either be using variables directly or calling functions behind the scenes!
The standard property declaration looks like this:
property SomeProperty: Integer read FMyInteger write SetMyInteger;
This defines a property "SomeProperty", of type Integer. Note that properties are not actually variables in
themselves, just ways of accessing other variables! Thus, the variable actually accessed with
"SomeObject.SomeProperty" will be FMyInteger. Now for the interesting bit: the read and write parts say how to
access the data. In this case, when the property is read (e.g. "if SomeObject.SomeProperty = 1") the value is taken
directly from the variable, as if the code had said "SomeObject.FMyInteger". However, when the property is set, it
is not done directly! The code actually calls a SetMyInteger procedure, which would be of the form:
procedure SetMyInteger(NewValue: Integer);
This gets called instead, as if the code had said "SomeObject.SetMyInteger(23)" instead of
"SomeObject.SomeProperty := 23".

Now, the read and write parts can specify direct data access (by putting the variable name) or via
procedures/functions (by putting the procedure/function name). To give another example, here's a read-only
property that gets its data from a function:
property SomeReadOnlyProperty: Boolean read GetMyValue;
This property cannot be written to, which safeguards the data nicely :-). The GetMyValue function would be of the
form: "function GetMyValue: Boolean;" Whenever the property is accessed, it would call this function to retrieve
the value... so the code "if SomeObject.SomeReadOnlyProperty = True then" would get translated into "if
SomeObject.GetMyValue = True then" behind the scenes.
Important note!
The variables (e.g. FMyInteger) and procedures/functions (e.g. GetMyValue, SetMyInteger) are not available to the
outside world! They are hidden away using "private" or "protected", so any direct calls would result in errors. This
is good, as otherwise the entire purpose of read/write specifiers would be defeated. Private, protected and other
such keywords will be explained (much) later.
Advantages of Properties
You should have noticed why properties are pretty cool, but here's a summary:

They allow you to say how data gets accessed, protecting it


They save you from 50,000 "get/set" lines of code, helping readability
They allow you to check data integrity before changing the variables

The first one is pretty obvious, really. If you declared your variables publicly they could be changed in any old
way. However, you can still allow direct access to your variables if you so desire. Properties allow you to change
the behind-the-scenes way of getting or setting data without having to rewrite any code that uses the component!
The second point is something that makes you really appreciate Delphi. In other languages (for example C++) you
do need to say "someObject.setThisValue(23)" everywhere. This makes your code less readable and just plain
sucks! It, in my view, reduces the readability as it detracts from your main intent: you want to change the value,
not call the function! Delphi has provided an elegant mechanism to avoid this kind of unnecessary code.
The last point is good for efficiency - if you have a graphical component, for example, you want to reduce the
drawing as much as possible. This can be done by a set procedure that checks if the old value is different from the
new value. Here's an example:
procedure TSomeComponent.SetBackgroundColour(NewColour: TColor);
begin
if NewColour <> FBackgroundColour then
begin
FBackgroundColour := NewColour;
Invalidate; // redraws the component
end;
end;
This means that your component will only redraw itself if it gets a different property value. This technique can be
applied elsewhere, with error checks and other such data checking.
Methods
Methods are very important! These are procedures or functions, and define the behaviour of your objects. They
are defined in the usual way, with one-line prototypes in the component and definitions later on.
Events

Events get defined by you, and are called whenever you want to notify the programmer of something. You can
usually ignore these until you get more experienced with components. They boil down to pointers to class
procedures, which means slight complications. Events will be covered later, so don't worry too much about them.
Question, Suggestions...
If you have any questions or comments to this (huge) article, please post them on the Delphi Programming Forum.
Discuss!

Creating Custom Delphi Components - Inside and Out


Page 2: New...Component
Creating a Component
Get the source code here: Line.zip (1Kb)
We'll start off at the deep end. We're going to create a simple component, called TLine. It will be a graphical control
that will draw a line in one of four directions. It won't do anything fancy, but it will deal with properties, drawing
and other useful stuff.
How Do We Start?
Every component needs a base class. This class is simply the "Big Daddy" object for it, from which the component
takes all properties, events and methods. For example, if you were writing a new type of button you might make
the base class "TButton". Your new control would have all the TButton's properties (like caption, etc), all the
Button's base class's stuff, and so on.
There are lots of possible base classes. Here's a small selection:
Type of component

Recommended Base Class

Generic, non-visual component

TComponent

Lightweight visual component

TGraphicControl

Visual component that needs focus

TCustomControl

Form-type object

TCustomForm

This is, of course, a small snippet of the possible cases you might need. We're interested in a small visual control, so
TGraphicControl is the best base class for us. There's no need for TCustomControl because our component won't
need window focus.
We can get started now. Go to File->New and select component. You'll be presented with a dialogue box. Fill it out
like this:

The only two things you need to fill out are the "Ancestor type" and "Class name" boxes. The rest get filled out
automatically. Once you've done that, click the OK button and you get transported to the code editor with a
skeleton class written out.
First we need to let the compiler know what we'll be using. Each unit (a file, usually containing a class) has a "uses"
clause. This lists what other files will be needed to compile the unit. Without this the compiler would have a really
hard job so it's up to us to make sure it's correct.
Note:
The following section about adding Graphics to the uses clause is relevant for Delphi 6 (and possibly Delphi 5).
Delphi 4 adds Graphics automatically so if you start a new component and the uses clause has "graphics" in it then
you don't need to do the next part.
You usually don't notice the "uses" clause as it's filled out with a large number of units by default (such as classes,
sysutils, etc.). These units include most of the functionality you require. However, sometimes you have to add your
own units (which usually listed in the help files for the appropriate class/procedure/function/type). We'll be
needing the type TColor, which represents a colour. If you look this up in the help file you'll see it's within the
'Graphics' unit. Let's add that now before we start. Go to the very top of the file and find the uses part. It will look
like this:
// <-- name of file without .pas
unit Line;
interface
uses
Windows, Messages, SysUtils, Classes, Controls
You can see that each unit needed is simply separated by a comma. We'll add Graphics to the uses unit. Make this
change:
uses
Windows, Messages, SysUtils,
Classes, Controls, Graphics; //(note added unit)
Question, Suggestions...

If you have any questions or comments to this (huge) article, please post them on the Delphi Programming Forum.
Discuss!

Creating Custom Delphi Components - Inside and Out


Page 3: Adding variables to components
Adding Some Variables
We need to customise this skeleton code to suit our purposes so let's get busy. The first thing to do is to declare a
new type. Change the bit that says:
type
TLine = class(TGraphicControl)
to read:
type
TLineDirection = (drLeftRight,
drUpDown,
drTopLeftBottomRight,
drTopRightBottomLeft);
TLine = class(TGraphicControl)
That should be fairly self-explanatory. We declared a new type called TLineDirection, which we can use when
drawing to figure out where to draw the line from/to. Now we add in a few variables (called member variables)
into the class itself. We need to store:

Line width
Line colour
Direction

This is where the whole idea of customisation comes in. If you want to, you could add in any variables you think
are necessary... you could add in code to draw dotted lines, for example. However, we'll stick with the basics for
now.
One thing you should have spotted is that we don't actually need to store the line width and colour, as they are
properties of the canvas's pen. This is very handy. However, we will need to add in a variable for the direction, as
well as get and set functions. Change the private section of the component to look like this:
private
{ Private declarations }
FLineDir: TLineDirection;
function GetLineWidth: Integer;
function GetLineColour: TColor;
procedure SetLineWidth(const NewWidth: Integer);
procedure SetLineColour(const NewColour: TColor);
procedure SetLineDir(const NewDir: TLineDirection);
Even though we haven't got variables for the line width and colour, we still need functions to set and retrieve the
values from the component's canvas. This is because these three values will be properties of TLine, and properties
want you to say how they will read/write, remember? Note we don't need a get function for FLineDir because we
can just let the property read its value directly from the variable.

The "F" before LineDir is a bit of notation. It stands for "field", and just means the variable belongs to the class. This
makes it a bit easier when writing your component to avoid mixing up local variables with class members.
Right, we've declared the functions so I suppose we'd better implement them. Bung this code in after the
implementation section (look for the "implementation" keyword in your file and add this immediately after it):
function TLine.GetLineWidth: Integer;
begin
Result := Canvas.Pen.Width;
end;
function TLine.GetLineColour: TColor;
begin
Result := Canvas.Pen.Color;
end;
procedure TLine.SetLineWidth(const NewWidth: Integer);
begin
if NewWidth <> Canvas.Pen.Width then
begin
Canvas.Pen.Width := NewWidth;
Invalidate; // redraws the component
end;
end;
procedure TLine.SetLineColour(const NewColour: TColor);
begin
if NewColour <> Canvas.Pen.Color then
begin
Canvas.Pen.Color := NewColour;
Invalidate;
end;
end;
procedure TLine.SetLineDir(const NewDir: TLineDirection);
begin
if NewDir <> FLineDir then
begin
FLineDir := NewDir;
Invalidate;
end;
end;
The get functions are very self-explanatory. They just nab the values from the Canvas and pass them along.
However, the set functions are slightly more complex than just setting the variables.
When the set function gets called, we only want to change the appropriate value if necessary. After all, since it's a
visual control any unnecessary changes will be apparent to the user (e.g. setting the line direction to the same
value again shouldn't cause a redraw). Each of the set functions checks if the new value is different to the current
one. If, and only if, it is then we need to change the value and cause a redraw.

Creating Custom Delphi Components - Inside and Out


Page 4: Adding propeties to components

Adding Properties
The next step is to add properties to the component. We've laid the groundwork so it's simply a matter of bunging
them in, like so:
published
{ Published Declarations }
property Direction: TLineDirection read FLineDir write SetLineDir;
property LineColour: TColor read GetLineColour write SetLineColour;
property LineWidth: Integer read GetLineWidth write SetLineWidth;
There should be no great surprises in there as you've seen similar property declarations in the previous tutorial
part. Now, believe it or not, we've very nearly finished the component!
A quick note about "published": any properties in this section will be available in the Object Inspector at designtime. This is, of course, what we want. If you don't want the properties published (if, for example, you only create
the component at run-time) then you can place properties in the "public" section. Properties there will be available
to outside code, just like published properties, but will not appear in the Object Inspector. If you see "run-time"
properties in the help files, it means they're public.
The next part is pretty fundamental to TLine - its Paint procedure. We need to draw the component, so it's time to
add in some code for that. Since our component has TGraphicControl as a base class it gains a Paint procedure. We
just need to modify this to draw our code and we're almost there. Add in this code to the component's "Protected"
section:
protected
{ Protected declarations }
procedure Paint; override;
The override part is a fancy bit, and means our procedure will get called instead of the base class's Paint procedure.
We can write the good stuff now, namely the drawing procedure. Add this code in after the implementation part:
procedure TLine.Paint;
var
start: Integer;
begin
inherited;
case FLineDir of
drLeftRight:
begin
start := (Height - Canvas.Pen.Width) div 2;
Canvas.MoveTo(0, start);
Canvas.LineTo(Width, Start);
end;
drUpDown:
begin
start := (Width - Canvas.Pen.Width) div 2;
Canvas.MoveTo(start, 0);
Canvas.LineTo(start, Height);
end;
drTopLeftBottomRight:
begin
Canvas.MoveTo(0, 0);
Canvas.LineTo(Width, Height);

end;
drTopRightBottomLeft:
begin
Canvas.MoveTo(Width, 0);
Canvas.LineTo(0, Height);
end;
end;
end;
The only thing you shouldn't understand is the "inherited" bit. Because the Paint procedure belongs to the base
class and we're overriding it, we don't want to miss out on important bits the base class does. The base class here
might need to do things like set up the canvas to draw on, get device contexts, allocate resources, or whatever. If
we don't let the base class do its thing by calling it via "inherited" we might just find our code blows up. Best not to
let that happen, eh?
And that, my friend, is enough for a working component. Save the file and go to the Component->Install
Component menu, select your file and say OK when it asks if you want to rebuild your library. If all goes well it
should tell you that TLine has been registered. You can find it on the Samples palette page and try messing around
with it.

Question, Suggestions...
If you have any questions or comments to this (huge) article, please post them on the Delphi
Programming Forum. Discuss!

Creating Custom Delphi Components - Inside and Out


Page 5: On Constructors
Constructors and Destructors
The TLine component you just created showed the basics of component writing. However, there are two major
issues to cover - constructors and destructors.
Suppose you use dynamic memory in your component. Without any constructors and destructors this would be
very error-prone. You'd end up with code that looked like this:
var
SomeObject: TSomething;
begin
SomeObject := TSomething.Create;
SomeObject.Init; // yuck!
//... use the object now
SomeObject.Squish; // yuck!
SomeObject.Free;
end;

You should be able spot why this would be bad - what if you forget to call Init or Squish? You would end up with an
exception or memory leak (bad news) and would look like a fool. Well, luckily, constructors and destructors can
help here. Oh, carefully forget to notice that I actually used constructors and destructors in the above example ;-).
It's kinda hard in Delphi to avoid them.
Constructors
Constructors get called whenever you create an object. Their job is to set up the internals of the object (variables,
pointers, dynamic memory, etc.) as appropriate. The constructor is used to ensure you have a valid object - it
should do no more or less than this. If you find after calling a constructor you still need to call some Init function
then you haven't written an appropriate constructor, and should change it.
All objects is Delphi have a standard constructor ("Create"), which they get from TObject. This can be used to create
the object, like so:
SomeObject := TThisClass.Create;
where TThisClass is SomeObject's type. You must do this with classes before using them or you will get an
exception. When you use the constructor the object gets space allocated for it and can be used. What if you want
to change this constructor? Well, you can do this quite easily.
Every constructor can get declared like a procedure, but with "constructor" replacing "procedure" in the
declaration. Since every object has a "create" constructor available, why not modify this to suit your purposes?
Here's an example:
type
TSomething = class(TObject)
public
{ Public declarations }
constructor Create;
// etc.
end;
implementation
// and the actual constructor definition
constructor TSomething.Create;
begin
inherited;
// do whatever you need (allocate memory, initialise variables, etc.)
end;
The above is the simplest constructor you might think about. There's only one thing that needs discussing "inherited".
Inheritance is a concept in object-oriented design where a class can be a base for other classes. An example might
be a "creature" class, which you could use as a base for "cat" and "dog" classes. The cat and dog would get data
and behaviour from the "creature" base class, such as moving, having legs, fur, and so on, and would change this to
suit their own needs (such as adding "woof" behaviour for dogs). The first class from which others derive is called
the "base class" (or "super-class") and the ones that inherit from it are called "derived classes" (or "sub-classes").
Since every class is a sub-class of TObject, they automatically get its properties, methods, events, etc. (a slight
exaggeration, as you'll see later). This includes the standard constructors and destructors ("Create" and "Destroy").
Since we have added our own code for the TSomething constructor the other code won't get executed - if you

create a TSomething then TSomething.Create will get called, not TObject.Create. This is bad, as the base class might
be doing clever/important things. Using the "inherited" keyword lets you call the base class's same method. This
works for any constructor/destructor/procedure/function ("method").
Note that constructors can have any name, and any parameters. You're not just limited to "Create" and "Destroy",
so another potential constructor might look like this:
type
TMyClass = class(TSomething)
public
constructor InitWithStuff(const x: String);
end;
implementation
constructor TMyClass.InitWithStuff(const x: String);
begin
inherited Create;
// do something with your parameter "x"
end;
I'll first of all point out the slight change to "inherited". By default, inherited wants to use a method in the base
class with the same name and parameters. If there isn't one, you can specify the method manually, as I've done
here. You can also pass in parameters - you could, for example, do:
inherited SomeProcedure(x,y,z);
Now then, the above code makes a sub-class of TSomething, called TMyClass. It now has another constructor called
"InitWithStuff". This is NOT available in TSomething, as it has only been introduced in this new class. Note now that
TMyClass has two constructors, not just one! You could create a TMyClass like this:
someObject := TMyClass.InitWithStuff('hello');
As you'd expect, this uses the constructor "InitWithStuff" we've just added to this new class. However, remember
that TMyClass still has the Create constructor. This constructor didn't magically disappear when we created the new
class - it's a method of TSomething (from TObject), which means that it's a method of this new derived TMyClass as
well. This means it's syntactically correct to create a TMyClass like this:
someObject := TMyClass.Create;
However, this isn't such a good idea. If an object gives you a constructor you should use it! Because we are
bypassing TMyClass's constructor (it really wants us to use InitWithStuff) and are instead using the base class's
version (TSomething.Create) we could be creating problems (excuse the bad pun :-p). The object we want might be
missing out on some vital work work it needs because we're not using the thing provided. This leads to an
important tip:
It's usually best to use the same name for constructors for every derived class
You can have two constructors (or more) in one class. This means one potential way to get around the problem
might be this:
type

TMyClass = class(TSomething)
public
constructor Create;
constructor InitWithStuff(const x: String);
end;
implementation
constructor TMyClass.Create;
begin
raise Exception.Create('You must use InitWithStuff to create a TMyClass');
end;
constructor TMyClass.InitWithStuff(const x: String);
begin
inherited Create;
// do whatever stuff you need now
end;
This solution works, but it's a little silly. It prevents anyone from using Create as a constructor for TMyClass (or any
classes that are derived from it and use "inherited") because they'll end up with an exception. However, it's easier
just to name your object constructors consistently from base to sub classes (usually you can just stick with "Create",
as it's a standard name).

Understanding, Using and Enhancing Delphi


VCL Components

Tutorials and articles on using Delphi Visual Component Library (VCL) controls and
components more efficiently at design and run time.

TColorButton - button with color properties


Full source code of the TColorButton Delphi component, an extension to the standard
TButton control, with font color, background color and mouse over color properties.
TButton.Color?
Have you ever asked yourself a question like: "How can I set the background color of a TButton?" Well, you
can't! What's more you cannot change the background color a TButton, TBitBtn and TSpeedButton.
The background color of a TButton is controlled by Windows, not by Delphi. TButton is a simple wrapper
around the standard Windows button and Windows does not allow it to be colored except by choosing the
colors in the Control Panel.
Note: to change the default button color system-wide, you need to open the Control Panel; in the Appearances
tab of the Display properties applet edit the color of the "3D Objects" item (badly, this will change the color of
some other Windows elements).
Since Windows insists on doing the background coloring with clBtnFace, the only way to change the
background color is to draw the button yourself. In other words, if you want a button where you decide about
the color, you will have to make an owner-drawn button component.

TColorButton
To overcome the lack of color-related properties of the standard TButton
control, I've created the TColorButton custom Delphi component. The
TColorButton adds three new properties to the standard TButton:

BackColor - specifies the background color of the button.


ForeColor - specifies the color of the button text. Note that this
"overrides" the Font.Color property.
HoverColor - specifies the color used to paint the button's
background when the mouse hovers over the button.

Here's how to set color-related properties of the TColorButton at run time:


ColorButton1.BackColor := clOlive; //background
ColorButton1.ForeColor := clYelow; //text
ColorButton1.HoverColor := clNavy; //mouse over

Installing into a Component palette


First, download the component. The TColorButton comes as a single unit file (.pas
extension). You'll need to add the component into an existing package. Here's "How to
Install Custom Component in Delphi (into Existing Package)"
Questions? Comments? Extensions? Exceptions?!
That's it. If you find this component practical and if you extend it by adding more properties,
please send your source and make it available to other developers.
As always if there are any questions or comments please post them on the Delphi
Programming Forum.

Anda mungkin juga menyukai