Forms Delphi

Title: How to use class references to dynamically create forms at runtime
Question: How to use class references to dynamically create forms at runtime
Answer:
Once upon a time I collect error messages in a StringList, and displayed them in a Listbox. But my users requested a new feature. They want to double-click the error message and jump to the associated form they even want to set the focus in the edit field in which the error occurs. But which message belongs to which form? And how can I store object references to objects which arent created yet? Answer: you can not!
But here is my solution. I hope it is useful.
First I create a TErrorItem class which contains all desired information. Objects of this class will be created whenever an error occurs and will be stored in a TList object. Here is the Code of my class.
TErrorItem = class
private
FErrorString : String;
FRefControl : String;
FPrimaryKey : TObject;
FRefForm : TFormClass;
public
property ErrorString : String read FErrorString write FErrorString;
property RefControl : String read FRefControl write FRefControl;
property PrimaryKey : TObject read FPrimaryKey write FPrimaryKey;
property RefForm : TFormClass read FRefForm write FRefForm;
end;
The ErrorString contains the errormessage for the user.
RefControl is the name of a TWincontrol which will be activated.
PrimaryKey is not needed for our purposes, but you can store here a database primary key to link your error objects directly to database entries.
RefForm is a class reference! This is where the magic lives :-)
And TformClass is:
TFormClass = class of TForm;
the definition of a class reference.
If an error occurs I create an ErrorItem object and fill its fields with all the needed information. Notice that RefForm needs a class name e.g. TForm3!
In my sample application the errorList is filled with two errors. Each linked to another form.
Now take a look what will happen, if we double-click a Listbox Item:
procedure TForm1.ListBox1DblClick(Sender: TObject);
//you don't need this function - you can call the constructor directly
function createForm(errForm:TFormClass): TForm;
begin
result := errForm.create(Application);
end;
var
i : integer;
aForm : TForm;
aError : TErrorItem;
classRef : TFormClass;
myI : IActiveObj;
begin
i := ListBox1.ItemIndex;
aError := ErrorList[i];
classRef:= aError.RefForm;
//using the class reference to call the constructor!
aForm := createForm(classRef);
//set the desired control as active Control
aForm.ActiveControl := (aForm.findChildControl(aError.RefControl) as TWinControl);

if aForm.GetInterface(IID_IActiveObj,myI) then
myI.setActiveObj(self);
//show the form - regardless of type!
aForm.Showmodal;
//be happy! And don't forget the try-finally block ;-)
end;
First mention the function createForm. It is just a wrapper around the constructor.
We take the clicked ErrorItem and read its class reference field.
With this class reference we call the createForm function and we receive a new Form and store it in a object reference called aForm. Next step we search (and hopefully find) the ChildControl which should be activated when the form is being shown. If the control is nested in a container e.g. a panel it will not be found!
The next thing we do has nothing to do with the topic class references, but is a good example using interfaces.
Because we do not know at runtime which Form we actually have created it is useful to let them implement one or more interfaces, so we can call e.g. some init procedures.
Here is my Sample Interface:
IActiveObj = interface
['{115FC41B-BDAC-4B9B-975A-5D3E7217F7D0}']
procedure setActiveObj(myObj : TObject);
end;
It just declares on method, but this is an example and we dont want to become confused :-)
I also declare a constant in my datamodul like this:
const
IID_IActiveObj : TGUID = '{115FC41B-BDAC-4B9B-975A-5D3E7217F7D0}';
(To create a GUID press CTRL+SHIFT+G)
Again:

if aForm.GetInterface(IID_IActiveObj,myI) then
myI.setActiveObj(self);
If aForm implements IDD_IactiveObj GetInterface will return true and set myI to the implementation of this interface by aForm. myI has the type of the interface, so I can call all methods this interface declares.
Dont forget: TForm2 and TForm3 must implement the interface.
Thats all. And always remember : Delphi rocks!