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.