Contributor: JOHN M. MIANO


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.


------------------------------------------------------------------------
Copyright 1996 - John Miano

Contributers

Glen Boyd