You can use
Borland Delphi without the
IDE, using the
command line compiler (dcc32.exe), however the IDE has a good
editor and
form designer, so you’d be silly not to.
The IDE is not entirely dissimilar to Visual Basic’s IDE. It is written entirely in Delphi.
The IDE uses the published property mechanism extensively to inspect controls – in fact, this is why published properties were introduced.
Delphi has no magic in it – everything that the IDE does, is done in Delphi using mechanisms that are available to you as a Delphi programmer.
The programming style of a novice Delphi programmer is usually neither quite procedural nor is it object-oriented. It consists of mostly designing forms and attaching event handlers to them by double-clicking the buttons, and using standard objects in long procedural blocks of code but not defining new object types. I call this programming style, reminiscent of Visual Basic coding, Object-based. Good programmers will gradually transition to a fully-blown object-oriented style.
It is worth taking note of what actually happens when you do a file|new form in Delphi. You create a .pas file, which contains a class that subclasses TForm. Every time that you drop a control onto the form, the class definition is automatically updated with new member objects in the default public section. I.e. after dropping a button on the form the unit reads:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('You clicked the button');
end;
end.
When you rename or delete a control the unit is automatically updated. You can also edit the text and the form will be updated. Borland calls this “Two-way tools” and yes it is patented.
But there is a lot of information that is not here: control positions, captions etc ... and the fact that the Button1Click procedure is to be called when the button is clicked. These are stored in the .dfm file, which is linked in via the {$R *.DFM} compiler directive
The corresponding .dfm file reads:
object Form1: TForm1
Left = 192
Top = 107
Width = 783
Height = 540
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style =
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 212
Top = 12
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
end
It is just a listing of objects and property values. And yes, you can edit these too if you know what you are doing.
One of the principles of Delphi is that anything that is done at design time
can be done at runtime. To create a button at runtime and assign a handler for
it's clicked event, use the following:
procedure TForm1.ClickHandler(Sender: TObject);
begin
ShowMessage('Button clicked');
end;
procedure TForm1.FormCreate(Sender: TObject);
var
lc: TButton;
begin
lc := TButton.Create(self); // the form will free it when it frees
lc.Parent := self;
lc.Left := 10; // coords relative to the top left of the parent, ie the
form
lc.Top := 10;
lc.Caption := 'Click me!';
lc.OnClick := ClickHandler; // method pointer
lc.Visible := True;
end;
This also demonstrates that Delphi event handling makes heavy use of method pointers to delegate] events from one object (the button) to another (the form). This is another good delphi idea that showns up again in C#. Without these, you would have to subclass TButton for each button on the form in order to specify custom behaviour when the button is clicked.
These event handlers, like everything in Delphi, strongly typed and are type-checked at compile-time. For e.g. if
ClickHandler did not have the sender parameter there would be a compilation error because the types would not be compatible.
The type used
here is actually
type TNotifyEvent = procedure(Sender: TObject)
of object;
This also allows the same handler to be attached to many different events on
different widgets. The hander can always alter it's behavior depending on the sender.