Published by Joao Morais on 31 Aug 2008 at 11:09 am
Forms management tip
From my point of view, one thing that is really missing for Delphi/Lazarus is a better application’s form management. Two problems: an independent public variable is used to control an instance, and the ‘please place everything within the form’ approach. The first one requires the programmer to either create the form at the start of the application and not destroy it anymore, or it requires the programmer to create the forms and keep track of object destruction insuring that no variable points to a destroyed object. The second, among other problems, requires that the first one works perfectly so that the communication between forms can work.
When using the second approach there is not much work to be done. You either have to live with this limitation or write a new framework to go around it. Now, in order to maintain an up-to-date public variable, the programmer can benefit from the small tricks I will be describing here.
My suggestion to maintain an up-to-date public variable is to give it to a class method. This method takes care to create the form when this is necessary, and to update the variable when the form is destroyed. For this, implement the following method in the forms base class, in other words, the one from which all the forms will inherit:
TBaseForm = class(TForm)
public
class procedure Execute(var AForm; AModal: Boolean = False);
end;
And implement the method like this:
class procedure TBaseForm.Execute(var AForm; AModal: Boolean);
var
VIndex: Integer;
begin
if not Assigned(_Forms) then
begin
_Forms := TStringList.Create;
_Forms.Sorted := True;
end;
if not _Forms.Find(ClassName, VIndex) then
VIndex := _Forms.Add(ClassName);
if TForm(AForm) = nil then
Application.CreateForm(Self, AForm);
_Forms.Objects[VIndex] := TObject(@AForm);
if AModal then
TForm(AForm).ShowModal
else
TForm(AForm).Show;
end;
what is still needed is to make the public variable references nil the moment the form is destroyed. You can choose from two events, either Destroy or Close. I will implement the Destroy, and leave the Close implementation for the programmer, either if he is going to assign caFree to the Action param or destroy the form manually:
procedure TBaseForm.FormDestroy(Sender: TObject);
type
PForm = ^TForm;
var
VIndex: Integer;
begin
if Assigned(_Forms) and _Forms.Find(ClassName, VIndex) and
(_Forms.Objects[VIndex] <> nil) then
begin
PForm(_Forms.Objects[VIndex])^ := nil;
_Forms.Objects[VIndex] := nil;
end;
end;
Finally, the StringList needs to be declared, and destroyed when the application is closed:
implementation var _Forms: TStringList = nil; ... initialization finalization _Forms.Free; end.
Done. The only thing needed is to create new forms that descend from this one and call TYourForm.Execute(YourForm). When the form is destroyed, the public variable will be assigned to nil.