Examples Delphi

I notices that the Delphi FAQ has not been updated in over a year. Since much
of the information needed to write Delphi components is undocumented I thought
that maybe there should be a component writing FAQ.
I have been going through some of my source code to get some of the tricks
that I have had to use in writing components. I am going through my mail to
see what tricks people have sent me and I will attempt to give credit to those
who have mailed me information.
Right now this document is short but hopefully it will grow over time. Some
of the information enclose has never been published. If you have
something to contribute or suggestions then mail them to me.
So hear goes......
The Unofficial Delphi Component Writing FAQ
Maintainer: John M. Miano miano@worldnet.att.net
Version: 1
Last Updated: 30-Aug-96
------------------------------------------------------------------------
Table of Contents
Section 1 - Introduction
1.1. What is the purpose of this document?
Section 2 - IDE
2.1. How can I locate problems the 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 make my control work only in design mode?
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?
3.2. How do I create a Windows '95-style scroll bar?
Section 4 - Bound Controls
4.1. Where is the documentation for the TDataLink class?
Section 5 - VCL
5.1. How can I step through the VCL source while debugging
Section 6 - Other Sources of Information
6.1. Are there any books one how to write Delphi components?
Section 7 - Persistant Objects
7.1. How can I save a complex object containing child objects to the .DFM
file.
------------------------------------------------------------------------
Section 1 - Introductions
1.1. What is the purpose of this document?
The purpose of this document is to answer common or undocumented questions
related to writing Delphi components. This information is provided as is.
There are no guarentee as to its correctness. If you find error or ommissions
sent them to the author.
This document is rather short at the moment but hopefully it will grow over
time.
------------------------------------------------------------------------
Section 2 - IDE Problems
2.1. How can I locate problems the result when my component is used in the
IDE?
The only solution to locating problems I have found it to:
1. In Delphi go to Tools/Options then go to the "Library" page.
Check the "Compile With Debug Info" box.
2. Rebuild the library.
3. Run Delphi from within Turbo Debugger.
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?
From Glen Boyd
Borland/Delphi/2.0/Debugging add a string value called EnableCPU and set
its string value to 1. This add the CPU window to the view menu. The CPU
window is
active at run time for stepping through and stuff like that.
2.3. I can create my control at run time but it crashes at design time. What
is wrong?
1. You component must descent from TComponent
2. Your constructory and destructor declarations must look like:
Constructor Create (AOwner : TComponent) ; Override ;
Destructor Destroy ; Override ;
3 You will get an Access Violation/GPF if you component has any published
properties that do not have a property editor defined. These include array
properties and properties of types you create.
How do I make a component work only in design mode?
The trick is to use the Register function. This is only called in design
mode. You can
use it to set a flag that your constructors can check.
2.4. How can I make my control work only in design mode?
The Register procedure is only called in design mode. You can define a flag
in your module and have your register procedure set that flag. If that flag
is clear in your constructor then you are not in design mode.
------------------------------------------------------------------------
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?
You need to define your own scroll bar class that intercepts the
CM_DESIGNHITTEST message.
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 then
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?
You need to set the page size for the scroll bar. The following code
sequence
illustrates this:
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 ;
------------------------------------------------------------------------
Section 4 - Bound Controls
4.1. Where is the documentation for the TDataLink class?
As far as I can tell the only documentation for TDataLink that exists in the
entire universe is what follows
Properties:
===========
Property: Active : Boolean (Read Only)
----------------------------------------
Returns true when the data link is connected to an active datasource.
The ActiveChanged method is called to give notification when the state
changes.
Property: ActiveRecord: (Read/Write)
--------------------------------------
This sets or returns the current record within the TDatalink's buffer window.
Valid values are
0..BufferCount - 1. There appear to be no range checks so assigning values
outside this range produces unpredictable results.
Property: BufferCount: (Read/Write)
-------------------------------------
The TDataLink maintains a window of records into the dataset This property is
the size of this window and determines the maximum number of row that can be
view simultaneously. For most controls you would use a BufferCount of one.
For controls such as a data grid this value is the number of visible rows.
Property: DataSet: TDataSet (Read)
------------------------------------
The dataset the TDataLink is attached to. This is a shortcut to
DataSource.DataSet.
Property: DataSource: TDataSource (Read/Write)
------------------------------------------------
Sets or returns data source control the TDataLink is attached to.
Property: DataSourceFixed: Boolean (Read/WRite)
-------------------------------------------------
This property is used to prevent the data source for the TDataLink from being
changed. If this property is set to Trye then assigning a value to the
DataSource property will result in an exception.
Property: Editing: Boolean (Read Only)
----------------------------------------
Returns true if the datalink is in edit mode.
Property: ReadOnly: Boolean (read/Write)
------------------------------------------
This property determines if the TDataLink is read only. It does not appear to
affect the attached datalink
or dataset. If this property is set to True the datalink will not go into
edit mode.
Property: RecordCount: Integer (Read)
---------------------------------------
The property returns the approximate number of records in the attached
dataset.
Methods:
========
function Edit: Boolean;
-----------------------
Puts the TDatalink's attached dataset into edit mode.
Return Value:
True => Success
False => Failure
procedure UpdateRecord;
-----------------------
It appears that this is a function that is intended to be called by other
parts of the data base interface and should not be called directly. All it
does is set a flag and call UpdateData (described below).
Virtual Methods
===============
The mechanism for having the TDataLink object communicate with a component is
to override these procedures.
procedure ActiveChanged
------------------------
This procedure is called whenever the datasource the TDataLink is attached to
becomes active or
inactive. Use the Active property to determine whether or not the link is
active.
procedure CheckBrowseMode
-------------------------
This method appears to get called before any changes take place to the
database.
procedure DataSetChanged;
-------------------------
This procedure gets called when the following events occur:
o Moving to the start of the dataset
o Moving to the end of the dataset
o Inserting or Appending to the dataset
o Deleting a record from the dataset
o Canceling the editing of a record
o Updating a record
The non-overrident action for this procedure is to call
RecordChanged (Nil)
procedure DataSetScrolled(Distance: Integer)
--------------------------------------------
This procedure is called whenever the current record in the dataset changes.
The Distance parameter tells how far the buffer window into the dataset was
scrolled (This seems to always be in the range -1, 0, 1).
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.
procedure FocusControl(Field: TFieldRef)
----------------------------------------
This appears to get called as a result of Field.FocusControl.
procedure EditingChanged
-------------------------
This procedure is called when the editing state of the TDataLink changes. Use
the Editing property to determine if the TDataLink is in edit mode or not.
procedure LayoutChanged
-----------------------
This procedure is called when the layout of the attached dataset changes (e.g.
column added) .
procedure RecordChanged(Field: TField)
--------------------------------------
This procedure gets called when:
o The current record is edited
o The record's text has changed
If the Field parameter is non-nil then the change occured to the specified
field.
procedure UpdateData
--------------------
This procedure is called immediately before a record is updated in the
database. You can call the Abort procedure to prevent the record from being
updated.
------------------------------------------------------------------------
Section 5 - VCL
5.1. How can I step through the VCL source while debugging
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.
------------------------------------------------------------------------
Section 6 - Other Sources of Information
6.1. Are there any books one how to write Delphi components?
I have seen a couple out there but the only one I can recommend is
"Developing Delphi Components" by Ray Konopka
------------------------------------------------------------------------
Section 7 - Persistant Objects
7.1. How can I save a complex object containing child objects to the .DFM
file.
I have tried all sorts of schemes using DefineProperties and WriteComponents
and they all failed to work. As far as I can 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:
1. Make all of the classes whose objects you want to save descent from
TComponent.
2. Make all of the values you want to save published.
3. Within your Register procedure add a call to RegisterComponents containing
all of the classes you wish to store.
4. Each class that owns child classes needs to overload the procedure
GetChildren. Within this procedure is needs to call the procedure passed as
an argument for each child to be stored.
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.