According to Vladโs request , this expanded to my initial answer , explaining how to register all forms belonging to Application without any changes to the design of each form. That is, forms created using TMyForm.Create(Application); , and also implies Application.CreateForm(TMyForm, MyForm); .
The original answer did not indicate any special registration methods for FreeNotification , since the parameters depend on how the forms are created. Since there were no restrictions on how forms are created in the answer to the question, the original answer is more suitable in the general case.
If we could make sure that Application belongs to a custom subclass of TApplication , the problem could be easily resolved by overriding TApplication.Notification; . This is not possible, therefore, in this special case, the fact is used that the ownership structure of the component notifies all components belonging to it when adding or removing another component. Thus, basically all we need is a component tracker, also owned by Application , and we can respond to its "sibling" notifications.
The following test case will demonstrate that new notifications work.
procedure TComponentTrackerTests.TestNewNotifications; var LComponentTracker: TComponentTracker; LInitialFormCount: Integer; LForm: TObject; begin LComponentTracker := TComponentTracker.Create(Application); try LComponentTracker.OnComponentNotification := CountOwnedForms; LInitialFormCount := FOwnedFormCount; LForm := TForm.Create(Application); CheckEquals(LInitialFormCount + 1, FOwnedFormCount, 'Form added'); LForm.Free; CheckEquals(LInitialFormCount, FOwnedFormCount, 'Form removed'); finally LComponentTracker.Free; end; end; procedure TComponentTrackerTests.CountOwnedForms(AComponent: TComponent; AOperation: TOperation); begin if (AComponent is TCustomForm) then begin case AOperation of opInsert: Inc(FOwnedFormCount); opRemove: Dec(FOwnedFormCount); end; end; end;
TComponentTracker is implemented as follows:
TComponentNotificationEvent = procedure (AComponent: TComponent; AOperation: TOperation) of object; TComponentTracker = class(TComponent) private FOnComponentNotification: TComponentNotificationEvent; procedure SetOnComponentNotification(const Value: TComponentNotificationEvent); procedure DoComponentNotification(AComponent: TComponent; AOperation: TOperation); protected procedure Notification(AComponent: TComponent; AOperation: TOperation); override; public property OnComponentNotification: TComponentNotificationEvent read FOnComponentNotification write SetOnComponentNotification; end; procedure TComponentTracker.DoComponentNotification(AComponent: TComponent; AOperation: TOperation); begin if Assigned(FOnComponentNotification) then begin FOnComponentNotification(AComponent, AOperation); end; end; procedure TComponentTracker.Notification(AComponent: TComponent; AOperation: TOperation); begin inherited Notification(AComponent, AOperation); DoComponentNotification(AComponent, AOperation); end; procedure TComponentTracker.SetOnComponentNotification(const Value: TComponentNotificationEvent); var LComponent: TComponent; begin FOnComponentNotification := Value; if Assigned(Value) then begin { Report all currently owned components } for LComponent in Owner do begin DoComponentNotification(LComponent, opInsert); end; end; end;
A WARNING
You can implement everything that you have selected in the OnComponentNotification event OnComponentNotification . This would include registering that the form is โdestroyedโ. However, such a simplified approach would be erroneous because TComponent.InsertComponent allows TComponent.InsertComponent to change the ownership of a component without destroying it.
Therefore, in order to accurately report the destruction, you would have to combine this using FreeNotification , as in the first
.
This is fairly easy to do by setting LComponentTracker.OnComponentNotification := FDestructionLogger.RegisterFreeNotification; where RegisterFreeNotification is done as follows:
procedure TDestructionLogger.RegisterFreeNotification(AComponent: TComponent; AOperation: TOperation); begin if (AComponent is TCustomForm) then begin case AOperation of opInsert: AComponent.FreeNotification(Self); end; end; end;