Delphi OldCreateOrder – er, what?

FireWind

Свой
Регистрация
2 Дек 2005
Сообщения
1,957
Реакции
1,199
Credits
4,009
OldCreateOrder – er, what?
February 18, 2021 by Uwe Raabe

The full title should be: What is the purpose of the OldCreateOrder property in a form and how does it affect my todays coding?, but it turned out to be too long for a catchy headline.

Probably you may already have wondered about that OldCreateOrder. It appears not only in forms, but also in some other classes, f.i. in a TDataModule. It was introduced in either Delphi 4 or Delphi 5 – (I can only say it was absent in Delphi 3, but present in Delphi 5).

Basically OldCreateOrder controls when the OnCreate and OnDestroy events are called. In case of a TCustomForm descendant, with OldCreateOrder = True the OnCreate is called inside TCustomForm.Create after the DFM is loaded, while with False the OnCreate is called after all inherited constructors are finished (i.e. in TCustomForm.AfterConstruction).

This has consequences for the way you code. Let me show you the difference with a simple example of a VCL form with a memo and a button. The memo contains some text entered at design time, which is saved in a TStringList instance during FormCreate. The TStringList is create in the constructor and destroyed in the destructor.
Код:
type
  TMainForm = class(TForm)
    edtText: TMemo;
    btnReset: TButton;
    procedure FormDestroy(Sender: TObject);
    procedure btnResetClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FDesignText: TStrings;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;
A click on the button restores the text to the saved content.
Код:
constructor TMainForm.Create(AOwner: TComponent);
begin
  inherited;
  FDesignText := TStringList.Create;
end;
 
procedure TMainForm.FormCreate(Sender: TObject);
begin
  FDesignText.Assign(edtText.Lines);
end;
 
destructor TMainForm.Destroy;
begin
  FDesignText.Free;
  FDesignText := nil;
  inherited Destroy;
end;
 
procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FDesignText.Clear;
end;
 
procedure TMainForm.btnResetClick(Sender: TObject);
begin
  edtText.Lines := FDesignText;
end;
When you run this project with OldCreateOrder = False (the default), everything is working as expected (unless you are doing this with a really, really old Delphi version).

Let’s switch OldCreateOrder to False and try again: It will crash with a nil pointer reference in FormCreate.

If we recall what I have written above

with OldCreateOrder = True the OnCreate is called inside TCustomForm.Create

this was to be expected.
Код:
constructor TMainForm.Create(AOwner: TComponent);
begin
  inherited; // <== here we call FormCreate
  FDesignText := TStringList.Create;
end;
The typical workaround in these old days, when OldCreateOrder was the only and standard behavior, was to move the TStringList creation before the inherited call. Luckily this approach works even when OldCreateOrder is False.

Of course, we could as well move the creation on the FormCreate event, but personally I dislike this approach – be it because it just doesn’t look clean to me.

Looking at the other side – Destroy and FormDestroy – things are quite similar, albeit the other way round. With OldCreateOrder = True, the FormDestroy is called inside the inherited call of Destroy, while with OldCreateOrder = False it is called before any destructor is executed.

So, whenever you see a pattern in creating instances before calling a constructors inherited and freeing after that in a destructor, you now know where this habit originates.