Edited By: John M. Miano miano@worldnet.att.net Version: 8 Last Updated: 06-Jun-1997
| 1.1. | What is the purpose of this document? |
Section 2 - IDE
Section 3 - Using other components within a component
Section 4 - Bound Controls
Section 5 - VCL
Section 6 - Other Sources of Information
Section 7 - Persistent Objects
Section 8 - Tools for Delphi
Section 9 - Basic Programming Techniques
Section 10 - Advanced Programming Techniques
Section 11 - Component Virtual Methods
Section 12 Windows API Functions
Section 13 - Control Borders
Section 14 - Control Styles
Section 15 - Windows Messages
The purpose of this document is to answer common or undocumented questions
related to writing Delphi components. After spending a ridiculous
amount of time trying to decipher the TDataLink component it
occured to me that there ought to be some way of recording and
sharing solutions to the problems encountered while writing
controls. All information is provided as is. There are no guarantee
as to its correctness.
If you have any questions you would like to be answered or have
any contributions you think would be suitable for inclusion please
send them to the editor. Reports of errors or omissions are also
welcome.
In addition to including more answers and questions I am trying to
expand in two more areas:
The only solution to locating problems I have found is to:
From Glen Boyd
Run the REGEDIT program and go to
"HKEY_CURRENT_USER\Software\Borland\Delphi\2.0\Debugging" and add a string value called EnableCPU and set its
string value to 1. This adds the CPU window to the view menu.
The CPU window is active at run time for stepping through and
stuff like that.
From Ray Lischner
If you don't want the user to be able to drop the component on a
form then use then RegisterNoIcon and RegisterClass procedures.
From Ray Konopka
When viewing the source for the VCL units, it's definitely a good
idea to become comfortable with bookmarks in the editor. That is,
Ctrl+Shift+N, when N is a number 0-9, to set a bookmark. Jump to
a bookmark using Ctrl+N.
You do this by creating a Component Editor. One would think that
a component editor would be something that is called from the
popup menu but it actually controls what appears in the menu or
rather the items that your component can add to the menu.
The steps to follow are:
You probably have WriteLn statements in your code.
I have found that when using a component editor that a
component's properties will not be saved. The property appears to
be correct in design mode but when you run or save the property is
set to a different value.
It appears that a property editor needs to call
You need to define your own scroll bar class that intercepts the
CM_DESIGNHITTEST message.
You need to set the page size for the scroll bar. The following
code sequence
illustrates this:
The C++Builder documentation contains a description of the
TDataLink class. It is not in any Delphi documentation so far. For
those of you who do not have C++Builder here is the description
that has been in this document for a long time:
Properties:
Return Value:
The mechanism for having the TDataLink object communicate with
a component is to override these procedures.
Use the ActiveRecord to determine which record within the buffer
window is the current one.
It is not possible to force a scroll of the buffer window.
At first glance the RecNo property for a data set appears to be what
you want but unfortunately this only works with dBase and Paradox
tables.
The way you can keep track of the current record number is to
create a class that derives from TDataLink. The main things you
need to do are:
Copy the VCL source modules you are interested in stepping
through to your project directory then rebuild the VCL library.
You will then be able to step through the VCL source modules.
From Max Nilson
A little documented part of TComponent and its decendants is the
the
Notification method. This method is primarily used to detect cases
where
components that you are referencing are being deleted. It does have
other
features but I have not yet thought of a reason to use them. For
Borlands
explanation of these things search for Notification and
FreeNotification in
the VCL help. Don't bother looking in the Component Writer's
Guide 8-)
When ever you reference another component from your component,
for example
including a TDataSource property in your component, you should
override the
Noficication method and respond to it by checking that the
component you are
referencing is not being deleted. You should also use the
FreeNotification
method to ensure that you are notified even it the TComponent you
are
referencing is in another module. By default you only recieve
notifications
of componets in the same module as your component has been
placed, and now
that Borland has provided data modules you are sure to run this
case more
often than you expect.
If you don't use the Nofification method you will find (as I did) that
deleting a component that you reference will place the Delphi IDE
into an
extremely unstable state. It doesn't quite crash, but its very hard to
do
anything afterwards.
Here is an example showing just the critical methods with a single
reference
to another component:
Component messages are used much like regular Windows
messages except that they are used for notification of events that are
only applicable to Delphi components. If you have a component
that publishes the Font property the component probably needs to
be repainted if any subproperties in the Font are changed. Changing
the Font property does not necessarily generate a Windows event
but the control still needs to know about the change. Component
messages serve this purpose.
It appears that virtual methods could have been used in place of
component messages. Presumably messages are used in order to
keep the size of the virtual dispatch table from getting out of hand.
The books "Secrets of Delphi 2.0" has descriptions for
the individual component messages.
This is a listing of some of the component messages andwhat they
do. The messages marked "Notification Only" do not
pass any useful information to the message handler and do not
expect the message handler to return a value.
The book that has become the standard for writing components is:
"Secrets of Delphi 2" by Ray Lischner, Waite Group
Press
Another book on writing components that has information not
found in Konopka's book is
The largest Delphi web site is "The Delphi Super Site"
at
I have found Component source code on the following sites as well:
I have tried all sorts of schemes using DefineProperties and
WriteComponents and they all failed to work. As far as I can tell the
only way to do this is to use Delphi's default mechanism to store
your child objects.
A sequence that does work for saving to a stream is:
csLoading is not set in ComponentState until immediately after the
component is created. However the component's owner will
already have this set so try
There are a couple of easy ways to view properties as they are
stored in the form file:
There is a YACC and LEX written by Albert Graef
(ag@muwiinfa.geschichte.uni-mainz.de) for Turbo Pascal that
works with Delphi. This can be found on many shareware sites
(search for Pascal and YACC).
One location is /msdos/turbopas/tply30a1.zip and
/msdos/turbopas/tply30a2.zip in the Simtel archives.
nomssi@physik.tu-chemnitz.de has a Pascal library based of the
Independent JPEG Group's free JPEG library. It is available from
the following locations:
The easiest way to create an array of objects is to use a TList
control. I often find it helpful to create specialized classes that
derive from TList. The following class shows how to create a list
for a specific type of object that has a little better error reporting
than the basic TList. For you own list you may need to implement
more methods.
There is no WinCrt unit in Delphi 2.0. It has been replaced by a
project options setting. On the linker page there is an option to
create a console application. Check this rather than use the WinCrt
unit.
If you are deriving from an existing component then the class of the
component you wish to derive from should be the base class.
Several of the registered VCL component classes are immediate
ancestors of corresponding custom classes (e.g. TMemo descends
from TCustomMemo). These custom classes implement most of
the functionality of the control but they do not publish many of the
properties that they define. In most cases you are probably better
off deriving your component from the custom class rather than the
well known registered class.
If you are creating a component from scratch then TCustomControl
is most likely your best choice. This is used for visual, windowed
controls.
Other less likely alternatives are:
TGraphicControl - For visual controls that have no associated
window. These cannot receive input focus.
TComponent - Non-visual component.
TWinControl - To create a control that derives from an existing
control that was not created specifically for Delphi.
Yes and no. Delphi allows you to create your own "Text-File
Device Driver" which allows you to use standard Delphi I/O
procedures for non-standard I/O streams such as Unix <LF>
files or network connections. These are not as powerful as C++ I/O
Streams but it is possible to get around their limitations.
The procedure for creating a Text-File Device Driver is reasonably
well documented in the "Object Pascal Langue Guide"
and there is one example in the VCL source in PRINTER.PAS.
Delphi also has stream classes for writing objects to a stream.
These are not as "general purpose" as C++'s I/O
streams.
Use the function GetEnumName located in the module TypInfo.
The book "Secrets of Delphi 2.0" has a lot of
information on how to us the TypInfo module.
A control's window handle is created by the CreateWnd method. If
you have processing that needs to be performed after the window
handle is created then you can override CreateWnd and do
The Loaded method is called for each component on a form after all
controls on the form have been loaded from the stream.
You could intercept the WM_PAINT message and set up your
control's canvas however will VCL do all this work for you if you
simply override the Paint method in your control.
The CreateParams method is used to set up the window style and all
the other arguments that are passed to CreateWindowEx to create
the control's window.To change the window style use something
like this which creates a window with or without a vertical scroll
bar.
The easiest way to scroll the elements of a control is to change all of
their coordinates then to force a repaint of the control.
Unfortunately this produces the flicker effect.
The best way to reduct this flickering is to use the ScrollWindow or
ScrollWindowEx Windows API function.
Another source of flickering can be from Windows using two
messages to paint: WM_PAINT and WM_ERASEBKGND. You
may want to intercept all of the WM_ERASEBKGND messages
and do all of your painting, including the background, in response to
WM_PAINT messages in the Paint method.
Use the ExitWindowsEx function.
Sending the WM_SETREDRAW message to your control can
either set or clear the flag used to determine if a control is redrawn.
CTL3D has no effect unless csFramed is set in ControlStyle. Try
something like this inside your constructor:
The trick to having a control border is that the border must be
created when the control's Window handle is created.
If you do not include csOpaque in ControlStyle then Invalidate calls
will cause the control's background to be erased. If you draw your
control's background in the Paint method then you should do this in
your constructor:
Another possible cause of flickering can be found by examining the
TWinControls WM_ERASEBKGND handling. Whenever this
message is received by a windowed control the VCL erases the
controls background to its default color. If your controls is a
TWinControl decendant, and draws itself in something other than
the default background color (e.g. a bitmap), then this can cause
some flickering as the background is cleared and then you repaint it
again.
By using the following method in your component you inform
Windows that you will handle _all_ of the necessary drawing
yourself. You just have to ensure that you do paint _all_ of your
controls surface, because anything you don't repaint will contain
random garbage pixels. This will also speed your control (very
slightly 8-) by saving one rectangle fill operation.
To receive keystroke messages for arrow keys you have to handle
the WM_GETDLGCODE messages. In the messages handler
return DLGC_WANTARROWS. If you do not handle
WM_GETDLGCODE Windows intercepts the arrow keys and uses
them to move among controls.
From Max Nilson
To receive keystroke messages for arrow keys you have to handle
the CM_WANTSPECIALKEY messages. The
CM_WANTSPECIALKEY allows a much more 'fine grained' way
of deciding if you require a special key than responding to the
WM_GETDLGCODE messages. The control message
CM_WANTSPECIALKEY is sent to a control whenever a special
key is being handled.
These special keys are VK_TAB, VK_LEFT, VK_RIGHT,
VK_UP, VK_DOWN, VK_RETURN, VK_EXECUTE,
VK_ESCAPE and VK_CANCEL. If the message result is non zero
then the key is passed onto the KeyPress method for you to handle,
otherwise it is passed onto the controls parent form for Delphi's
standard navigation handling. The Delphi standard navigation is
where Delphi handles the Tab, Shift-Tab and arrow key motion
between controls, without needing to use the default Windows
dialog manager at all!
A simple example:
Yes. Application.ProcessMessages
2.1. How can I locate problems that result when
my component is used in the IDE?
2.2. How do I view assembly langugage the
Delphi Generates?
2.3. I can create my control at run time but it
crashes at design time. What is wrong?
2.4. How can I create a component that cannot
be dropped on a form?
2.5. What is an easy way to backtrack through
source code?
2.6. How can I add to the popup menu that is
displayed when the right mouse button is clicked on my
component?
2.7. How come I get I/O 103 in design
mode?
2.8. Why are my property values not getting
saved when I use a component editor?
3.1. How can I add a scroll bar component to
my component and have it work at in design mode?
3.2. How do I create a Windows '95-style scroll
bar?
4.1. Where is the documentation for the
TDataLink class?
4.2. How can I tell the number of the record I
am on in a data set?
5.1. How can I step through the VCL source
while debugging?
5.2. My component references other
components. How can I tell if a component my component
references had been deleted?
5.3. What are component messages?
5.4. My control has focus but it is not getting
keystroke messages. What's happening?
6.1. Are there any books one how to write
Delphi components?
6.2. Are there any good web sites with
information on how to write components?
7.1. How can I save a complex object containing
child objects to the .DFM file.
7.2. How can I tell if my constructor is being
called for an object being loaded from a stream?
7.3. How can I tell if my component's properties
are being saved correctly to the form file?
8.1. Is there YACC and LEX for
Delphi?
8.2. How can I write code to display JPEG
files?
9.1. How do I create a dynamic array of
objects?
9.2. Where is Wincrt in Delphi 2.0?
9.3. What should the base class be for my
component?
10.1. Is there a Delphi equivalent to C++'s I/O
stream classes?
10.2. How can I get the text equivalent for an
enumerated type?
11.1. How can I find out when my component
has had its window handle created?
11.2. How can I tell when all the components
on my form have been loaded?
11.3. Where is the best place to draw my
control?
11.4. How do I change the Window Style for my control?
12.1. I am trying to scroll the contents of my
control but I get an ugly flicker effect. How can I eliminate
this?
12.2. How can I restart windows?
12.3. How can I batch updates to when
changing the appearance of my control?
13.1. How come my control does not have a
3D border even when I have CTL3D set to True?
13.2 How do I implement a BorderStyle
property?
14.1. How do I stop my control from flickering
when it gets repainted?
15.1. How come my control does not get
keystroke messages for the arrow keys?
15.2. Is there an equivalent of Visual Basic's
"DoEvents" statement?
Section 1 - Introductions
1.1. What is the purpose of this document?
If you have an suggestions for any of these they are also welcome.
Keep in mind that I cannot include an item if I cannot verify it.
Section 2 - IDE Problems
2.1. How can I locate problems that result when my component is
used in the IDE?
If you get a GPF you can use view the stack and get some idea
where the problem is occuring.
2.2. How do I view assembly langugage the Delphi Generates?
2.3. I can create my control at run time but it crashes at design time.
What is wrong?
The following declarations will cause serious problems if you
attempt to use TMyComponent in design mode.
Constructor Create (AOwner : TComponent) ; Override ;
Destructor Destroy ; Override ;
Type
TComplex = Record
RealPart : Double ;
ComplexPart : Double ;
End ;
class TMyComponent = Class (TComponent)
Private
F1 : TComplex ;
Published
Property P1 : TComplex Read F1 Write F1 ;
End ;
2.4. How can I create a component that cannot be dropped on a
form?
2.5. What is an easy way to backtrack through source code?
2.6. How can I add to the popup menu that is displayed when the
right mouse button is clicked on my component?
This topic is described well in the book "Developing Delphi
Components".
2.7. How come I get I/O 103 in design mode?
2.8. Why are my property values not getting saved when I use a
component editor?
Designer.Modified
to let Delphi know that you have changed a property value.
Section 3 - Using other components within a component
3.1. How can I add a scroll bar component to my component and
have it work at in design mode?
TMyScrollBar = class (TScrollBar)
Procedure CMDesignHitTest (var Message : TCMDesignHitTest) ;
Message CM_DESIGNHITTEST ;
End ;
Procedure TMyScrollBar.CMDesignHitTest (var Message : TCMDesignHitTest) ;
Begin
Message.Result := 1 ;
End ;
When your component creates one of these scroll bars it needs to
use
TMyScrollBar.Create (Nil)
rather than
TMyScrollBar.Create (Self)
otherwise the scroll bar will display sizing handles when it is click.
This means you need to be sure to explicitly free the scroll bar in
your component's destructor.
3.2. How do I create a Windows '95-style scroll bar?
Procedure SetPageSize (ScrollBar : TScrollBar ; PageSize : Integer) ;
Var
ScrollInfo : TScrollInfo ;
Begin
ScrollInfo.cbSize := Sizeof (ScrollInfo) ;
ScrollInfo.fMask := SIF_PAGE ;
ScrollInfo.nPage := PageSize ;
SetScrollInfo (ScrollBar.Handle, SB_CTL, ScrollInfo, True) ;
End ;
To retrieve the page size use:
Function GetpageSize (ScrollBar : TScrollBar) ;
Var
ScrollInfo : TScrollInfo ;
Begin
If HandleAllocated Then
Begin
ScrollInfo.cbSize := Sizeof (ScrollInfo) ;
ScrollInfo.fMask := SIF_PAGE ;
GetScrollInfo (ScrollBar.Handle, SB_CTL, ScrollInfo) ;
Result := ScrollInfo.nPage ;
End ;
End ;
Section 4 - Bound Controls
4.1. Where is the documentation for the TDataLink class?
Methods:
Virtual Methods:
True => Success
False => Failure
The non-overridden action for this procedure is to call
RecordChanged (Nil)
If the Field parameter is non-nil then the change occured to the
specified field.
4.2. How can I tell the number of the record I am on in a data set?
You can attach an object or your class to a data source and use it to
keep track of the current record.
Section 5 - VCL
5.1. How can I step through the VCL source while debugging?
5.2. My component references other components. How can I tell if
a component my component references has been deleted?
type
TMyComponent = class(TComponent)
private
FDataSource: TDataSource;
procedure SetDataSource(Value: TDataSource);
protected
procedure Notification(AComponent: TComponent; Operation: TOperation);
override;
published
property DataSource: TDataSource read FDataSource write SetDataSource;
end;
procedure TMyComponent.SetDataSource(Value: TDataSource);
begin
if Value <> FDataSource then
begin
FDataSource := Value;
if FDataSource <> nil then
// Tell the component that we are interested in its fate
FDataSource.FreeNotification(Self)
end
end;
procedure TMyComponent.Notification (AComponent : TComponent;
Operation : TOperation);
begin
inherited Notification(AComponent, Operation);
// If this is the component we are referencing then remove our reference
if (Operation = opRemove) and (AComponent = FDataSource) then
FDataSource := nil
end;
5.3. What are component messages?
Return Value: Appears to be either zero or one.
This message is sent in design mode when the mouse is over the
control. It appears that the purpose of the message is to determine
if the control wants to process mouse messages while in design
mode. If the return value is one then Delphi lets the control process
mouse messages. If it is zero then the Delphi handles the messages.
If a control sets this message to one all the time then the popup
menu will never appear. If the control does not handle this message
or returns zero all the time then the control cannot response to
mouse messages in design mode.
Return Value: None
A control sends itself this message when it receives a
WM_WININICHANGE message.
5.4. My control has focus but it is not gettig keystroke messages.
What's happening?
Section 6 - Other Sources of Information
6.1. Are there any books on how to write Delphi components?
"Developing Delphi Components" by Ray Konopka,
Coriolis Group Books
While this book not specifically on how to write components it has
a lot of information that is invaluable to the component writer:
"Programming Delphi Custom Components" by Fred
Bulback, M&T Books
6.2. Are there any good web sites with information on how to write
components?
http://sunsite.icm.edu.pl/~robert/delphi
This page has links to many other Delphi sites.
http://www.coast.net/~jkeller
You can also find Delphi sites by using:
http://www.pobox.com/~bstowers/delphi
Yahoo: www.yahoo.com
Unfortunately Web sites have a nasty habit of disappearing or
moving. Please notify the maintainer these addresses are out of
date.
Alta Vista: www.altavista.digital.com
Section 7 - Persistant Objects
7.1. How can I save a complex object containing child objects to the
.DFM file.
Procedure TMyComponent.GetChildren (Proc : TGetChildProc) ;
Begin
Proc (Child1) ;
Proc (Child2) ;
...
Proc (Childn) ;
End ;
Getting the objects out of the stream is a little trickier. Your parent
object may need to overload the GetChildOwner and
GetChildParent functions. Otherwise Delphi will try to make the
child owned by the form. (In Delphi V1 you need to override the
Readstate method.)
7.2. How can I tell if my contructor is being called for an object
being loaded from a stream?
Constructor TMyClass.Create (AOwner : TComponent) ;
Begin
If csLoading in AOwner.ComponentState Then
Begin
End
Else
Begin
End ;
End ;
7.3. How can I tell if my component's properties are being saved
correctly to the form file?
Stefan Hoffmeister points out that if you copy a control the the
clipboard then you can paste the text representation of the control
to an editor such Notepad. You can edit the control in the editor
and paste it back into to your Delphi application.
Section 8 - Tools for Delphi
8.1. Is there YACC and LEX for Delphi?
8.2. How can I write code to display JPEG files?
ftp://druckfix.physik.tu-chemnitz.de/pub/nv/
http://www.tu-chemnitz.de/~nomssi/pub/pasjpeg.zip
Section 9 - Basic Programming Techniques
9.1. How do I create a dynamic array of objects?
TListOfMyObject = class (TList)
Private
Function GetItems (Index : Ordinal) : TmyObject ;
public
Property Items [Index : Ordinal] : TmyObject Read GetItems ;
Procedure Add (AObject : TmyObject) ;
End ;
Function TListOfMyObject.GetItems (Index : Ordinal) : TmyObject ;
Begin
If Index >= Count Then
raise Exception.CreateFmt ('Index (%d) outside range 1..%d',
[Index, Count -1 ]) ;
Result := Inherited Items [Index] ;
End ;
Procedure TListOfMyObject.Add (AObject : TmyObject) ;
Begin
Inherited Add (AObject) ;
End ;
9.2. Where is the Wincrt unit in Delphi 2.0?
9.3. What should the base class be for my component?
Section 10 - Advanced Programming Techniques
10.1. Is there a Delphi equivalent to C++'s I/O Stream classes?
10.2. How can I get the text equivalent for an enumerated type?
Type
TMyType = (Value1, Value2) ;
...
Var
TypeValue : TmyType ;
...
WriteLn (GetEnumName (TypeInfo (TMyType), Ord (TypeValue)) ;
The module TypInfo has many other functions for obtaining
information about types.
Section 11 - Component Virtual Methods
11.1. How can I find out when my component has had its window
handle created?
Procedure TMyClass.CreateWnd ;
Begin
Inherited CreateWnd ; { Don't forget or you'll never get a window handle. }
{ Your processing goes here. }
End ;
11.2. How can I tell when all the components on my form have been
loaded?
Procedure TMyClass.Loaded ;
Begin
Inherited Loaded ; { Clears the csLoading in ComponentState }
{ Your processing goes here. }
End ;
11.3. Where is the best place to draw my control?
Procedure TMyClass.Paint ;
Begin
{ You only need to call inherited Paint if your class inherits
directly from TCustomControl or TGraphicControl. You may need
to call this if your control inherits from an existing control. }
Inherited Paint ;
{ Your processing goes here. }
End ;
11.4. How do I change the Window Style for my control?
procedure TMyControl.CreateParams(var Params: TCreateParams) ;
Begin
Inherited CreateParams (Params) ;
IF IWantAScrollBar Then
Params.Style := Params.Style OR WS_VSCROLL
Else
Params.Style := Params.Style AND NOT WS_VSCROLL ;
End ;
Section 12 - Windows API Functions
12.1. I am trying to scroll the contents of my control but I get an
ugly flicker effect. How can I eliminate this?
12.2. How can I restart windows?
12.3. How can I batch updates to when changing the appearance of
my control?
Section 13 - Control Borders
13.1. How come my control does not have a 3D border even when I
have CTL3D set to True?
ControlStyle := ControlStyle + [csFramed] ;
13.2. How do I implement a BorderStyle property?
FBorderStyle : TBorderStyle ;
Procedure SetBorderStyle (Style : TBorderStyle) ;
Property BorderStyle : TBorderStyle Read FBorderStyle write SetBorderStyle ;
procedure CreateParams(var Params: TCreateParams) ; Override ;
procedure TMyControl.CreateParams(var Params: TCreateParams) ;
Begin
Inherited CreateParams (Params) ;
If FBorderStyle = bsSingle Then
Params.Style := Params.Style Or WS_BORDER
Else
Params.Style := Params.Style And Not WS_BORDER ;
End ;
Procedure TMyControl.SetBorderStyle (Style : TBorderStyle) ;
Begin
IF Style <> FBorderStyle Then
Begin
FBorderStyle := Style ;
{ Create new window handle for the control. }
RecreateWnd ;
End ;
End ;
Section 14 - Component Styles
14.1. How do I stop my control from flickering when it gets
repainted?
ControlStyle := ControlStyle + [csOpaque] ;
From Max Nilson
type
TMyComponent = class(TWinControl)
...
protected
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd);
message WM_ERASEBKGND;
...
end;
procedure TBMyComponent.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
Message.Result := 0
end;
Section 15 - Windows Messages
15.1. How come my control does not get keystroke messages for
the arrow keys?
type
TMyComponent = class(TWinControl)
...
protected
procedure CMWantSpecialKey(var Message: TCMWantSpecialKey);
message CM_WANTSPECIALKEY;
...
end;
procedure TMyComponent.CMWantSpecialKey (var Message: TCMWantSpecialKey);
begin
inherited;
// We want to handle the left arrow ourselves
if Message.CharCode = VK_LEFT then
Message.Result := 1;
end;
The 'fine grained' I refered to above comes from this ability to
examine a specific keypress and decide if you need to handle the key
your self, or allow it to continue on into Delphi's handler. If you had
a single control with three images, for example, you could allow the
left and right arrows to more back and forth between them, and also
let the user to move onto the next tab stop from the last image on
your control, by allowing Delphi to handle the keypress instead of
your internal control handling.
15.2. Is there an equivalent of Visual Basic's
"DoEvents" statement?
Copyright 1996 - John Miano
Contributors
Glen Boyd
Stefan Hoffmeister
Ray Konopka
Ray Lischner
Max Nilson